关于flutter的provider出现的错误

About errors that occur in flutter's provider

我正在学习 flutter 的提供程序,但遇到一个错误。

以下代码有效。

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

但是下面的代码不起作用。

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

使用此代码,按下按钮时会输出以下错误。

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:

我不会分离widgets,我想写一个类似code2的widget
有什么好的方法请告诉我。

谢谢!

在你的第一个例子中,NewWidget 是用一个新的 BuildContext 构建的,它已经可以访问它的祖先,所以这个 Widget 可以看到你在那里创建的提供者:context.read<Model>().

但是,在你的第二个例子中,你在同一个小部件 Main 中创建和使用你的提供者,所以所有东西都在同一个 BuildContext 上,当你 运行 context.read<Model>() flutter 将尝试在您的 Widget 树中查找 Model,但它不会找到它,因为您刚刚创建了它。在这种情况下,您可以使用 Builder 小部件:

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

通过使用 Builder 小部件,您可以创建一个新的 BuildContext 可用于检索有关您刚创建的提供商的信息,这是因为您的构建器小部件是在您的 ChangeNotifierProvider 之后构建,它是它的子级,因此它可以轻松地在其父级上查找并找到此信息。

还要注意错误告诉你的是什么,flutter 编译器在这类问题上真的很聪明:

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

这些行准确地告诉你我之前解释的内容。

除了上面的回答,还可以使用Provider包的Consumerclass

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

从 MultiProvider 包装 MaterialApp。并设置 Provider Class(es) 的值。

例如:

return MultiProvider(
  providers: [
    ChangeNotifierProvider.value(
      value: Cart(),
    ),
  ],
  child: MaterialApp(
    title: 'Perfumino',
    home: Home(),
  ),
);