skip to Main Content

In Flutter I currently have the following basic app structure (simplified):

class MyProvider extends ChangeNotifier {
  String get value => 'Hello!';
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: MyProvider()),
      ],
      builder: (context, _) => MaterialApp(
        home: Scaffold(
          body: Center(
            child: Text(context.read<MyProvider>().value),
          ),
        ),
      ),
    );
  }
}

Now, MyProvider needs an async init method. I thought I could initialize the provider in the main function and then pass it along to the app and eventually to the MultiProvider, like so:

class MyProvider extends ChangeNotifier {
  Future<void> init() async {
    // Something async
  }

  String get value => 'Hello!';
}

Future<void> main() async {
  final myProvider = MyProvider();
  await myProvider.init();

  final app = MyApp(
    providers: [
      myProvider,
    ],
  );
  runApp(app);
}

class MyApp extends StatelessWidget {
  const MyApp({super.key, this.providers = const []});

  final List<ChangeNotifier> providers;

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ...providers.map((p) => ChangeNotifierProvider.value(value: p)),
      ],
      builder: (context, _) => MaterialApp(
        home: Scaffold(
          body: Center(
            child: Text(context.read<MyProvider>().value),
          ),
        ),
      ),
    );
  }
}

However, this doesn’t work as it throws a ProviderNotFoundException.
I’m struggling to see why though. To me, the two versions seem almost identical, as the actual ChangeNotifierProvider is created in the same place. Only the value is created somewhere else.

Am I doing something obviously wrong?

As a side note, I know I could make my app work by wrapping the MaterialApp inside a FutureBuilder and initializing the provider there, but that just looks ugly and suboptimal.

2

Answers


  1. You can wrap MyApp widget into MultiProvider

    main() async {
      final p = MyProvider();
      await p.init();
    
      runApp(
       MultiBlocProvider(
         providers:[
           ChangeNotifierProvider<MyProvider>.value(value: p),
         ],
         child: MyApp()
       )
      )
    }
    
    Login or Signup to reply.
  2. Update your provider like this:

    class MyProvider extends ChangeNotifier {
      MyProvider() {
        init();
      }
      Future<void> init() async {
        // Something async
      }
      String get value => 'Hello!';
    }
    

    When you initialize the provider, the init function is automatically called.

    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider<MyProvider>(
              create: (context) => MyProvider(),
            ),
          ],
          builder: (context, _) => MaterialApp(
            home: Scaffold(
              body: Center(
                child: Text(context.read<MyProvider>().value),
              ),
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search