本文介绍Flutter状态管理工具Riverpod + SharedPreference的最佳实践。
如果您不使用SharedPreferences或其他持久性存储方式,当应用程序关闭并重新打开时,用户的状态将丢失。这是因为应用程序的内存状态不会在关闭时持久保存。
如果您希望在应用程序关闭和重新打开时保持用户的状态,您需要将用户的状态数据保存到持久性存储中,例如SharedPreferences、数据库或云存储。
这样,当应用程序重新打开时,您可以从持久性存储中检索用户的状态数据,并将其重新应用到应用程序中。
本文按照以下三步进行展开,集成、声明、读取。
第一步,集成Riverpod
1.添加Riverpod依赖
在pubspec.yaml中dependencies、dev_dependencies位置,添加对应Riverpod依赖,然后执行flutter pub get。
name: my_app_name
environment:
sdk: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
hooks_riverpod: ^2.4.5
flutter_hooks:
riverpod_annotation: ^2.3.0
dev_dependencies:
build_runner:
custom_lint:
riverpod_generator: ^2.3.5
riverpod_lint: ^2.3.3
2.全局声明
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
void main() {
runApp(
// For widgets to be able to read providers, we need to wrap the entire
// application in a "ProviderScope" widget.
// This is where the state of our providers will be stored.
ProviderScope(
child: MyApp(),
),
);
}
第二步,声明Provider
Riverpod提供了很多不同类别的Provider,按需选择即可,下面列举两种常用的Provider,StateNotifierProvider和FutureProvider。
在lib文件夹下创建CustomRiverpods.dart,代码如下:
import 'dart:convert';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'SharedPreferencesService.dart';//在我的另一篇文章,SharedPreference最佳实践 https://juejin.cn/post/7288229413036277818
import 'http.dart'; // 封装Dio,在我的另一篇文章,Dio网络请求最佳实践
//重点,与SharedPreference结合
Provider sharedPreferencesProvider = Provider<SharedPreferencesService>((ref) {
return SharedPreferencesService.instance;
});
//简单对象(simple state object),下面是存储登录用户信息的例子
final userProvider = StateNotifierProvider<UserNotifier, Map<String, dynamic>>((ref) {
final spInstance = ref.watch(sharedPreferencesProvider);
final user = spInstance.getString("user");
return UserNotifier(user != null ? jsonDecode(user) : {});
});
class UserNotifier extends StateNotifier<Map<String, dynamic>> {
UserNotifier(Map<String, dynamic> state) : super(state);
void updateUser(Map<String, dynamic> newUser) {
state = newUser;
}
}
//这段代码的功能是创建一个用于管理user的状态提供程序,并且使用SharedPreferences存储和获取用户数据。
//代码逐行解释如下:
//1. 创建一个名为sharedPreferencesProvider的Provider对象,该对象提供了SharedPreferencesService的实例。Provider是一个用于依赖注入的库,它可以提供单例的对象实例。
//2. 创建一个名为userProvider的StateNotifierProvider对象,用于管理用户数据的状态。该对象接受一个UserNotifier的实例作为状态管理器,并且提供了一个Map<String, dynamic>类型的初始状态。
//3. 创建一个名为UserNotifier的类,继承自StateNotifier<Map<String, dynamic>>。UserNotifier是一个状态管理器,用于更新和管理用户数据。
//4. 在UserNotifier的构造函数中,调用父类的构造函数,并传入初始状态。
//5. 定义了一个名为updateUser的方法,用于更新用户数据。该方法接受一个Map<String, dynamic>类型的新用户数据,并将状态更新为新的用户数据。
//总结:这段代码创建了一个用于管理用户数据的状态提供程序,并使用SharedPreferences存储和获取用户数据。它提供了一个UserNotifier实例作为状态管理器,并提供了一个updateUser方法用于更新用户数据。
//如果不需要持久化存储,以上面的userProvider为例,改造如下:
final userProvider = StateNotifierProvider<UserNotifier, Map<String, dynamic>>((ref) {
return UserNotifier({});
});
class UserNotifier extends StateNotifier<Map<String, dynamic>> {
UserNotifier(Map<String, dynamic> state) : super(state);
void updateUser(Map<String, dynamic> newUser) {
state = newUser;
}
void updateToken(String newToken) {
state = {...state, 'token': newToken};
}
}
//简单对象(simple state object),下面的例子是存储机构树(比如市、区、县)
final organizationTreeProvider = StateNotifierProvider<OrganizationTreeNotifier, Map<String, dynamic>>(
(ref) {
final spInstance = ref.watch(sharedPreferencesProvider);
final organizationTree = spInstance.getString("organizationTree");
return OrganizationTreeNotifier(organizationTree != null ? jsonDecode(organizationTree) : {});
});
class OrganizationTreeNotifier extends StateNotifier<Map<String, dynamic>> {
OrganizationTreeNotifier(Map<String, dynamic> state) : super(state);
void updateOrganizationTree(Map<String, dynamic> newTree) {
state = newTree;
}
void resetOrganizationTree() {
state = {};
}
}
//请求响应(A result from an API call)
final dictFutureProvider = FutureProvider.family<List<dynamic>, String>((ref, dictType) async {
try {
final spInstance = ref.watch(sharedPreferencesProvider);
final localDict = spInstance.getString(dictType);
//如果本地不为空,返回本地结果
if (localDict != null) {
final dictList = jsonDecode(localDict) as List<dynamic>;
if (dictList.isNotEmpty) {
return dictList;
}
}
//如果本地为空,发起远程请求
EasyLoading.show(maskType: EasyLoadingMaskType.black);
final response = await DioApi.post("/api/app/sys/dictData/findListByType", data: {'type': dictType});
EasyLoading.dismiss();
if (response["success"]) {
final result = response["data"] as List<dynamic>;
//请求结果存储到本地
await spInstance.setString(dictType, jsonEncode(result));
//返回结果
return result;
} else {
final errorMsg = response["message"] ?? "获取字典失败: $dictType";
EasyLoading.showError(errorMsg);
return [];
}
} catch (e) {
EasyLoading.showError(e.toString());
EasyLoading.dismiss();
return [];
}
});
第三步,读取Provider
读取StateProvider
import 'CustomRiverpods.dart';
//注意 要将 extends 后面的 StatefulWidget 改为 ConsumerStatefulWidget
class MineScreen extends ConsumerStatefulWidget {
const MineScreen({super.key});
//这里也要对应变更声明
@override
_MineScreenState createState() => _MineScreenState();
}
//同样,这里也要将 State 改为 ConsumerState
class _MineScreenState extends ConsumerState<MineScreen> {
@override
Widget build(BuildContext context) {
//读取userProvider
final user = ref.watch(userProvider);
//更新userProvider
final userNotifier = ref.read(userProvider.notifier);
userNotifier.updateUser(user);
//在Widget中使用
return Text(user["name"]);
}
}
读取FutureProvider
import 'CustomRiverpods.dart';
class CompanyList extends ConsumerStatefulWidget {
@override
_CompanyListState createState() => _CompanyListState();
}
class _CompanyListState extends ConsumerState<CompanyList> {
List licenseTypes = [];
@override
void initState() {
// TODO: implement initState
super.initState();
_extraInit();
}
void _extraInit() async {
//读取dictFutureProvider,并传入参数'licenseType'
final container = ProviderContainer();
licenseTypes =
await container.read(dictFutureProvider('licenseType').future);
setState(() {});
}
}
更多内容可前往官方文档。