Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

About errors that occur in flutter's provider

I'm learning about flutter's provider and I'm suffering from one error.

The following code works.

[code1]
class Model extends ChangeNotifier {
  void save() {
    print('save');
  }
}
class Main extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => Model(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('test'),
        ),
        body: NewWidget(),
      ),
    );
  }
}

class NewWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text(
        "test",
      ),
      onPressed: () {
        context.read<Model>().save();
      },
    );
  }
}

But the code below does not work.

[code2]
class Model extends ChangeNotifier {
  void save() {
    print('save');
  }
}

class Main extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => Model(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('test'),
        ),
        body: RaisedButton(
          child: Text(
            "test",
          ),
          onPressed: () {
            context.read<Model>().save();
          },
        ),
      ),
    );
  }
}

With this code, the following error is output when the button is pressed.

Error: Could not find the correct Provider<Model> above this Main Widget

This likely happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:

- The provider you are trying to read is in a different route.

  Providers are "scoped". So if you insert of provider inside a route, then
  other routes will not be able to access that provider.

- You used a `BuildContext` that is an ancestor of the provider you are trying to read.

  Make sure that Main is under your MultiProvider/Provider<Model>.
  This usually happen when you are creating a provider and trying to read it immediatly.

  For example, instead of:

I don't separate widgets, I want to write one widget like code2.
Please let me know if there is any good way.

Thanks!

like image 751
日高岳大 Avatar asked Jun 16 '20 22:06

日高岳大


3 Answers

In your first example, NewWidget is build with a new BuildContext, that already have access to it's ancestor, so this Widget can see the provider that you have created there with: context.read<Model>().

But, on your second example, you are creating and using your provider all in the same Widget Main so everything on the same BuildContext, and when you run context.read<Model>() flutter will try to look up in your Widget tree to find the Model, but it won't find it because you have just created it. That's a scenario where you could make use, of the Builder Widget:

class Main extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => Model(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('test'),
        ),
        body: Builder(
          // Here the magic happens
          // this builder function will generate a new BuilContext for you
          builder: (BuildContext newContext){
            return RaisedButton(
              child: Text(
                "test",
              ),
              onPressed: () {
                 newContext.read<Model>().save();
              },
            );
          }
        ),
      ),
    );
  }
}

By using the Builder Widget, you have the ability to create a new BuildContext the can be used to retrieve information about the provider you have just created, that's because your Builder widget, is build after your ChangeNotifierProvider, and it is a child of it, so it can easily look up and find this information on it's parent.

Also pay attention on what errors tell you, flutter compiler is really smart with that kind of issues:

Make sure that Main is under your MultiProvider/Provider. This usually happen when you are creating a provider and trying to read it immediatly.

These lines tell you exactly what I explained before.

like image 60
Leo Letto Avatar answered Nov 17 '22 15:11

Leo Letto


In addition to the above answer, you can also use the Consumer class of the Provider package.

import 'package:flutter/material.dart';    
import 'package:flutter/foundation.dart';    
import 'package:provider/provider.dart';

class Model extends ChangeNotifier {    
  void save() {    
  print('save');    
  }    
}    

class Main extends StatelessWidget {    
  @override    
  Widget build(BuildContext context) {    
    return ChangeNotifierProvider(    
      create: (context) => Model(),    
      child: Scaffold(    
        appBar: AppBar(    
          title: Text('test'),    
        ),    
        body: Consumer<Model>(    
          builder: (context, model, _) {    
            return RaisedButton(    
              child: Text(    
                "test",    
              ),    
              onPressed: () {    
                context.read<Model>().save();    
              },    
            );    
          },    
        ),    
      ),    
    );    
  }    
}    
like image 25
Faizan Ahmad Avatar answered Nov 17 '22 14:11

Faizan Ahmad


Wrap MaterialApp from MultiProvider. and set the Provider Class(es) in value.

Ex:

return MultiProvider(
  providers: [
    ChangeNotifierProvider.value(
      value: Cart(),
    ),
  ],
  child: MaterialApp(
    title: 'Perfumino',
    home: Home(),
  ),
);
like image 1
Gayan Chinthaka Avatar answered Nov 17 '22 16:11

Gayan Chinthaka