【问题标题】:How to open new MaterialPageRoute as a child in widget tree in Flutter如何在 Flutter 的小部件树中将新的 MaterialPageRoute 作为子项打开
【发布时间】:2020-08-03 10:51:21
【问题描述】:

在下面的示例中,当我推送新的 MaterialPageRoute 时,它​​会在 Flutter 小部件树中的 Home 小部件的同一级别上创建。我希望它作为小部件 Home 的子级,因此 Home 将是 Child 小部件的父级。

这是一个完整的代码:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Home(),
  ));
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Center(
            child: Text("This is home"),
          ),
          RaisedButton(
            child: Text("Open child view"),
            onPressed: () {
              Navigator.of(context)
                  .push(MaterialPageRoute(builder: (context) => Child()));
            },
          )
        ],
      ),
    );
  }
}

class Child extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
      child: Text("Child view"),
    ));
  }
}

这是小部件树的样子

我想实现这一点,因为我想在 Home 小部件中初始化 ScopedModel 并让它在我创建的每个新 MaterialPageRoute 中都可用。

【问题讨论】:

    标签: flutter dart tree navigation widget


    【解决方案1】:

    我使用嵌套导航器解决了我的问题。

    示例如下:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MaterialApp(
        home: Home(),
      ));
    }
    
    var homeNavigatorKey = GlobalKey<NavigatorState>();
    
    class Home extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Navigator(
            key: homeNavigatorKey,
            onGenerateRoute: (settings) {
              /*dirty code for illustration purposes only*/
              if (settings.name == '/child') {
                return MaterialPageRoute(builder: (context) => Child());
              } else {
                return MaterialPageRoute(
                    builder: (context) => Column(
                          children: <Widget>[
                            Center(
                              child: Text("This is home"),
                            ),
                            RaisedButton(
                              child: Text("Open child view"),
                              onPressed: () {
                                homeNavigatorKey.currentState.pushNamed('/child');
                              },
                            )
                          ],
                        ));
              }
            },
          ),
        );
      }
    }
    
    class Child extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Container(
          child: RaisedButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text("Back to home"),
          ),
        ));
      }
    }
    
    

    这棵树是这样的

    并允许将数据从 Home 传递给每个孩子。

    如果您有任何更简单的解决方案,请随时发表评论或发布答案。

    【讨论】:

      【解决方案2】:

      Navigator.of(context).push() 将用新屏幕替换您的Home 屏幕(同时将Home 屏幕保留在内存中,以便您可以返回到它)。它们在小部件树中处于同一级别,嵌套它们的唯一方法是使用嵌套的Navigators 我认为。

      但是,考虑到您想要实现的目标:为什么不将 ScopedModel 向上初始化一级并在小部件树的根目录中提供呢?这样您就可以在Home 屏幕和Child 屏幕中访问您的模型。

      下面是你如何做到这一点,我添加了一个简单的 ScopedModel 并且能够在Child 屏幕内访问它的textproperty。

      import 'package:flutter/material.dart';
      import 'package:scoped_model/scoped_model.dart';
      import 'package:tryout/model.dart';
      
      void main() {
        runApp(
          ScopedModel<TestModel>(
            model: TestModel(),
            child: MaterialApp(
              home: Home(),
            ),
          )
        );
      }
      
      class Home extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: Column(
              children: <Widget>[
                Center(
                  child: Text("This is home"),
                ),
                RaisedButton(
                  child: Text("Open child view"),
                  onPressed: () {
                    Navigator.of(context)
                        .push(MaterialPageRoute(builder: (context) => Child()));
                  },
                )
              ],
            ),
          );
        }
      }
      
      class Child extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return Scaffold(
              body: Container(
            child: Text("${ScopedModel.of<TestModel>(context).text}"),
          ));
        }
      }
      

      这是TestModel 类:

      import 'package:scoped_model/scoped_model.dart';
      
      class TestModel extends Model {
        String _text = "Test to see if this works!";
        String get text => _text;
      }
      

      【讨论】:

      • 感谢您的回答。我知道这种可能性,这就是我目前实现它的方式,但是我有一个具有多个不同模型的复杂应用程序,我希望在重新创建特定小部件时重新创建其中的一些(此示例中的 Home 代表此小部件)。我想知道是否有比实现嵌套导航器更简单的方法。
      • 啊,好的。也许您可以通过在执行 Navigators.push() 之前首先处理已经初始化的模型来做到这一点?
      • 此时我在这些模型中有一个 dispose() 方法,但我不想手动清理模型,因为每次新程序员向模型添加新变量/功能时,他都必须记得在 dispose() 方法中处理它,我不喜欢它。您认为有可能为已经初始化的模型分配一个全新的对象实例吗?我的意思是ScopedModel.of&lt;TestModel&gt;(context) = TestModel()?
      • 我不知道在您的小部件树中重新创建实例的可能性。通过 Child 小部件的构造函数传递一个新实例,而不是让 InheritedWidget 为您工作,我认为您不应该这样做,因为它会创建模型的多个实例和状态,这与包是关于什么的。
      • 但是,为什么首先需要一个新实例?重建模型应该解决什么问题?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-18
      • 2019-09-14
      相关资源
      最近更新 更多