Flutter分页搜索插件paginated_search的使用

发布于 1周前 作者 yibo5220 来自 Flutter

Flutter分页搜索插件paginated_search的使用

功能特性

  • 显示加载项:在获取下一页数据时显示加载项。
  • 自动加载下一页:当用户滚动到列表底部时,自动加载下一页(无限滚动)。
  • 可定制化:可以自定义错误、加载和其他视图。
  • 初始查询:可以通过设置初始查询,在视图初始化时加载数据。

功能演示

入门指南

此插件依赖于 Riverpod,请确保你已经熟悉如何使用 Riverpod

使用方法

1. 创建 SearchProvider

SearchProvider 提供搜索数据给分页控制器。你可以通过 PaginatedState 获取当前查询、当前页码、每页的项目数量等信息。

class MockSearchProvider extends SearchProvider<String, String> {
  [@override](/user/override)
  Future<List<String>> performSearch(Ref ref, PaginatedState<String, String> state) async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 400));
    
    // 生成模拟数据
    return List.generate(
      state.pageSize, 
      (index) => '${state.query ?? "No Query"} - ${state.page * state.pageSize + index}'
    );
  }
}
2. 创建分页控制器

创建一个分页控制器,允许分页视图处理不同的流程。你可以指定一个初始查询,以便在视图初始化时加载初始页面的数据。

final paginatedSearchControllerProvider = createPaginatedController(
  searchProvider: MockSearchProvider(),
  initialQuery: "Initial Query"  // 初始查询
);
3. 创建分页搜索视图

现在你可以创建一个分页搜索视图。这个视图包含了搜索框、分页列表和底部加载指示器。

class MyHomePage extends PaginatedSearchView<String, String> {
  const MyHomePage({
    super.key,
    required super.paginatedController,
    super.invalidateOnDispose = false,
  });

  [@override](/user/override)
  ConsumerState<ConsumerStatefulWidget> createState() {
    return _MyHomePageState();
  }
}

class _MyHomePageState extends PaginatedSearchViewState<MyHomePage> with TickerProviderStateMixin {
  late final tabController = TabController(length: 2, vsync: this);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnnotatedRegion<SystemUiOverlayStyle>(
        value: SystemUiOverlayStyle.dark,
        child: SafeArea(
          child: CustomScrollView(
            controller: paginatedScrollController,
            slivers: [
              // 标题
              Text(
                'Paginated Search',
                style: context.textTheme.headlineMedium.bold,
              ).withPadding(s16HorizontalPadding).asSliver,

              // 描述
              const Text('Search through your data easily')
                  .withPadding(s16HorizontalPadding)
                  .asSliver,

              // 搜索框
              TextField(
                onChanged: (value) =>
                    ref.read(paginatedSearchControllerProvider.notifier).search(query: value),
                decoration: const InputDecoration(
                  prefixIcon: Icon(Icons.search_rounded),
                  hintText: 'Search...',
                  filled: true,
                ),
              ).withPadding(s16HorizontalPadding).asSliver,

              // 分页列表
              PaginatedSliverListView(
                paginatedController: paginatedSearchControllerProvider,
                itemBuilder: (item) => Card(
                  elevation: 8,
                  child: Text(
                    item,
                    style: TextStyle(
                      color: context.colorScheme.onSecondaryContainer,
                      fontWeight: FontWeight.bold,
                    ),
                  ).withPadding(s24Padding),
                ).withPadding(s4Vertical8Horizontal),
                loadingBuilder: (_) => const CircularProgressIndicator.adaptive().alignCenter.asSliver,
                errorBuilder: (context, error, st) => const Text("Error happened").asSliver,
              ),

              // 底部加载指示器
              PaginatedBottomWidget(
                paginatedController: paginatedSearchControllerProvider,
                onGoingLoading: (context) => const CircularProgressIndicator.adaptive().alignCenter,
                onGoingErrorBuilder: (context, error, st) => const Text("Something went wrong").alignCenter,
              ).asSliver,
            ],
          ),
        ).withHeaderOverlayGlow(context: context),
      ),
    );
  }
}
4. 运行应用

最后,创建 MyApp 并运行应用。

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      themeMode: ThemeMode.dark,
      darkTheme: ThemeData(
        brightness: Brightness.dark,
        useMaterial3: true,
        colorSchemeSeed: Colors.blue.shade900,
      ),
      debugShowCheckedModeBanner: false,
      home: MyHomePage(paginatedController: paginatedSearchControllerProvider),
    );
  }
}

完整示例代码

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:paginated_search/paginated_helpers.dart';
import 'package:paginated_search/paginated_search.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      themeMode: ThemeMode.dark,
      darkTheme: ThemeData(
        brightness: Brightness.dark,
        useMaterial3: true,
        colorSchemeSeed: Colors.blue.shade900,
      ),
      debugShowCheckedModeBanner: false,
      home: MyHomePage(paginatedController: paginatedSearchControllerProvider),
    );
  }
}

final paginatedSearchControllerProvider =
    createPaginatedController(searchProvider: MockSearchProvider(), initialQuery: "Initial Query");

