Coder Social home page Coder Social logo

The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart' about riverpod HOT 20 CLOSED

rrousselgit avatar rrousselgit commented on July 20, 2024
The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'

from riverpod.

Comments (20)

zgramming avatar zgramming commented on July 20, 2024 2

@rrousselGit but i still confused , i should make 2 variable to define state what i want listen and method from StateNotifier ?
something like :

final IsLoading isLoading = useProvider(isLoadingProvider); // it for method toggleLoading();
final bool loading = useProvider(isLoadingProvider.state); // it for listen state

Because documentation example i attach above , not showing to listen state and only show call method counter.increment()

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024 1

As said by StateNotifierProvider, if you want to listen to the state of a StateNotifier, use "useProvider(myStateNotifierProvider.state)"

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024 1

Oh my bad, that's for the next changes

For now you can do ref.read(userProvider).addListener((state) {...}

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024 1

Not necessarily a good practice, as the value may change over time.
You may want a StreamProvider for that reason too

from riverpod.

Tameflame avatar Tameflame commented on July 20, 2024 1

How would I listen to state changes without using a hookwidget? Is the below correct?

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Search Sellers")),
      body: Consumer(
        builder: (context, watch, child) {
          var location = watch(mapApiProvider.state); // mapApiProvider is a StateNotifierProvider
          var myLatLong = {
            location.userLocation.latitude,
            location.userLocation.longitude
          };
          return Center(
              child: Text("You're location is ${myLatLong.toString()}"));
        },
      ),
    );
  }

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024 1

Yes this is correct

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024

Yes, that is correct.

Although the counter example does counter.read(context).increment()

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

It's more clear for me. But it's have option/possible make it more simple ? something like i only make 1 variable final loading = useProvider(loadingProvider) . and this variable can use for calling method and listen state ?

final loading = userProvider(loadingProvider);

//Listen state
Text(loading.state);

//Calling method 
loading.toggleLoading(value);

with above approach , will make warning like my thread . Because make 2 variable with almost same name , make me confused.

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024

No this is expected

It allows reading the notifier without listening to the state, to avoid pointless rebuilds.

from riverpod.

edisonywh avatar edisonywh commented on July 20, 2024

Mhm, I am trying to do exactly that (one for reading and one for methods), but I can't seem to get the method way to work?

final StateNotifierProvider themeProvider = StateNotifierProvider((ref) {
  return ThemeManager();
});

class ThemeManager extends StateNotifier<ThemeData> {
  ThemeManager() : super(darkTheme);

  void setTheme(ThemeData theme) async {
    state = theme;
  }
}

Then now when I try to obtain toggle setTheme, I retrieve it like so:

final ThemeManager themeManager = useProvider(themeProvider);

But I get error of:

The argument type 'StateNotifierProvider<StateNotifier>' can't be assigned to the parameter type 'ProviderListenable'.

What am I doing wrong here?

EDIT: Nevermind, looks like I just had to remove the type annotation for:

- final StateNotifierProvider themeProvider = StateNotifierProvider((ref) {
+ final themeProvider = StateNotifierProvider((ref) {
  return ThemeManager();
});

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024

As I mentioned, the syntax is "useProvider(themeProvider.state)"

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

@rrousselGit i don't know it's should create new issue or not , i got this warning again but in FutureProvider i want know this is best practice or not.

I have simple Http get request to show all user from server, and i using manage it using 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;
  }
}

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 :

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 again The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.

How can i get idUser from userProvider ? and i want know this is best practice using FutureProvider ?

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

@rrousselGit ref.read(userProvider.state); give me error :

The argument type 'StateNotifierStateProvider<UserGoogleModel>' can't be assigned to the parameter type 'ProviderBase<ProviderDependency<dynamic>, Object>'.

I think method read on Consumer and FutureProvider it's different.

read on Consumer accept Res Function<Res>(ProviderBase<ProviderDependencyBase, Res>)
read on FutureProviduer accept ProviderBase<ProviderDependency<T>, Object>

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

@rrousselGit i have 2 problem with this approach .

  1. Value obtained from listener always null , although i'm sure it's have value.

UserProvider

final showSpecificUser = FutureProvider.autoDispose((ref) async {
  UserGoogleModel temp;
  final usrProvider = ref.read(userProvider);
  usrProvider.addListener((state) async {
    final result = await usrProvider.searchUserByIdOrEmail(emailuser: state.emailUser);
    // print('listenern RESULT ${result.nameUser} || ${result.emailUser} || ${result.idUser}');
    temp = result;
    print('Temp RESULT ${result.nameUser} || ${result.emailUser} || ${result.idUser}');
  });

  return temp;
});

And i simple call it like this

     Consumer((ctx, read) {
              final provider = read(showSpecificUser);
              return provider.when(
                data: (value) {
                  if (value == null) {
                    return Text('Not have data');
                  }
                  // print('specific user ${value.nameUser}');
                  return Text(value.nameUser ?? 'null');
                },
                loading: () => const CircularProgressIndicator(),
                error: (error, stackTrace) => Text(error.toString()),
              );
            }),

Temp Result

tanya rousel

tanya rousel 2

2.With addListener((state)async{}) it will always listen ,although i already used FutureProvider.autoDisposed.

Although already used FutureProvider.autoDisposed , after logout it's still listen old value. please see below gif.
tanya rousel 3

from riverpod.

AlexHartford avatar AlexHartford commented on July 20, 2024

Can't you just add a getter for state in your class that extends StateNotifier? 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.

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

@AlexHartford interesting, i will try it .

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

@rrousselGit based on my above code , what's the problem with my code ?

from riverpod.

rrousselGit avatar rrousselGit commented on July 20, 2024

If "t" changes,this will not print the new value

from riverpod.

zgramming avatar zgramming commented on July 20, 2024

@rrousselGit I think best option it's waiting you implement what you mention on it

from riverpod.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.