Flutter Riverpod:如何实现 FutureProvider?

Posted

技术标签:

【中文标题】Flutter Riverpod:如何实现 FutureProvider?【英文标题】:Flutter Riverpod : How to Implement FutureProvider? 【发布时间】:2020-11-11 12:20:22 【问题描述】:

我使用Flutter Riverpod 包来处理http 请求。我有简单的 Http get 请求来显示来自服务器的所有用户,我使用来自Flutter Riverpod 包的 FutureProvider 来管理它。

API

class UserGoogleApi 
  Future<List<UserGoogleModel>> getAllUser() async 
    final result = await reusableRequestServer.requestServer(() async 
      final response =
          await http.get('$appConfig.baseApiUrl/$appConfig.userGoogleController/getAllUser');
      final Map<String, dynamic> responseJson = json.decode(response.body);
      if (responseJson['status'] == 'ok') 
        final List list = responseJson['data'];
        final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
        return listUser;
       else 
        throw responseJson['message'];
      
    );
    return result;
  

用户提供者

class UserProvider extends StateNotifier<UserGoogleModel> 
  UserProvider([UserGoogleModel state]) : super(UserGoogleModel());
   
Future<UserGoogleModel> searchUserByIdOrEmail(
    String idUser,
    String emailuser,
    String idOrEmail = 'email_user',
  ) async 
    final result = await _userGoogleApi.getUserByIdOrEmail(
      idUser: idUser,
      emailUser: emailuser,
      idOrEmail: idOrEmail,
    );
    UserGoogleModel temp;
    for (var item in result) 
      temp = item;
    
    state = UserGoogleModel(
      idUser: temp.idUser,
      createdDate: temp.createdDate,
      emailUser: temp.emailUser,
      imageUser: temp.emailUser,
      nameUser: temp.nameUser,
      tokenFcm: temp.tokenFcm,
      listUser: state.listUser,
    );

    return temp;
  

  Future<List<UserGoogleModel>> showAllUser() async 
    final result = await _userGoogleApi.getAllUser();
    state.listUser = result;
    return result;
  


final userProvider = StateNotifierProvider((ref) => UserProvider());

final showAllUser = FutureProvider.autoDispose((ref) async 
  final usrProvider = ref.read(userProvider);
  final result = await usrProvider.showAllUser();
  return result;
);

设置完成后,我可以像这样调用 showAllUser

    Consumer((ctx, read) 
              final provider = read(showAllUser);
              return provider.when(
                data: (value) 
                  return ListView.builder(
                    itemCount: value.length,
                    shrinkWrap: true,
                    itemBuilder: (BuildContext context, int index) 
                      final result = value[index];
                      return Text(result.nameUser);
                    ,
                  );
                ,
                loading: () => const CircularProgressIndicator(),
                error: (error, stackTrace) => Text('Error $error'),
              );
            ),

如果http请求没有必填参数也没问题,但是如果我的http请求需要参数我就遇到了问题。我不知道如何处理。

假设,我有另一个 http get 来显示来自 id 用户或电子邮件用户的特定用户。然后 API 看起来像:

API

  Future<List<UserGoogleModel>> getUserByIdOrEmail(
    @required String idUser,
    @required String emailUser,
    @required String idOrEmail,
  ) async 
    final result = await reusableRequestServer.requestServer(() async 
      final baseUrl =
          '$appConfig.baseApiUrl/$appConfig.userGoogleController/getUserByIdOrEmail';
      final chooseURL = idOrEmail == 'id_user'
          ? '$baseUrl?id_or_email=$idOrEmail&id_user=$idUser'
          : '$baseUrl?id_or_email=$idOrEmail&email_user=$emailUser';
      final response = await http.get(chooseURL);
      final Map<String, dynamic> responseJson = json.decode(response.body);
      if (responseJson['status'] == 'ok') 
        final List list = responseJson['data'];
        final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
        return listUser;
       else 
        throw responseJson['message'];
      
    );
    return result;
  

用户提供者

final showSpecificUser = FutureProvider.autoDispose((ref) async 
  final usrProvider = ref.read(userProvider);

  final result = await usrProvider.searchUserByIdOrEmail(
    idOrEmail: 'id_user',
    idUser: usrProvider.state.idUser, //  => warning on "state"
  );
  return result;
);

当我使用 usrProvider.state.idUser 从 userProvider 访问 idUser 时,我收到了这个警告。 The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'. 这与我在this 上的问题类似,但在那个问题上我已经知道使用read(userProvider.state) 解决了,但是在FutureProvider 中我无法使用ref(userProvider) 获得相同的结果。

我错过了什么?

【问题讨论】:

【参考方案1】:

警告:这不是长期解决方案

假设您的 FutureProvider 在每次使用后都被正确处理,这应该是一个合适的解决方法,直到 Riverpod 的新更改生效。我做了一个快速测试看看,它确实有效。确保您定义了这样的 getter,并且不要覆盖 StateNotifier 定义的默认值。

class A extends StateNotifier<B> 
...
  static final provider = StateNotifierProvider((ref) => A());

  getState() => state;
...

final provider = FutureProvider.autoDispose((ref) async 
    final a = ref.read(A.provider);
    final t = a.getState();
    print(t);
);

不理想,但似乎是一个很好的解决方法。我相信在外部无法访问状态的目的是确保状态操作由 StateNotifier 本身处理,因此在此期间使用 getter 不会是世界末日。

【讨论】:

在 Riverpod 文档上 riverpod.dev/docs/concepts/combining_providers 的标题 我可以在不听的情况下阅读提供程序吗? 开发人员告诉我们在任何情况下都不要使用 .read() 在提供者 @MwBakker 这个问题/答案不再相关。在更新版本的riverpod 中不应再出现此问题。

以上是关于Flutter Riverpod:如何实现 FutureProvider?的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 中使用 Riverpod 实现 pull-to-refresh

重走Flutter状态管理之路—Riverpod最终篇

重走Flutter状态管理之路—Riverpod最终篇

重走Flutter状态管理之路—Riverpod进阶篇

重走Flutter状态管理之路—Riverpod进阶篇

Flutter 用 provider 和 riverpod 制作表单