class MockSearchProvider extends SearchProvider<String, String> {
  [@override](/user/override)
  Future<List<String>> performSearch(Ref ref, PaginatedState<String, String> state) async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 400));
    
    // 生成模拟数据
    return List.generate(
      state.pageSize, 
      (index) => '${state.query ?? "No Query"} - ${state.page * state.pageSize + index}'
    );
  }
}

class MyHomePage extends PaginatedSearchView<String, String> {
  const MyHomePage({
    super.key,
    required super.paginatedController,
    super.invalidateOnDispose = false,
  });

  [@override](/user/override)
  ConsumerState<ConsumerStatefulWidget> createState() {
    return _MyHomePageState();
  }
}

class _MyHomePageState extends PaginatedSearchViewState<MyHomePage> with TickerProviderStateMixin {
  late final tabController = TabController(length: 2, vsync: this);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnnotatedRegion<SystemUiOverlayStyle>(
        value: SystemUiOverlayStyle.dark,
        child: SafeArea(
          child: CustomScrollView(
            controller: paginatedScrollController,
            slivers: [
              // 标题
              Text(
                'Paginated Search',
                style: context.textTheme.headlineMedium.bold,
              ).withPadding(s16HorizontalPadding).asSliver,

              // 描述
              const Text('Search through your data easily')
                  .withPadding(s16HorizontalPadding)
                  .asSliver,

              // 搜索框
              TextField(
                onChanged: (value) =>
                    ref.read(paginatedSearchControllerProvider.notifier).search(query: value),
                decoration: const InputDecoration(
                  prefixIcon: Icon(Icons.search_rounded),
                  hintText: 'Search...',
                  filled: true,
                ),
              ).withPadding(s16HorizontalPadding).asSliver,

              // 分页列表
              PaginatedSliverListView(
                paginatedController: paginatedSearchControllerProvider,
                itemBuilder: (item) => Card(
                  elevation: 8,
                  child: Text(
                    item,
                    style: TextStyle(
                      color: context.colorScheme.onSecondaryContainer,
                      fontWeight: FontWeight.bold,
                    ),
                  ).withPadding(s24Padding),
                ).withPadding(s4Vertical8Horizontal),
                loadingBuilder: (_) => const CircularProgressIndicator.adaptive().alignCenter.asSliver,
                errorBuilder: (context, error, st) => const Text("Error happened").asSliver,
              ),

              // 底部加载指示器
              PaginatedBottomWidget(
                paginatedController: paginatedSearchControllerProvider,
                onGoingLoading: (context) => const CircularProgressIndicator.adaptive().alignCenter,
                onGoingErrorBuilder: (context, error, st) => const Text("Something went wrong").alignCenter,
              ).asSliver,
            ],
          ),
        ).withHeaderOverlayGlow(context: context),
      ),
    );
  }
}

更多关于Flutter分页搜索插件paginated_search的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分页搜索插件paginated_search的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter中使用paginated_search插件进行分页搜索的一个示例代码。paginated_search插件允许你通过分页的方式从数据源中检索和显示数据。以下是一个简单的示例,展示了如何使用这个插件。

首先,确保你已经在pubspec.yaml文件中添加了paginated_search依赖:

dependencies:
  flutter:
    sdk: flutter
  paginated_search: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来是一个完整的示例代码,展示了如何使用PaginatedSearch小部件:

import 'package:flutter/material.dart';
import 'package:paginated_search/paginated_search.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Paginated Search Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final List<String> _data = List.generate(100, (index) => 'Item $index');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Paginated Search Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: PaginatedSearch<String>(
          key: ValueKey('paginatedSearch'),
          searchQuery: '', // 初始搜索查询为空字符串
          itemsPerPage: 10, // 每页显示10个项目
          onSearchQueryChanged: (query) {
            // 这里可以添加处理搜索查询变化的逻辑,例如过滤数据
            setState(() {}); // 强制重建小部件以显示新的搜索结果
          },
          itemBuilder: (context, item, index) {
            return ListTile(
              title: Text(item),
            );
          },
          pageLoader: (context, query, pageIndex) async {
            // 模拟异步加载数据
            await Future.delayed(Duration(seconds: 1)); // 模拟网络延迟
            // 这里返回当前页的数据,根据query进行过滤
            final results = _data
                .where((item) => item.toLowerCase().contains(query.toLowerCase()))
                .skip(pageIndex * 10)
                .take(10)
                .toList();
            return results;
          },
        ),
      ),
    );
  }
}

在这个示例中:

  1. _data是一个包含100个字符串的列表,作为我们的数据源。
  2. PaginatedSearch小部件用于分页搜索。
  3. searchQuery属性设置为空字符串,表示初始时没有搜索查询。
  4. itemsPerPage属性设置为10,表示每页显示10个项目。
  5. onSearchQueryChanged回调用于处理搜索查询的变化。在这个示例中,它只是调用setState来强制重建小部件,但你可以在这里添加更多逻辑,比如过滤数据。
  6. itemBuilder回调用于构建每个项目的UI。在这个示例中,它简单地返回一个ListTile
  7. pageLoader回调是异步的,用于加载当前页的数据。在这个示例中,它模拟了一个网络延迟,然后根据搜索查询和页码返回过滤后的数据。

这个示例展示了paginated_search插件的基本用法,你可以根据需要进行扩展和自定义。

回到顶部