在新路径中无法访问InheritedWidget

时间:2019-03-20 20:28:48

标签: dart flutter

我是Flutter的新手,对InheritedWidget如何使用路线感到困惑。我正在将SQLite数据库与sqflite库一起使用。基本上,我想要实现的是,在启动应用程序时,我希望所有不需要数据库的小部件立即显示。例如,我的Scaffold的bottomNavigationBar不需要数据库,但是主体需要数据库。因此,我想立即显示bottomNavigationBar,并在正文中显示CircularProgressIndicator。打开数据库后,我希望正文显示从数据库加载的内容。

因此,为了实现这一目标,我在FutureBuilder之前使用Scaffold打开数据库。当Future未完成时,我将null传递给抽屉,将CircularProgressBar传递给主体,并将bottomNavigationBar照常传递。 Future完成后,我用自己的HomePage(称为InheritedWidget)包装了抽屉和主体(称为DataAccessor)。这似乎可行,因为我可以在DataAccessor小部件中访问HomePage。但是,当我在抽屉中使用Navigator导航到SettingsScreen时,我的DataAccessor无法访问,并返回null。

这是一些示例代码,不使用数据库,而是延迟5秒Future

import 'package:flutter/material.dart';

void main() => runApp(App());

class App extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FutureBuilder(
        future: Future.delayed(Duration(seconds: 5)),
        builder: (context, snapshot) {
          Widget drawer;
          Widget body;
          if (snapshot.connectionState == ConnectionState.done) {
            drawer = DataAccessor(
              child: Drawer(
                child: ListView(
                  children: <Widget>[
                    ListTile(
                      title: Text("Settings"),
                      onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SettingsScreen()))
                    )
                  ]
                )
              )
            );
            body = DataAccessor(child: HomePage());  
          }
          else {
            drawer = null;
            body = Center(child: CircularProgressIndicator());
          }
          return Scaffold(
            drawer: drawer,
            body: body,
            bottomNavigationBar: BottomNavigationBar(
              items: <BottomNavigationBarItem>[
                BottomNavigationBarItem(icon: Container(), title: Text("One")),
                BottomNavigationBarItem(icon: Container(), title: Text("Two"))
              ]
            )
          );
        }
      )
    );
  }
}

class HomePage extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        DataAccessor dataAccessor = DataAccessor.of(context); //dataAccessor IS NOT null here
        print("HomePage: ${dataAccessor == null}");
        return Text("HomePage");
    }
}

class SettingsScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        DataAccessor dataAccessor = DataAccessor.of(context); //dataAccessor IS null here
        print("SettingsScreen: ${dataAccessor == null}");
        return Text("SettingsScreen");
    }
}

class DataAccessor extends InheritedWidget {

    DataAccessor({Key key, Widget child}) : super(key: key, child: child);

    @override
    bool updateShouldNotify(InheritedWidget oldWidget) => false;

    static DataAccessor of(BuildContext context) => context.inheritFromWidgetOfExactType(DataAccessor);

}

我可能做错了事情。不确定将小部件存储在变量中的做法有多好。还是两次使用相同的InheritedWidget?我还尝试用我的Scaffold包装整个DataAccessor(并且在加载数据库时将其设置为null),但是问题仍然存在,我无法获取我的DataAccessor在我的SettingsScreen中。

我已经读到一个可能的解决方案是将我的InheritedWidget放在MaterialApp之前,但是我不想诉诸于此。我不希望在打开数据库时显示一个新的屏幕,我希望不需要显示数据库的窗口小部件。这应该是可能的。

谢谢!

1 个答案:

答案 0 :(得分:1)

最后一段中的解决方案是您需要的。 MaterialApp包含管理路由的Navigator,因此,您的所有路由都可以访问必须位于InheritedWidget上方(即高于)的同一NavigatorMaterialApp

使用Remi's method,您将得到一个像这样的小部件树:

  • MyApp(静态.of()返回MyAppState
  • MyAppState,其build返回_MyInherited(child: MaterialApp(...)),并且其initState开始加载数据库,并在加载时调用setState

建立首页时,您可以通过MyAppState访问.of,因此可以确定数据库是否已加载。如果还没有,则只需构建数据库独立的小部件;如果有,则构建所有小部件。