简体   繁体   English

要从 initState() 异步初始化提供程序但得到“找不到正确的提供程序”

[英]Provider to be initialized asynchronously from `initState()` but get `could not find the correct Provider`

I develop an ad app, with a message button on the detailed view.我开发了一个广告应用程序,在详细视图上有一个消息按钮。

When the user tap on it, the chats view (stateful widget) is pushed to the screen.当用户点击它时,聊天视图(有状态的小部件)被推送到屏幕上。

The initState() is there to call the asyncInitMessages() which asynchronously fetches the chats and related message from the distant database. initState() 用于调用asyncInitMessages() ,后者从远程数据库异步获取聊天和相关消息。 The asyncInitMessages() belongs to the Chats class which extends ChangeNotifier . asyncInitMessages()属于扩展ChangeNotifierChats class。


/// A chat conversation
class Chats extends ChangeNotifier {
  /// Internal, private state of the chat.


  void asyncInitMessages(
      {required ClassifiedAd ad,
      required String watchingUserId,
      required bool isOwner}) async {

     // blah blah
  }
}

The ClassifiedAdMessagesView stateful widget class implementation is as follows (snipet): ClassifiedAdMessagesView状态小部件 class 实现如下(片段):


  @override
  void initState() {
    
    // == Fetch conversation and messages 
    asyncInitMessages();

  }


void asyncInitMessages() async {
    // === Update all messages
    try {
      Provider.of<Chats>(context, listen: false).asyncInitMessages(
          ad: widget.ad,
          watchingUserId: widget.watchingUser!.uid,
          isOwner: _isOwner);
    } catch (e) {
      if (mounted) {
        setState(() {
          _error = "$e";
          _ready = true;
        });
      }
    }
  }


  @override
  Widget build(BuildContext context) {
    // <<<<<<<<<<< The exception fires at the Consumer line right below
    return Consumer<Chats>(builder: (context, chats, child) {
      return Scaffold(
        // ... blah blah 

Finally, when running ll that, I got the exception in the build at the Consumer line:最后,在运行 ll 时,我在Consumer行的构建中得到了异常:

could not find the correct Provider<chats>找不到正确的提供者<聊天>

Help greatly appreciated.非常感谢帮助。

[UPDATED] [更新]

Here is the main (very far up from the messages screen)这是主要的(离消息屏幕很远)



Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  //if (Firebase.apps.isEmpty) {
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  // } else {
  //   Firebase.app(); // if already initialized, use that one
  // }
  if (USE_DATABASE_EMULATOR) {
    FirebaseDatabase.instance.useDatabaseEmulator(emulatorHost, emulatorPort);
  }

  runApp(RootRestorationScope(
      restorationId: 'root',
      child: ChangeNotifierProvider(
          create: (context) => StateModel(),
          child: const App())));
}


class App extends StatefulWidget {
  const App({super.key});
  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {

  @override
  Widget build(BuildContext context) {
    return PersistedAppState(
        storage: const JsonFileStorage(),
        child: MultiProvider(
            providers: [
              ChangeNotifierProvider<ThemeModel>.value(value: _themeModel),
              //ChangeNotifierProvider<AuthModel>.value(value: _auth),
            ],
            child: Consumer<ThemeModel>(
                builder: (context, themeModel, child) => MaterialApp(
                  // blah blah

    }
  }
}

And the component just on top of the而组件就在



/// Classified ad detail view
class ClassifiedAdDetailView extends StatefulWidget {
  final User? watchingUser;
  final ClassifiedAd ad;

  const ClassifiedAdDetailView(
      {Key? key, required this.watchingUser, required this.ad})
      : super(key: key);

  @override
  State<ClassifiedAdDetailView> createState() => _ClassifiedAdDetailViewState();
}

class _ClassifiedAdDetailViewState extends State<ClassifiedAdDetailView>
    with TickerProviderStateMixin {

 @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
        create: (context) => Chats(),
        builder: ((context, child) => Scaffold(

// blah blah

          ElevatedButton(
            onPressed: () => Navigator.of(context).push(MaterialPageRoute(
                                builder: (context) => ClassifiedAdMessagesView(
                                    ad: ad,
                                    watchingUser: widget.watchingUser)));
                          }),


Providers must be located in the widget tree above the widget where you want to use them with Consumer or Provider.of .提供者必须位于您要将它们与ConsumerProvider.of一起使用的小部件上方的小部件树中。 When you push a new route with Navigator , it won't be add the pushed route below the widget from where you push, it will add it at the same level where home of MaterialApp is located.当您使用Navigator推送新路线时,它不会将推送的路线添加到您推送的小部件下方,它会将其添加到MaterialApp home的同一级别。

(I think the error message you get also states that you can't access the providers between routes.) (我认为您收到的错误消息还指出您无法在路由之间访问提供程序。)

In general the tree will look like this if you push some routes (check it with the Flutter Widget Inspector):一般来说,如果你推送一些路由,树将看起来像这样(用 Flutter Widget Inspector 检查它):

  • MaterialApp材料应用
    • home
      • widget1部件 1
      • widget2小部件2
        • widget21小部件21
        • widget22小部件22
    • page1第1页
      • widget1部件 1
      • widget2小部件2
    • page2第2页
    • page3第3页

In your code you create the provider in ClassifiedAdDetailView and then push在您的代码中,您在ClassifiedAdDetailView中创建提供程序,然后推送
ClassifiedAdMessagesView from this in the onPressed method. ClassifiedAdMessagesView中的onPressed方法由此而来。 You won't be access this provider from ClassifiedAdMessagesView because the tree will be like (simplified):您不会从ClassifiedAdMessagesView访问此提供程序,因为树将类似于(简化):

  • MaterialApp材料应用
    • home
    • ClassifiedAdDetailView分类广告详情视图
    • ClassifiedAdMessagesView分类广告消息视图

The solution is to "lift the state up" and place the provider above every widget from where you need to access it.解决方案是“将 state 向上提起”,并将提供程序置于您需要访问它的每个小部件上方。 It can be a part of your existing Multiprovider above MaterialApp but if it is too far, you need to find a proper place that is above both ClassifiedAdDetailView and ClassifiedAdMessagesView .它可以是MaterialApp上方现有Multiprovider的一部分,但如果距离太远,您需要在ClassifiedAdDetailViewClassifiedAdMessagesView上方找到一个合适的位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM