Flutter分页搜索插件paginated_search的使用
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
更多关于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;
},
),
),
);
}
}
在这个示例中:
_data
是一个包含100个字符串的列表,作为我们的数据源。PaginatedSearch
小部件用于分页搜索。searchQuery
属性设置为空字符串,表示初始时没有搜索查询。itemsPerPage
属性设置为10,表示每页显示10个项目。onSearchQueryChanged
回调用于处理搜索查询的变化。在这个示例中,它只是调用setState
来强制重建小部件,但你可以在这里添加更多逻辑,比如过滤数据。itemBuilder
回调用于构建每个项目的UI。在这个示例中,它简单地返回一个ListTile
。pageLoader
回调是异步的,用于加载当前页的数据。在这个示例中,它模拟了一个网络延迟,然后根据搜索查询和页码返回过滤后的数据。
这个示例展示了paginated_search
插件的基本用法,你可以根据需要进行扩展和自定义。