Flutter
是由众多widget
构成的UI
框架,之前的文章我们在不同的widget
之间传递数据是通过构造函数传参的方式传递。如果嵌套的widget
过多,这么写不免有些麻烦且层级复杂。所以Flutter
还提供了其他方案来实现跨 widget 间数据的传递
,下面就介绍InheritedWidget、Notification 和 EventBus
这三种方案。
InheritedWidget
是widget
的基类,可有效地向下传播信息。
可以理解为子 widget
可以在任何位置获取继承了InheritedWidget
的父 widget
中的数据。
示例中通过
构造函数
和继承InheritedWidget
两种方式实现父 widget :FrogColor
向子 widget:FColor
传值。
FrogColor
继承InheritedWidget
,并在构造方法中传递数据和方法:model 和 doSomeThing
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.model,
@required this.doSomeThing,
@required Widget child,
}) : assert(model != null),
assert(child != null),
super(key: key, child: child);
// 直接从 _FirstPageState 中的 State 获取数据,这里可指定为泛型
final _FirstPageState model;
// 方法
final Function() doSomeThing;
// 获取实例
static FrogColor of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
// 判断是否需要更新
@override
bool updateShouldNotify(FrogColor old) => model != old.model;
}
FrogColor
包裹FColor
子widget
,同时传递data和 doSomeThing 方法
。由于上面定义的data
是_FirstPageState
所以这里直接传递this
,doSomeThing
方法中我们传递了_changeColor
用于改变mColor
的值,并且在子布局中能够刷新显示最新的color
。FColor
中获取到FrogColor
中的date数据
和调用doSomeThing 方法
上面的FrogColor
中model
是直接传入_FirstPageState
,这样耦合度会比较高
所以我们将这个对象转为泛型
,封装后如下:
新建 state_provider.dart
文件:
import 'package:flutter/material.dart';
/// 使用泛型传入数据
class IProvider extends InheritedWidget {
// 数据
final T data;
// 方法
final Function() doSomeThing;
IProvider({Key key, Widget child, this.data, this.doSomeThing})
: super(key: key, child: child);
@override
bool updateShouldNotify(IProvider oldWidget) {
return data != oldWidget.data;
}
static IProvider of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType>();
}
}
封装好的使用需要传入数据类
:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('InheritedWidget'),
),
body: IProvider<_FirstPageState>(// 传入 _FirstPageState
data: this
doSomeThing: _changeColor,//提供修改数据的方法
child: FColor('哈哈哈'),
)
);
}
以上就是InheritedWidget
的使用,使用InheritedWidget
的方式可以降低数据和UI
界面的耦合。
直接使用InheritedWidget
不免有些麻烦,Flutter
提供了更加强大的 Provider 库,用于实现依赖注入和状态管理(后续了解之后详细介绍)。
InheritedWidget
仅限与从父widget
向下传递数据。那么从子widget
向上传递数据如何实现呢?Notifiation
就可以实现。
Notification
数据共享方式是从子widgetw 向上传递至父
widget`。
在之前的文章 Flutter 嵌套滚动 CustomScrollView 示例 中,我们使用NotificationListener
去监听子child CustomScrollView
的滑动距离。
同理,将子widget
的数据传递给父widget
也可以用这个方式实现
从子widget
向上发送数据,在父widget
中监听到消息并打印在控制台。
以上就是Notification
的使用,使用Notification
的方式可以子widget
的数据通过dispatch
发送给父widget
。
无论是 InheritedWidget
还是 Notificaiton
,都需要依赖与父子关系的widget
。
而一些场景下的数据传递是没有这层关系的,
在Android
中有事件总线的方式可以很方便的实现这类场景的数据传递,
Flutter
中也有这样类似的库:EventBus
使用
Dart Streams
进行应用程序解耦的简单事件总线。
dependencies:
event_bus: ^1.1.1
EventBus
,并定义消息数据类final eventBus = new EventBus();
/// 传递的数据
class MsgEvent{
final String msg;
MsgEvent(this.msg);
}
// 监听数据变化
eventBus.on().listen((event) {
print("接收到的数据:${event.msg}");
});
eventBus.fire(MsgEvent("子 widget 发送过来的数据:老头子"));
通过以上的4步即可完成不依赖widget
的数据传输。
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
final eventBus = EventBus();
class FirstPage extends StatefulWidget {
@override
_FirstPageState createState() {
return _FirstPageState();
}
}
class _FirstPageState extends State {
@override
void initState() {
super.initState();
// 监听数据变化
eventBus.on().listen((event) {
print("接收到的数据:${event.msg}");
});
}
@override
void dispose() {
super.dispose();
// 释放资源
eventBus.destroy();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('EventBus'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 子 widget
RaisedButton(
// 按钮点击时分发通知
onPressed: () {
eventBus.fire(MsgEvent("父 widget 发送过来的数据:我是你爸爸"));
},
child: Text("父 widget 发送数据"),
),
CustomChild(),
],
));
}
}
class CustomChild extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
// 按钮点击时分发通知
onPressed: () {
eventBus.fire(MsgEvent("子 widget 发送过来的数据:老头子"));
},
child: Text("子 widget 发送数据"),
);
}
}
/// 消息数据类
class MsgEvent{
final String msg;
MsgEvent(this.msg);
}
wan~