【问题标题】:Flutter - Could not find the correct ProviderFlutter - 找不到正确的提供者
【发布时间】:2021-02-19 14:53:39
【问题描述】:

我有一个具有如下文件结构的应用程序:main -> auth -> home -> secret。关键代码如下:

对于main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<User>.value(value: AuthService().user),
        ChangeNotifierProvider(create: (context) => SecretProvider()),
      ],
      child: MaterialApp(
        title: 'My Secrets',
        home: AuthScreen(),
      ),
    );
  }
}

对于home.dart

class HomeScreen extends StatelessWidget {
  final AuthService _auth = AuthService();

  @override
  Widget build(BuildContext context) {
    var secretProvider = Provider.of<SecretProvider>(context);

    return ChangeNotifierProvider(
      create: (context) => SecretProvider(),
      child: Scaffold(
        appBar: AppBar(
          // some codes...
        ),
        body: StreamBuilder<List<Secret>>(
          stream: secretProvider.secrets,
          builder: (context, snapshot) {
            return Padding(
              padding: EdgeInsets.symmetric(vertical: 15.0),
              child: ListView.separated(
                // return 0 if snapshot.data is null
                itemCount: snapshot.data?.length ?? 0,
                itemBuilder: (context, index) {
                  return ListTile(
                    leading: Icon(Icons.web),
                    title: Text(snapshot.data[index].title),
                    trailing: Icon(Icons.edit),
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => SecretScreen(
                            secret: snapshot.data[index],
                          ),
                        ),
                      );
                    },
                  );
                },
                separatorBuilder: (context, index) {
                  return Divider();
                },
              ),
            );
          },
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecretScreen()),
            );
          },
        ),
      ),
    );
  }
}

对于secret.dart

class SecretScreen extends StatefulWidget {
  final Secret secret;

  SecretScreen({this.secret});

  @override
  _SecretScreenState createState() => _SecretScreenState();
}

class _SecretScreenState extends State<SecretScreen> {
  // some codes...

  @override
  void initState() {
    final secretProvider = Provider.of<SecretProvider>(context, listen: false);

    // some codes...

    super.initState();
  }

  @override
  void dispose() {
    // some codes...

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final secretProvider = Provider.of<SecretProvider>(context);

    return Scaffold(
      // some codes...
    );
  }
}

这些代码工作得很好,但后来由于一些类实例生命周期问题,我决定将 ChangeNotifierProvidermain.dart 移动到 home.dart。新代码如下:

对于main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<User>.value(value: AuthService().user),
      ],
      child: MaterialApp(
        title: 'My Secrets',
        home: AuthScreen(),
      ),
    );
  }
}

对于home.dart

class HomeScreen extends StatelessWidget {
  final AuthService _auth = AuthService();

  @override
  Widget build(BuildContext context) {
    // var secretProvider = Provider.of<SecretProvider>(context);

    return ChangeNotifierProvider(
      create: (context) => SecretProvider(),
      child: Consumer<SecretProvider>(
        builder: (context, secretProvider, child) {
          return Scaffold(
            appBar: AppBar(
              // some codes...
            ),
            body: StreamBuilder<List<Secret>>(
              stream: secretProvider.secrets,
              // stream: SecretProvider().secrets,
              builder: (context, snapshot) {
                return Padding(
                  padding: EdgeInsets.symmetric(vertical: 15.0),
                  child: ListView.separated(
                    // return 0 if snapshot.data is null
                    itemCount: snapshot.data?.length ?? 0,
                    itemBuilder: (context, index) {
                      return ListTile(
                        leading: Icon(Icons.web),
                        title: Text(snapshot.data[index].title),
                        trailing: Icon(Icons.edit),
                        onTap: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => SecretScreen(
                                secret: snapshot.data[index],
                              ),
                            ),
                          );
                        },
                      );
                    },
                    separatorBuilder: (context, index) {
                      return Divider();
                    },
                  ),
                );
              },
            ),
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => SecretScreen()),
                );
              },
            ),
          );
        },
      ),
    );
  }
}

基本上,我只是将ChangeNotifierProvider 移动到home.dart 并使用Consumer 传递上下文,但是这一次,每当我导航到secret 屏幕时,它都会提示我如下错误:

Could not find the correct Provider<SecretProvider> above this SecretScreen Widget

This likely happens because you used a `BuildContext` that does not include the provider
of your choice.

这个BuildContext 真的很烦我。即使我的ChangeNotifierProvider 比以前低一级,SecretScreen 小部件仍然应该知道从HomeScreen 传递的SecretProvider,因为它仍然是HomeScreen 的孩子,据我所知,上下文应包含SecretProvider

【问题讨论】:

    标签: flutter


    【解决方案1】:

    您收到此错误是因为您的SecretProvider 实例是HomeScreen 的一部分,而HomeScreen 不是SecretScreen 的父级。

    按顺序,当您推送一个新页面时,这个新页面不是前一个页面的后代,因此您无法使用.of(context) 方法访问继承的对象。

    这里用一个代表小部件树的模式来解释这种情况:

    1. 在 MaterialApp(导航器)之上有一个 Provider:

      Provider
        MaterialApp
          HomeScreen -> push SecretScreen
          SecretScreen -> Here we can acces the Provider by calling Provider.of(context) because the context can access to its ancestors 
      
    2. 使用在 HomeScreen 中创建的提供程序:

      MaterialApp
        HomeScreen -> push SecretScreen
          Provider -> The provider is part of HomeScreen
        SecretScreen -> The context can't access to the Provider because it's not part of its ancestors
      

    我希望我的回答很清楚,并能帮助你理解发生了什么;)

    【讨论】:

    • 很好的解释,你能否在社区的答案中写一些代码,谢谢
    • @jitsm555 完成 :) 告诉我这是否增加了清晰度;)
    • 感谢您的详细解释。我一直认为推送的页面是前一个页面的子页面。但是,如果我确实想在不将提供程序移动到 HomeScreen 的某种父级的情况下使此代码正常工作,有没有办法实现这一点?
    • 你首先要做的就是要做的事情。提供程序旨在像这样使用。您仍然可以将对象传递到您的页面,但您失去了提供者的好处
    • 我明白了。非常感谢。
    猜你喜欢
    • 2019-12-23
    • 2021-04-04
    • 2021-01-06
    • 2019-12-05
    • 2023-01-26
    • 2021-06-09
    • 2021-08-09
    相关资源
    最近更新 更多