Flutter无限滚动数据加载插件riverpod_infinite_scroll的使用
Flutter无限滚动数据加载插件riverpod_infinite_scroll的使用
概述
riverpod_infinite_scroll
是一个专门为 Riverpod
设计的插件,用于实现无限滚动的数据加载。它基于 infinite_scroll_pagination
插件,提供了一个名为 RiverPagedBuilder
的小部件来构建无限滚动列表。
安装和导入
首先,您需要在项目中添加依赖项:
flutter pub add riverpod_infinite_scroll
flutter pub add infinite_scroll_pagination
然后在 Dart 文件中导入所需的包:
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:riverpod_infinite_scroll/riverpod_infinite_scroll.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
如何工作
RiverPagedBuilder
是这个插件的核心部件,它期望一个 Riverpod 的 StateNotifierProvider
,该 Provider 必须实现两个方法:load
和 nextPageKeyBuilder
。
示例 - 简单版本
假设我们有一个返回 Post
对象列表的 API,并且我们需要显示这些 Post 的 feed。
Post 模型
class Post {
final int id;
final String title;
final String image;
const Post({required this.id, required this.title, required this.image});
}
StateNotifier 实现
class EasyExampleNotifier extends PagedNotifier<int, Post> {
EasyExampleNotifier()
: super(
load: (page, limit) => Future.delayed(const Duration(seconds: 2), () {
return [
const Post(id: 1, title: "My first work", image: "https://www.mywebsite.com/image1"),
const Post(id: 2, title: "My second work", image: "https://www.mywebsite.com/image2"),
const Post(id: 3, title: "My third work", image: "https://www.mywebsite.com/image3"),
];
}),
nextPageKeyBuilder: NextPageKeyBuilderDefault.mysqlPagination,
);
void add(Post post) {
state = state.copyWith(records: [...(state.records ?? []), post]);
}
void delete(Post post) {
state = state.copyWith(records: [...(state.records ?? [])]..remove(post));
}
}
final easyExampleProvider = StateNotifierProvider<EasyExampleNotifier, PagedState<int, Post>>(
(_) => EasyExampleNotifier(),
);
使用 RiverPagedBuilder
class EasyExample extends StatelessWidget {
const EasyExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: RiverPagedBuilder<int, Post>(
firstPageKey: 0,
provider: easyExampleProvider,
itemBuilder: (context, item, index) => ListTile(
leading: Image.network(item.image),
title: Text(item.title),
),
pagedBuilder: (controller, builder) =>
PagedListView(pagingController: controller, builderDelegate: builder),
),
);
}
}
更复杂的示例
如果需要处理更复杂的状态,可以创建自定义的 StateNotifier
并使用 PagedNotifierMixin
。
User 模型
class User {
final String id;
final String name;
final String profilePicture;
const User({required this.id, required this.name, required this.profilePicture});
}
自定义 StateNotifier
class CustomExampleState extends PagedState<String, User> {
final bool filterByCity;
const CustomExampleState({
this.filterByCity = false,
List<User>? records,
String? error,
String? nextPageKey,
}) : super(records: records, error: error, nextPageKey: nextPageKey);
@override
CustomExampleState copyWith({
bool? filterByCity,
List<User>? records,
dynamic error,
dynamic nextPageKey,
}) {
final sup = super.copyWith(records: records, error: error, nextPageKey: nextPageKey);
return CustomExampleState(
filterByCity: filterByCity ?? this.filterByCity,
records: sup.records,
error: sup.error,
nextPageKey: sup.nextPageKey,
);
}
}
class CustomExampleNotifier extends StateNotifier<CustomExampleState>
with PagedNotifierMixin<String, User, CustomExampleState> {
CustomExampleNotifier() : super(const CustomExampleState());
@override
Future<List<User>?> load(String page, int limit) async {
try {
if (state.previousPageKeys.contains(page)) {
await Future.delayed(const Duration(seconds: 0), () {
state = state.copyWith();
});
return state.records;
}
var users = await Future.delayed(const Duration(seconds: 3), () {
return [
const User(
id: "abcdef",
name: "John",
profilePicture: "https://www.mywebsite.com/images/1",
),
const User(
id: "asdfgh",
name: "Mary",
profilePicture: "https://www.mywebsite.com/images/2",
),
const User(
id: "qwerty",
name: "Robert",
profilePicture: "https://www.mywebsite.com/images/3",
),
];
});
state = state.copyWith(
records: [...(state.records ?? []), ...users],
nextPageKey: users.length < limit ? null : users[users.length - 1].id,
previousPageKeys: {...state.previousPageKeys, page}.toList(),
);
} catch (e) {
state = state.copyWith(error: e.toString());
}
}
void add(User user) {
state = state.copyWith(records: [...(state.records ?? []), user]);
}
void delete(User user) {
state = state.copyWith(records: [...(state.records ?? [])]..remove(user));
}
}
final customExampleProvider = StateNotifierProvider<CustomExampleNotifier, CustomExampleState>(
(_) => CustomExampleNotifier(),
);
使用 RiverPagedBuilder 显示用户列表
class CustomExample extends StatelessWidget {
const CustomExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: RiverPagedBuilder<String, User>(
firstPageKey: 'FirstPage',
provider: customExampleProvider,
itemBuilder: (context, item, index) => ListTile(
leading: Image.network(item.profilePicture),
title: Text(item.name),
),
pagedBuilder: (controller, builder) => PagedGridView(
pagingController: controller,
builderDelegate: builder,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
),
),
);
}
}
主应用程序入口
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const EasyExample()));
},
child: const Text('Easy example'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const CustomExample()));
},
child: const Text('Custom example'),
),
],
),
);
}
}
通过上述示例,您可以轻松地在 Flutter 应用中实现无限滚动列表并使用 Riverpod 进行状态管理。希望这对您有所帮助!
更多关于Flutter无限滚动数据加载插件riverpod_infinite_scroll的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter无限滚动数据加载插件riverpod_infinite_scroll的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何使用 riverpod_infinite_scroll
插件在 Flutter 中实现无限滚动数据加载的示例代码。riverpod_infinite_scroll
是一个结合 Riverpod 状态管理和无限滚动功能的插件。
首先,确保你的 pubspec.yaml
文件中已经添加了必要的依赖:
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^1.0.0 # 确保使用最新版本的 Riverpod
riverpod_infinite_scroll: ^0.1.0 # 确保使用最新版本的 riverpod_infinite_scroll
然后,运行 flutter pub get
来获取这些依赖。
接下来,我们编写一个示例应用,展示如何使用 riverpod_infinite_scroll
实现无限滚动。
主文件 main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_infinite_scroll/riverpod_infinite_scroll.dart';
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Infinite Scroll Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: InfiniteScrollPage(),
);
}
}
class InfiniteScrollPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final controller = ref.watch(infiniteScrollControllerProvider);
final items = ref.watch(itemsProvider(controller.page));
return Scaffold(
appBar: AppBar(
title: Text('Infinite Scroll Demo'),
),
body: RefreshIndicator(
onRefresh: () async {
controller.reset();
await controller.loadMore();
},
child: InfiniteScrollListView.builder(
controller: controller,
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${items[index]}'),
);
},
onLoading: () => Center(child: CircularProgressIndicator()),
onError: (error, stackTrace) => Center(child: Text('Error: $error')),
),
),
);
}
}
final itemsProvider = Provider.autoDispose<List<int>>((ref, [int? page]) {
page ??= 1;
return ref.watch(paginatedItemsProvider(page));
});
final paginatedItemsProvider = Provider.autoDispose<Future<List<int>>>((ref, int page) async {
// Simulate network fetch with a delay
await Future.delayed(Duration(seconds: 1));
final start = (page - 1) * 20;
return List.generate(20, (index) => start + index + 1);
});
final infiniteScrollControllerProvider = Provider<InfiniteScrollController>((ref) {
return InfiniteScrollController(
pageCallback: (page) async {
final items = await ref.read(paginatedItemsProvider(page + 1).future);
ref.read(itemsProvider.notifier(page + 1)).state = items;
},
errorWidget: (error, stackTrace) => Text('Error: $error'),
);
});
解释
-
依赖注入:
- 使用
ProviderScope
包裹根应用,确保 Riverpod 的依赖注入正常工作。
- 使用
-
状态管理:
itemsProvider
:根据当前页码提供当前页的数据列表。paginatedItemsProvider
:模拟分页数据获取,返回一个 Future,代表网络请求或数据库查询。infiniteScrollControllerProvider
:创建InfiniteScrollController
实例,并定义加载更多数据的逻辑。
-
UI 构建:
InfiniteScrollPage
:使用ConsumerWidget
监听 Riverpod 状态。InfiniteScrollListView.builder
:构建无限滚动列表视图,并处理加载更多、加载错误等状态。
-
刷新功能:
- 使用
RefreshIndicator
实现下拉刷新功能,当触发刷新时重置控制器并重新加载数据。
- 使用
这个示例展示了如何使用 riverpod_infinite_scroll
插件在 Flutter 应用中实现无限滚动数据加载。你可以根据自己的需求调整数据获取逻辑和 UI 样式。