首页 > 解决方案 > 使用继承的小部件将数据传递到多个屏幕

问题描述

有一些InheritedWidget我不明白的困惑。
我已经搜索并阅读了一些关于 stackoverflow 的 QA InheritedWidget仍有一些我不明白的地方。
首先,让我们创建一个场景。
这是我的InheritedWidget

class MyInheritedWidget extends InheritedWidget {
  final String name;
  MyInheritedWidget({
    @required this.name,
    @required Widget child,
    Key key,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) =>
      oldWidget.name != this.name;

  static MyInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  }
}

这就是MyHomePage包含MyInheritedWidget. MyInheritedWidget有两个孩子:WidgetA和一个导航到另一个屏幕的按钮,在这种情况下Page1

class MyHomePage extends StatefulWidget {
  @override
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: MyInheritedWidget(
          name: 'Name',
          child: Column(
            children: [
              WidgetA(),
              TextButton(
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => Page1(),
                    ),
                  );
                },
                child: Text('Go to page 1'),
              )
            ],
          ),
        ),
      ),
    );
  }
}

里面WidgetA有一个文本小部件,显示来自的名称字段MyInheritedWidget和导航到的另一个按钮Page2

class WidgetA extends StatefulWidget {
  @override
  _WidgetAState createState() => _WidgetAState();
}

class _WidgetAState extends State<WidgetA> {
  @override
  Widget build(BuildContext context) {
    final myInheritedWidget = MyInheritedWidget.of(context);
    return Column(
      children: [
        Text(myInheritedWidget.name),
        TextButton(
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => Page2(),
              ),
            );
          },
          child: Text('Go to page 2'),
        )
      ],
    );
  }
}

Page1每个都只有一个文本小部件,Page2显示来自.MyInheritedWidget

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final myInheritedWidget = MyInheritedWidget.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 1'),
      ),
      body: Text(myInheritedWidget.name),
    );
  }
}
class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final myInheritedWidget = MyInheritedWidget.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 2'),
      ),
      body: Text(myInheritedWidget.name),
    );
  }
}

在这种情况下,name字段MyInheritedWidget是不可访问的 formPage1Page2,但可以在 中访问WidgetA

现在让我们来看看这个问题:
据说InheritedWidget可以从它的所有后代访问。子孙是什么意思?
MyHomePage,我知道WidgetA是一个后裔MyInheritedWidget。但是,Page1也是?的后裔MyInheritedWidget
如果答案是否定的,我怎样才能成为Page1的后代MyInheritedWidget
我需要再把它包在里面MyInheritedWidget吗?
如果有这样的导航链怎么办:Page1 -> Page2 -> Page3 ... Page10并且我想MyInheritedWidgetPage10中访问,我必须将每个页面都包装在里面MyInheritedWidget吗?

标签: flutterinherited-widget

解决方案


正如@pskink 所说,MyHomePagepushesPage1是 的后代Navigator,它是 under MaterialApp, not MyInheritedWidget。最简单的解决方案是在MyInheritedWidget上面创建MaterialApp. 这是我的代码(使用ChangeNotifierProvider代替MyInheritedWidget)。

void main() {
  setupLocator();
  runApp(DevConnectorApp());
}

class DevConnectorApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final log = getLogger('DevConnectorApp');
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<AuthService>(
          create: (ctx) => AuthService(),
        ),
        ChangeNotifierProxyProvider<AuthService, ProfileService>(
          create: (ctx) => ProfileService(),
          update: (ctx, authService, profileService) =>
              profileService..updateAuth(authService),
        ),
      ],
      child: Consumer<AuthService>(builder: (ctx, authService, _) {
        log.v('building MaterialApp with isAuth=${authService.isAuth}');
        return MaterialApp(

这是一个使用 multipleNavigators来确定InheritedWidget. 该小部件为您的示例中的屏幕ContextWidget创建一个InheritedWidget和一个Navigator并具有子小部件。

class InheritedWidgetTest extends StatefulWidget {
  @override
  State createState() => new InheritedWidgetTestState();
}

class InheritedWidgetTestState extends State<InheritedWidgetTest> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: Column(
          children: [
            ContextWidget('First'),
            ContextWidget('Second'),
          ],
        ),
      ),
    );
  }
}

class ContextWidget extends StatelessWidget {
  Navigator _getNavigator(BuildContext context, Widget child) {
    return new Navigator(
      onGenerateRoute: (RouteSettings settings) {
        return new MaterialPageRoute(builder: (context) {
          return child;
        });
      },
    );
  }

  final name;
  ContextWidget(this.name);
  @override
  Widget build(BuildContext context) {
    return MyInheritedWidget(
      name: this.name,
      child: Expanded(
        child: _getNavigator(
          context,
          PageWidget(),
        ),
      ),
    );
  }
}

class PageWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        WidgetA(),
        TextButton(
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => Page1(),
              ),
            );
          },
          child: Text('Go to page 1'),
        )
      ],
    );
  }
}

推荐阅读