注册

为什么我建议Flutter中通过构造参数给页面传递信息

哈喽,我是老刘


前段时间有人问我这个问题碰到没有:
Flutter - 升级3.19之后页面多次rebuild?

说实话我们没有碰到这个问题


我先来简单解释一下这个问题,本质上是因为使用了 InheritedWidget


通过InheritedWidget向子树传递数据


InheritedWidget可以向其子树中的所有Widget提供数据。这使得无关的Widget能方便地获取同一个InheritedWidget提供的数据,实现Widget树中不直接相关Widget之间的数据共享。

Flutter SDK 中正是通过 InheritedWidget 来共享应用主题(Theme)和 Locale(当前语言环境)信息的。

其使用方法如下

实现一个InheritedWidget


class MyInheritedWidget extends InheritedWidget {  // 继承InheritedWidget
final int data;
MyInheritedWidget({required this.data, required Widget child}) : super(child: child);
@override
// 这个方法定义了当数据发送变化时是否通知子树中的子Widget。
// 它返回一个布尔值,true表示通知子Widget,false表示不通知。
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.data != data;
}
// 子Widget可以通过调用MyInheritedWidget.of()静态方法来获取MyInheritedWidget实例,并获取其提供的数据。
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType()!;
}
}


获取InheritedWidget中的数据


class MyText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'${MyInheritedWidget.of(context).data}',
style: Theme.of(context).textTheme.headline4,
);
}
}


当InheritedWidget中的数据发生变化时,就会通知所有通过InheritedWidget.of()方法注册了关注数据变化的成员。

这时这些子树中的组件就会重绘。


其实前面文章中的问题就是当你使用ModalRoute.of(context)方法获取页面路由的参数时,其实也就是向一个全局级别的InheritedWidget节点注册了关注其变化。

而当这个全局的路由节点的行为发生变化后(页面退栈通知数据变化),就会出现原先没有的重绘现象出现了。


为什么我们的代码没有这个问题


我们在传递页面参数时其实是没有使用ModalRoute.of(context)的方式获取页面参数的。

我们使用的是页面类的构造参数。

举个例子,比如打开一个商品详情页,需要传递商品id作为页面参数。

代码如下


class ProductDetailPage extends StatefulWidget {
final String productId;

const ProductDetailPage({required this.productId});

@override
_ProductDetailPageState createState() => _ProductDetailPageState();
}

class _ProductDetailPageState extends State<ProductDetailPage> {
@override
void initState() {
super.initState();
// 使用productId获取商品详情
}

@override
Widget build(BuildContext context) {
// 根据productId构建UI
return Scaffold(
// ...
);
}
}


定义路由可以使用动态生成路由的方式:


MaterialApp(
onGenerateRoute: (RouteSettings settings) {
// 通过settings.name可以获取传入的路由名称,并据此返回不同的路由
final productId = settings.arguments['id'] as String;
return MaterialPageRoute(
builder: (context) => ProductDetailPage(productId: productId),
);
}
)


那为什么我们要选择这种方式传递页面参数,而不是ModalRoute.of(context)的方式呢?

这其实是一种本能,一种下意识的行为。


两个思维习惯


1、减少不可控因素


老刘写了十多年的代码了,光Flutter就写了快6年。

这么多年的实战形成的习惯就是对不可控的外部依赖心怀警惕。

InheritedWidget就是一种很典型的场景。

如果是自己写的InheritedWidget还好,但如果是外部的,比如系统SDK的。

那么你怎么保证它通知的数据变化时你想要的呢?

这次的问题不就是很典型的例子吗。

远隔千里之外的人修改了几行代码,就对你的App的行为造成了影响。


2、模块化思维


也许你觉得写一个页面就是一个页面。

但是在我看来,很有可能某一天它就是某个页面的一个组件。

假设有一天你的产品要适配pad端

那么很有可能商品列表页和商品详情页会合并成一个页面:左边是列表右边是详情。

这时候原先独立的详情页就是页面的一个组件了。


image.png


这时候是不是通过构造参数传递商品id会合理很多?


总结


我们从一个实际的bug出发,解释了为什么建议大家通过构造参数进行页面传参。

进而引出了关于日常编码中的一些很具体的思维习惯。

总之很多时候最简单直接的用法可能也是最好的选择。


作者:程序员老刘
来源:juejin.cn/post/7394823316585168933

0 个评论

要回复文章请先登录注册