Shazamo Morebucks
Shazamo Morebucks

Reputation: 50

Flutter Web, Riverpod and GoRouter, How to access state notifier from within GoRouter

I want users on my app to be able to deep link onto profile pages, for example the user might go to "www.myapp.com/#/users/{userId}", and I want my gorouter to be detect the {userId} variable from the uri, and then feed that data into a state notifier, which would fetch details for that user into the app.

So my code looks something like this:

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationProvider: _router.routeInformationProvider,
      routeInformationParser: _router.routeInformationParser,
      routerDelegate: _router.routerDelegate,
    );
  }

  final GoRouter _router = GoRouter(
    initialLocation: '/login',
    routes: <GoRoute>[
      GoRoute(path: '/', builder: (context, state) => LoginSignupPage()),
      GoRoute(path: '/login', builder: (context, state) => LoginSignupPage()),
      GoRoute(
        path: '/projects/:projectShortId',
        builder: (context, state) {
          var projectShortId = state.params['projectShortId']!;
          return Consumer(
            builder: (context, ref, child) {
              ref
                .read(projectStateNotifierProvider.notifier)
                .setProjectFromCustomURI(projectShortId);
              return ViewProjectPage();
            },
          );
        },
      )
    ],
  );
}

But when I run it, initially typing the url "/projects/{userId1}" works fine, but then if I type a different url "/projects/{userId2}", then I get rebuild errors like the following (they're very long, but its just the following with a bunch of update+rebuild framework errors after it):

Error: setState() or markNeedsBuild() called during build.
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49      throw_    
packages/flutter/src/widgets/framework.dart 4549:11                                                                            <fn>      
packages/flutter/src/widgets/framework.dart 4563:14
markNeedsBuild
packages/flutter_riverpod/src/framework.dart 287:5
[_debugCanModifyProviders]
packages/riverpod/src/framework/provider_base.dart 513:42                                                                      <fn>      
packages/riverpod/src/framework/provider_base.dart 337:7                                                                       setState  
packages/riverpod/src/state_notifier_provider/auto_dispose.dart 118:10                                                         listener  
packages/state_notifier/state_notifier.dart 225:23                                                                             set state 
packages/myapp_mvp/state/providers/project_state_provider/project_state_provider.dart 33:5
setProjectFromCustomURI

Upvotes: 3

Views: 1717

Answers (2)

munsif.ali
munsif.ali

Reputation: 54

define a provider for your GoRouter and inside the provider you can access the ref like this:


final routerProvider = Provider<GoRouter>((ref) {
  return GoRouter(
    initialLocation: '/login',
    routes: <GoRoute>[
      GoRoute(path: '/', builder: (context, state) => LoginSignupPage()),
      GoRoute(path: '/login', builder: (context, state) => LoginSignupPage()),
      GoRoute(
        path: '/projects/:projectShortId',
        builder: (context, state) {
          var projectShortId = state.params['projectShortId']!;
          ref
              .read(projectStateNotifierProvider.notifier)
              .setProjectFromCustomURI(projectShortId);
          return ViewProjectPage();
        },
      )
    ],
  );
});

Upvotes: 1

Bashar
Bashar

Reputation: 214

This Error Is Caused When You're Trying to Build A Widget while another one is building its Propably because you called ref.notifier.read in a builder, thats a very bad practice and could slow down your app if You Are setting The State Inside. may I suggest using navigatorBuilder from The Go Route Library To Create A Consumer Wrapper For Every Child Widget and creatign A Custom Notifier And State To Store Route Paramters And Then Easily Access Them From Any Consumer Widget Like Your ViewProjectPage

 navigatorBuilder: (context, state, child) {
  return Consumer(
    builder: (_, ref, __) {
      ref.watch(authControllerProvider).params = state.params;
         return child;},
  );
},

answer source from thread : How to define a GoRouter that depends on a Provider?

Upvotes: 1

Related Questions