Skip to content

BaseListPage 架构

BaseListPage 是专门用于处理列表页面的基类,继承自 BasePage。它封装了分页加载、下拉刷新、触底加载更多等复杂的列表逻辑,让开发者只需关注 Item 的渲染。

核心功能

  • 自动分页: 内置分页逻辑,自动管理页码。
  • 下拉刷新: 集成 RefreshIndicator(可自定义)。
  • 触底加载: 滚动到底部自动触发加载下一页。
  • 状态管理: 自动处理首次加载、刷新中、加载更多中、无更多数据、加载失败等状态。

快速上手

1. 定义 ViewModel

继承 BaseListViewModel<T>,其中 T 是列表项的数据模型。

dart
class UserListViewModel extends BaseListViewModel<User> {
  @override
  Future<List<User>> loadData({required int pageNum}) async {
    // 调用 API 获取数据
    // 无需处理分页状态,基类会自动处理
    return await UserRepository.getUsers(page: pageNum);
  }
}

2. 定义 Page

继承 BaseListPage<T, VM>,实现 buildItem 方法。

dart
class UserListPage extends BaseListPage<User, UserListViewModel> {
  const UserListPage({super.key});

  @override
  State<UserListPage> createState() => _UserListPageState();
}

class _UserListPageState extends BaseListPageState<User, UserListViewModel, UserListPage> {
  @override
  UserListViewModel createViewModel() => UserListViewModel();

  @override
  String get title => '用户列表';

  // 开启下拉刷新
  @override
  bool get enableRefresh => true;

  // 渲染单个列表项
  @override
  Widget buildItem(BuildContext context, User item, int index) {
    return ListTile(
      leading: CircleAvatar(backgroundImage: NetworkImage(item.avatar)),
      title: Text(item.name),
      subtitle: Text(item.email),
    );
  }
}

进阶配置

自定义列表容器

默认使用 ListView.builder,你可以重写 buildListWidget 来使用 GridView 或其他容器。

dart
@override
Widget? buildListWidget(BuildContext context) {
  return GridView.builder(
    controller: _controller, // 必须使用基类的 controller
    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 2,
    ),
    itemCount: viewModel.items.length,
    itemBuilder: (context, index) {
      return buildItem(context, viewModel.items[index], index);
    },
  );
}
dart
@override
Widget? buildHeader(BuildContext context) {
  return Container(
    padding: const EdgeInsets.all(16),
    child: const Text('这是列表头部'),
  );
}

@override
Widget? buildFooter(BuildContext context) {
  // 注意:这是列表内容的底部,不是加载更多的 Footer
  return const Padding(
    padding: EdgeInsets.all(16),
    child: Text('这是列表底部'),
  );
}

自定义加载更多指示器

你可以重写 loadMoreFooterBuilder 来自定义加载更多的样式,或者使用全局主题配置。

dart
@override
Widget? loadMoreFooterBuilder(BuildContext context, bool hasMore, bool isLoadingMore) {
  if (!hasMore) {
    return const Center(child: Text('到底啦~'));
  }
  if (isLoadingMore) {
    return const Center(child: CircularProgressIndicator());
  }
  return null;
}

常见问题

1. 如何手动刷新列表?

在 ViewModel 中调用 refresh() 方法:

dart
// 在 Page 中
viewModel.refresh();

// 或者带参数(静默刷新)
viewModel.refresh(init: false);

2. 如何修改分页参数?

可以在 ViewModel 中重写分页配置:

dart
@override
int get pageSize => 20; // 默认 10

@override
int get firstPageNum => 0; // 默认 1

3. 如何处理搜索?

搜索本质上是带参数的刷新。

dart
class UserListViewModel extends BaseListViewModel<User> {
  String _keyword = '';

  void search(String keyword) {
    _keyword = keyword;
    refresh(); // 触发刷新,重新调用 loadData
  }

  @override
  Future<List<User>> loadData({required int pageNum}) async {
    return await UserRepository.getUsers(page: pageNum, keyword: _keyword);
  }
}

Released under the MIT License.