如何在 Flutter 的小部件树中将新的 MaterialPageRoute 作为子项打开
Posted
技术标签:
【中文标题】如何在 Flutter 的小部件树中将新的 MaterialPageRoute 作为子项打开【英文标题】:How to open new MaterialPageRoute as a child in widget tree in Flutter 【发布时间】: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 中都可用。
【问题讨论】:
【参考方案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
屏幕内访问它的text
property。
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<TestModel>(context) = TestModel()
?
我不知道在您的小部件树中重新创建实例的可能性。通过 Child 小部件的构造函数传递一个新实例,而不是让 InheritedWidget
为您工作,我认为您不应该这样做,因为它会创建模型的多个实例和状态,这与包是关于什么的。
但是,为什么首先需要一个新实例?重建模型应该解决什么问题?以上是关于如何在 Flutter 的小部件树中将新的 MaterialPageRoute 作为子项打开的主要内容,如果未能解决你的问题,请参考以下文章
Flutter/Dart:我的小部件树中可以有多个带有状态的小部件吗?
Flutter:检测任何在屏幕上不可见但在小部件树中的小部件的重建