【问题标题】:multi Provider doesn't work in material app in flutter?多提供者在颤动的材料应用程序中不起作用?
【发布时间】:2019-12-09 18:06:31
【问题描述】:

我创建了一个应用程序并使用了 MultiProvider,但是当我在 MaterialApp 中使用它时它不起作用

我想用它来改变应用主题颜色但是

它给了我一个错误:

*注意:当我在任何其他屏幕上使用帖子提供程序时,它都可以工作。

我的代码:

import 'package:blog_app/provider/posts.dart';
import 'package:blog_app/provider/settings.dart';
import 'package:blog_app/screens/splash_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<Posts>(
          builder: (context) => Posts(),
        ),
        ChangeNotifierProvider<Settings>(
          builder: (context) => Settings(),
        ),
      ],
      child: MaterialApp(
        darkTheme: Provider.of<Settings>(context).darkModeEnabled ? ThemeData.dark() : ThemeData.light(),
        debugShowCheckedModeBanner: false,
        title: 'Blogy',
        theme: ThemeData(
          primaryColor: Colors.deepPurple[900],
          cursorColor: Colors.deepPurple[900],
          accentColor: Colors.deepPurple[900],
          fontFamily: 'Ubuntu',
        ),
        home: SplashScreen(),
      ),
    );
  }
}

错误:-

I/flutter ( 9316): The following ProviderNotFoundError was thrown building MyApp(dirty):
I/flutter ( 9316): Error: Could not find the correct Provider<Settings> above this MyApp Widget
I/flutter ( 9316):
I/flutter ( 9316): To fix, please:
I/flutter ( 9316):
I/flutter ( 9316):   * Ensure the Provider<Settings> is an ancestor to this MyApp Widget
I/flutter ( 9316):   * Provide types to Provider<Settings>
I/flutter ( 9316):   * Provide types to Consumer<Settings>
I/flutter ( 9316):   * Provide types to Provider.of<Settings>()

【问题讨论】:

    标签: flutter dart provider


    【解决方案1】:

    发生此错误是因为您在同一 build 方法中创建您的提供者和消费者。这导致它们具有相同的context,但尚未注册任何Provider&lt;Settings&gt;Provider.of&lt;Settings&gt;(context) 正在尝试在小部件树中找到上面的 Provider&lt;Settings&gt;,但那里没有这样的提供程序。

    使用Consumer 似乎是一种有效的解决方法,但在每次更改时重新创建整个 MaterialApp 可能会非常繁重。

    我建议改为为提供者和应用根目录创建单独的小部件:

    class AppProviders extends StatelessWidget {
      final Widget child;
    
      AppProviders({this.child});
    
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider<Posts>(
              builder: (context) => Posts(),
            ),
            ChangeNotifierProvider<Settings>(
              builder: (context) => Settings(),
            ),
          ],
          child: child;
        );
      }
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          darkTheme: Provider.of<Settings>(context).darkModeEnabled ? ThemeData.dark() : 
          ThemeData.light(),
          debugShowCheckedModeBanner: false,
          title: 'Blogy',
          theme: ThemeData(
            primaryColor: Colors.deepPurple[900],
            cursorColor: Colors.deepPurple[900],
            accentColor: Colors.deepPurple[900],
            fontFamily: 'Ubuntu',
          ),
          home: SplashScreen(),
        );
      }
    }
    

    然后将MyApp 小部件包装在AppProviders 中的runApp 函数内。

    void main() {
      runApp(
        AppProviders(
          child: MyApp(),
        )
      );
    }
    

    这可确保提供程序在您的根应用小部件上方注册,并且它们在其context 中可见。

    或者,您可以声明三个小部件,其中第三个只是AppProviders(child: MyApp()),然后在runApp 中调用它。请注意,在MyAppbuild 方法中创建AppProviders 会导致和之前一样的错误,所以不要这样尝试。

    【讨论】:

      【解决方案2】:

      以下测试代码正常运行,您可以使用您的案例进行测试
      使用Consumer 包裹MaterialApp

      代码sn-p

      return MultiProvider(
            providers: [
              ChangeNotifierProvider<Posts>(
                create: (context) => Posts(),
              ),
              ChangeNotifierProvider<Settings>(
                create: (context) => Settings(darkModeEnabled: true),
              ),
            ],
            child: Consumer<Settings>(builder: (_, settings, child) {
              return MaterialApp(
                darkTheme:
                    settings.darkModeEnabled ? ThemeData.dark() : ThemeData.light(),
                debugShowCheckedModeBanner: false,
                title: 'Blogy',
                theme: ThemeData(
                  primaryColor: Colors.deepPurple[900],
                  cursorColor: Colors.deepPurple[900],
                  accentColor: Colors.deepPurple[900],
                  fontFamily: 'Ubuntu',
                ),
                home: SplashScreen(),
              );
            }),
          );
      

      完整的测试代码

      import 'package:flutter/material.dart';
      import 'package:provider/provider.dart';
      
      void main() => runApp(MyApp());
      
      class Posts extends ChangeNotifier {}
      
      class Settings extends ChangeNotifier {
        bool darkModeEnabled;
        Settings({this.darkModeEnabled});
      }
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MultiProvider(
            providers: [
              ChangeNotifierProvider<Posts>(
                create: (context) => Posts(),
              ),
              ChangeNotifierProvider<Settings>(
                create: (context) => Settings(darkModeEnabled: true),
              ),
            ],
            child: Consumer<Settings>(builder: (_, settings, child) {
              return MaterialApp(
                darkTheme:
                    settings.darkModeEnabled ? ThemeData.dark() : ThemeData.light(),
                debugShowCheckedModeBanner: false,
                title: 'Blogy',
                theme: ThemeData(
                  primaryColor: Colors.deepPurple[900],
                  cursorColor: Colors.deepPurple[900],
                  accentColor: Colors.deepPurple[900],
                  fontFamily: 'Ubuntu',
                ),
                home: SplashScreen(),
              );
            }),
          );
        }
      }
      
      class SplashScreen extends StatefulWidget {
        SplashScreen({Key key}) : super(key: key);
      
        //final String title;
      
        @override
        _SplashScreenState createState() => _SplashScreenState();
      }
      
      class _SplashScreenState extends State<SplashScreen> {
        int _counter = 0;
      
        void _incrementCounter() {
          setState(() {
            _counter++;
          });
        }
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(
              title: Text("test"),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'You have pushed the button this many times:',
                  ),
                  Text(
                    '$_counter',
                    style: Theme.of(context).textTheme.display1,
                  ),
                ],
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: Icon(Icons.add),
            ),
          );
        }
      }
      

      【讨论】:

      • 如果我们有一个 AuthProvider 怎么样?如何处理消费者
      • 如果我们想从 Material App 的列表中消费多个提供者,我们必须嵌套消费者吗?有没有比嵌套更好的解决方案?
      • @maheshmnj,你不需要嵌套消费者,你可以使用 Consumer2 为两个提供者,Consumer3 为 3 个提供者等等。 pub.dev/documentation/provider/latest/provider/…
      猜你喜欢
      • 2019-10-30
      • 2019-01-28
      • 2017-06-24
      • 1970-01-01
      • 2017-01-05
      • 2019-04-17
      • 2022-08-19
      • 2019-07-29
      • 2020-04-20
      相关资源
      最近更新 更多