无法在此小部件上方找到正确的提供者

har*_*B10 6 dart flutter flutter-provider

我在使用 Flutter Provider 时遇到了问题......我的流程是这样的:登录用户 ID 被传递给新的小部件后 -> 从那里执行保存到数据库,然后重定向到新的小部件(仪表板)。

这是登录后小部件的代码:

return MaterialApp(
      title: title,
      home: Scaffold(
          appBar: AppBar(
            title: Text(title),
          ),
          body: ListView(
            children: <Widget>[
              Container(
                margin: EdgeInsets.all(8.0),
                child: Card(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(Radius.circular(8.0))),
                  child: InkWell(
                    onTap: () {
                      var user = Provider.of<UserRepository>(context);
                      user.savePreference(user.user.id, "Something");
                      user.navigateToNewPage(Dashboard(), context);
                      print(user.user.id);
                    },
Run Code Online (Sandbox Code Playgroud)

这有效:

user.savePreference(user.user.id, "Something");
Run Code Online (Sandbox Code Playgroud)

但这导致了一个问题:

user.navigateToNewPage(Dashboard(), context);
Run Code Online (Sandbox Code Playgroud)

在仪表板小部件中,我正在创建:

 Widget build(BuildContext context) {
    var user = Provider.of<UserRepository>(context);
Run Code Online (Sandbox Code Playgroud)

在 UserRepository 我有这个:

class UserRepository with ChangeNotifier {
  User user;
  Status _status = Status.Uninitialized;

  Status get status => _status;
  User get getUser => user;

  UserRepository.instance();

Future<void> navigateToNewPage(Widget page, BuildContext context) {
    Navigator.push(context, MaterialPageRoute(builder: (context) => page));
  }
Run Code Online (Sandbox Code Playgroud)

我知道这个话题已经解决了问题,但找不到适合我的问题的任何内容。

Bak*_*ker 51

提供者范围

MaterialApp
 > provider(Screen A)
 > Screen B
Run Code Online (Sandbox Code Playgroud)

如果Provider在屏幕 A 中实例化,则在Navigator.push从 A之后无法在屏幕 B 中访问它?B.

为什么?

因为Provideris an InheritedWidgetand在其 Screen A范围之外Navigator使用。(请参阅下面的详细信息。)MaterialApp contextcontext

使固定

Provider向上移动到公共父级,MaterialApp context允许屏幕 A 和 B 继承其状态/上下文。

provider(MaterialApp)
 > Screen A
 > Screen B
Run Code Online (Sandbox Code Playgroud)

例子

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// wrap MaterialApp in Provider widget
    return ChangeNotifierProvider(
      create: (context) => ColorModel(), // ? create/init your state model
      child: MaterialApp(
          home: ScreenA()
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

细节

提供者

  • Provider是基于InheritedWidget. 只有小部件可以继承父小部件的状态。

    • Provider需要是任何想要访问“提供”状态对象的小部件树的根小部件。

航海家

  • Navigator.push(context)在屏幕上不使用context从屏幕A.
    • 它使用context来自MaterialApp.
  • Navigator.push(context) 实际上是 Navigator.of(context).push
  • Navigator.of(context)意味着:搜索这个上下文层次结构,直到找到一个实例化导航器的上下文
    • 默认NavigatorMaterialApp.
    • 除非您明确创建/使用不同的Navigator,否则您将使用默认值。
    • Navigatorcontext就是MaterialApp.
  • B屏将获得方面(的MaterialApp),没有屏幕A.上下文
    • B 是A的兄弟姐妹,而不是它的孩子。
    • B 不是从 A 继承的,即使它在contextA内部看起来是“实例化的” 。
    • 屏幕 B 是 的MaterialApp context,而不是屏幕 A 的子项context
    • Provider context 范围,如果在屏幕 A 中定义,则不包括屏幕 B

屏幕 A ? 乙

Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenB()))
Run Code Online (Sandbox Code Playgroud)

实际上是:

Navigator.of(context).push(MaterialPageRoute(builder: (context) => ScreenB()))
Run Code Online (Sandbox Code Playgroud)

这就像:

Navigator.of(MaterialApp).push(
  MaterialPageRoute(builder: (MaterialAppContext) => ScreenB())
)
Run Code Online (Sandbox Code Playgroud)

所以屏幕 B在屏幕 A之下MaterialApp context而不是在屏幕 A之下,context因此无法访问屏幕 AProvider及其context.

类似问题的代码示例此答案Provider

  • 这是一个非常有用的答案,值得更多的赞成票...... (4认同)
  • /// 将 MaterialApp 包装在 Provider 小部件中对我有用@AliYarKhan 我在下面评论了 multiprovider。 (2认同)

Mik*_*kin 7

尝试将位于 Scaffold 下方的小部件树的一部分提取到单独的小部件。您现在使用的上下文用于构建您的顶级小部件,该小部件还没有导航器。

生成的代码应如下所示:

return MaterialApp(
      title: title,
      home: Scaffold(
          appBar: AppBar(
            title: Text(title),
          ),
          body: LoginWidget()
Run Code Online (Sandbox Code Playgroud)
class LoginWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
            children: <Widget>[
              Container(
                margin: EdgeInsets.all(8.0),
                child: Card(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(Radius.circular(8.0))),
                  child: InkWell(
                    onTap: () {
                      var user = Provider.of<UserRepository>(context);
                      user.savePreference(user.user.id, "Something");
                      user.navigateToNewPage(Dashboard(), context);
                      print(user.user.id);
                    },

...

  }
}
Run Code Online (Sandbox Code Playgroud)

  • 您能说得更具体一些吗? (2认同)

Buf*_*ffK 7

contextProvider.of(context) 的参数必须是您定义的提供程序的子项。

@override
  Widget build(BuildContext context) {
    // !important here, Scaffold.of(context) returns null
    return Scaffold(
      appBar: AppBar(title: Text('Demo')),
      body: Builder(
        builder: (BuildContext context) {
          return FlatButton(
            child: Text('BUTTON'),
            onPressed: () {
              // here, Scaffold.of(context) returns the locally created Scaffold
              Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('Hello.')
              ));
            }
          );
        }
      )
    );
  }
Run Code Online (Sandbox Code Playgroud)

官方示例:https : //api.flutter.dev/flutter/widgets/BuildContext-class.html

这是我的代码:

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [ChangeNotifierProvider<SomeModel>(
        create: (context){
          return SomeModel();
        },
      ),],
      child: Builder(builder: (BuildContext context){
        BuildContext rootContext = context;
        return Container(
//Here to use rootContext is safe
//Provider.of<SomeModel>(rootContext, listen: false);
        );
      }),
    );
}
Run Code Online (Sandbox Code Playgroud)


Era*_*rlu 6

这就是我们与多个提供商一起使用的方式

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (context) => GeneralProvider(),
      ),
    ],
    child: MaterialApp(
      home: InitPage(),
      onGenerateRoute: MyRouter.generateRoute,
      initialRoute: '/InitPage',
      debugShowCheckedModeBanner: false,
      title: 'Eray',
    ),
  );
}
}
Run Code Online (Sandbox Code Playgroud)