Flutter 列表 + Riverpod 架构实战 —— 从 setState 到状态驱动列表的工程落地

Flutter 列表真正的威力,不在 ListView,而在:

👉 列表 = 状态的投影结果


一、为什么 Flutter 列表必须配状态管理?

新手写法:

Dart 复制代码
setState(() {
  list.addAll(newData);
});

问题:

  • 业务逻辑混在 UI
  • 无法复用
  • 无法测试
  • 分页、错误、加载状态混乱

企业级目标:

👉 UI 只关心"状态"

👉 所有列表行为 = 状态机

二、列表的标准状态建模(架构核心)

一个工程级列表,最少包含:

Dart 复制代码
class ListState<T> {
  final List<T> items;
  final bool isLoading;
  final bool isRefreshing;
  final bool hasMore;
  final String? error;

  const ListState({
    this.items = const [],
    this.isLoading = false,
    this.isRefreshing = false,
    this.hasMore = true,
    this.error,
  });
}

👉 这是 Flutter 列表的"领域模型(Domain State)"。

三、Riverpod Controller 层(替代 ViewModel)

Dart 复制代码
class UserListController extends StateNotifier<ListState<User>> {
  UserListController() : super(const ListState());

  Future<void> refresh() async {
    state = state.copyWith(isRefreshing: true);
    final data = await repo.fetch(page: 1);
    state = state.copyWith(
      items: data,
      isRefreshing: false,
      hasMore: true,
    );
  }

  Future<void> loadMore() async {
    if (!state.hasMore || state.isLoading) return;

    state = state.copyWith(isLoading: true);
    final data = await repo.fetchNext();
    state = state.copyWith(
      items: [...state.items, ...data],
      isLoading: false,
      hasMore: data.isNotEmpty,
    );
  }
}
Dart 复制代码
final userListProvider =
  StateNotifierProvider<UserListController, ListState<User>>(
    (ref) => UserListController(),
  );

👉 这里就是 MVVM / MVI 的 ViewModel / Reducer 层。

四、UI 层(纯渲染,不写业务)

Dart 复制代码
class UserListPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(userListProvider);
    final controller = ref.read(userListProvider.notifier);

    return RefreshIndicator(
      onRefresh: controller.refresh,
      child: ListView.builder(
        itemCount: state.items.length + 1,
        itemBuilder: (context, index) {
          if (index == state.items.length) {
            return _Footer(state);
          }
          return UserItem(state.items[index]);
        },
      ),
    );
  }
}
Dart 复制代码
class _Footer extends StatelessWidget {
  final ListState state;

  const _Footer(this.state);

  @override
  Widget build(BuildContext context) {
    if (state.isLoading) {
      return Center(child: CircularProgressIndicator());
    }
    if (!state.hasMore) {
      return Center(child: Text("没有更多了"));
    }
    return SizedBox.shrink();
  }
}

✔ UI = 纯函数

✔ 可测试

✔ 可替换

✔ 可维护

五、滚动触底触发分页

Dart 复制代码
final scrollController = ScrollController();

scrollController.addListener(() {
  if (scrollController.position.pixels >
      scrollController.position.maxScrollExtent - 200) {
    ref.read(userListProvider.notifier).loadMore();
  }
});

👉 滚动是输入

👉 Controller 决定状态变化

👉 UI 只是结果

六、这套架构解决了什么?

问题 解决
setState 混乱 状态集中管理
分页难写 行为模型化
异常难处理 error 状态
UI 不可测 Controller 可单测
列表难复用 Domain State 可复用

👉 这是"架构级列表"。

七、架构总结

Flutter 列表的终极形式:

Dart 复制代码
Input(滚动/点击)
   ↓
Controller(Riverpod)
   ↓
State(列表领域模型)
   ↓
ListView(UI渲染)
相关推荐
恋猫de小郭5 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
一只大侠的侠9 小时前
Flutter开源鸿蒙跨平台训练营 Day 10特惠推荐数据的获取与渲染
flutter·开源·harmonyos
renke336413 小时前
Flutter for OpenHarmony:色彩捕手——基于HSL色轮与感知色差的交互式色觉训练系统
flutter
子春一15 小时前
Flutter for OpenHarmony:构建一个 Flutter 四色猜谜游戏,深入解析密码逻辑、反馈算法与经典益智游戏重构
算法·flutter·游戏
铅笔侠_小龙虾15 小时前
Flutter 实战: 计算器
开发语言·javascript·flutter
微祎_17 小时前
Flutter for OpenHarmony:构建一个 Flutter 重力弹球游戏,2D 物理引擎、手势交互与关卡设计的工程实现
flutter·游戏·交互
一起养小猫17 小时前
Flutter for OpenHarmony 实战_魔方应用UI设计与交互优化
flutter·ui·交互·harmonyos
hudawei99617 小时前
flutter和Android动画的对比
android·flutter·动画
一只大侠的侠18 小时前
Flutter开源鸿蒙跨平台训练营 Day7Flutter+ArkTS双方案实现轮播图+搜索框+导航组件
flutter·开源·harmonyos
一只大侠的侠19 小时前
Flutter开源鸿蒙跨平台训练营 Day9分类数据的获取与渲染实现
flutter·开源·harmonyos