使用继承的小部件将数据传递到多个屏幕

问题描述

关于 InheritedWidget 有一些我不明白的困惑。
我在 stackoverflow搜索并阅读了一些关于 InheritedWidget 的 QA,但仍有一些我不明白的地方。
首先,让我们创建一个场景。
这是我的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>();
  }
}

这是包含 MyHomePageMyInheritedWidgetMyInheritedWidget 有两个子项: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 中的 name 字段和另一个导航到 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'),)
      ],);
  }
}

Page1Page2 只有一个文本小部件,用于显示 MyInheritedWidget 中的 name 字段。

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'),);
  }
}

在这种情况下,MyInheritedWidgetname 字段无法通过 Page1Page2 访问,但可以在 WidgetA 中访问.

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

解决方法

正如@pskink 所说,MyHomePage 推送 Page1,它是 Navigator 的后代,它在 MaterialApp 之下,而不是 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,_) {
        log.v('building MaterialApp with isAuth=${authService.isAuth}');
        return MaterialApp(

以下示例使用多个 Navigators 来限定 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'),)
      ],);
  }
}