3

I using Flutter Riverpod package to handling http request. I have simple Http get request to show all user from server, and i using manage it using FutureProvider from Flutter Riverpod package.

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;
  }
}

User Provider

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

After that setup, i simply can call showAllUser like this :

    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'),
              );
            }),

it's no problem if http request don't have required parameter, but i got problem if my http request required parameter. I don't know how to handle this.

Let's say , i have another http get to show specific user from id user or email user. Then API look like :

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;
  }

User Provider

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

When i access idUser from userProvider using usrProvider.state.idUser , i got this warning. The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.

It's similiar problem with my question on this, but on that problem i already know to solved using read(userProvider.state) , but in FutureProvider i can't achieved same result using ref(userProvider).

I missed something ?

anticafe
  • 6,816
  • 9
  • 43
  • 74
Zeffry Reynando
  • 3,445
  • 12
  • 49
  • 89

1 Answers1

0

Warning: This is not a long-term solution

Assuming that your FutureProvider is being properly disposed after each use that should be a suitable workaround until the new changes to Riverpod are live. I did a quick test to see and it does work. Make sure you define a getter like this and don't override the default defined by 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);
});

Not ideal but seems like a fine workaround. I believe the intention of state being inaccessible outside is to ensure state manipulations are handled by the StateNotifier itself, so using a getter in the meantime wouldn't be the end of the world.

Alex Hartford
  • 5,110
  • 2
  • 19
  • 36
  • On the Riverpod docs here https://riverpod.dev/docs/concepts/combining_providers at title _Can I read a provider without listening to it?_ the devs tell us to not in any circumstance use `.read()` inside a provider – MwBakker Jan 16 '22 at 22:39
  • @MwBakker this question/answer aren't really relevant anymore. This problem shouldn't occur anymore in newer versions of riverpod. – Alex Hartford Jan 17 '22 at 16:38