Flutter Riverpod+SharedPreference实现数据持久化 V1.1

838 阅读4分钟

本文介绍Flutter状态管理工具Riverpod + SharedPreference的最佳实践。

如果您不使用SharedPreferences或其他持久性存储方式,当应用程序关闭并重新打开时,用户的状态将丢失。这是因为应用程序的内存状态不会在关闭时持久保存。

如果您希望在应用程序关闭和重新打开时保持用户的状态,您需要将用户的状态数据保存到持久性存储中,例如SharedPreferences、数据库或云存储。

这样,当应用程序重新打开时,您可以从持久性存储中检索用户的状态数据,并将其重新应用到应用程序中。

本文按照以下三步进行展开,集成、声明、读取。

第一步,集成Riverpod

1.添加Riverpod依赖

pubspec.yamldependenciesdev_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,按需选择即可,下面列举两种常用的ProviderStateNotifierProviderFutureProvider

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(() {});
    }

}

更多内容可前往官方文档