Flutter中跨组件数据传递
Flutter中的数据传递一般包括:父->子,子->父,父->父。也就是说嵌套时的传递以及跨页面的传递。对于数据传递我们通常使用的有三种方法。
方法1:InheritedWidget
InheritedWidget
:它提供了一种数据在Widget树中从上向下传递,共享的方式。我们在应用的根Widget中通过InheritedWidget
共享了一个数据,那么我们可以在任意子Widget中获取该共享的数据!其通用方式就是:
class ShareDataWidget extends InheritedWidget {
final int data;
ShareDataWidget({this.data, Widget child}) : super(child: child);
static ShareDataWidget of(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
@override
bool updateShouldNotify(ShareDataWidget oldWidget) {
return oldWidget.data !=data;
}
}
其实就是继承InheritedWidget
,首先定义需要保存的数据比如这里的int型data,然后实现updateShouldNotify
方法,最后定义一个获取该Widget的静态方法,在需要使用该保存数据的时候通过这个静态方法获取。使用时,在父widget中传入需要保存的data,在child中通过定义的静态方法来获取最新值。比如定义的parent如下
class Parent extends StatefulWidget {
Parent({Key key}) : super(key: key);
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> with WidgetsBindingObserver {
int _counter = 0;
//当Widget第一次插入到Widget树时会被调用。对于每一个State对象,Flutter只会调用该回调一次
@override
void initState() {
super.initState();
print("page2 parent initState......");
}
@override
void setState(fn) {
super.setState(fn);
print("page2 parent setState......");
}
/*
*初始化时,在initState之后立刻调用
*当State的依赖关系发生变化时,会触发此接口被调用
*/
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("page2 parent didChangeDependencies......");
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
print("page2 state is $state");
}
//绘制界面
@override
Widget build(BuildContext context) {
print("page2 parent build......");
return Scaffold(
appBar: AppBar(title: Text("setState demo")),
body: Center(
child: ShareDataWidget(
data: _counter,
child: RaisedButton(
///点击事件
onPressed: () {
setState(() {
_counter++;
});
},
child: Child(),
),
)),
);
}
//状态改变的时候会调用该方法,比如父类调用了setState
@override
void didUpdateWidget(Parent oldWidget) {
super.didUpdateWidget(oldWidget);
print("page2 parent didUpdateWidget......");
}
//当State对象从树中被移除时,会调用此回调
@override
void deactivate() {
super.deactivate();
print('page2 parent deactivate......');
}
//当State对象从树中被永久移除时调用;通常在此回调中释放资源
@override
void dispose() {
super.dispose();
print('page2 parent dispose......');
}
}
可以看到我们传入了_counter属性到ShareDataWidget
中,并且SharedDataWidget
的child需要使用共享的变量。我们的Child定义如下
class Child extends StatefulWidget {
Child({Key key}) : super(key: key);
@override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
//绘制界面
@override
Widget build(BuildContext context) {
print("child build......");
return Text.rich(TextSpan(children: [
TextSpan(
text: '点击按钮查看状态变化 count: ${ShareDataWidget.of(context).data.toString()}',
style: TextStyle(color: Color.fromARGB(100, 255, 0, 0))),
TextSpan(
text: "新加的一段文本",
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
print("点击了文本");
})
]));
}
//当Widget第一次插入到Widget树时会被调用。对于每一个State对象,Flutter只会调用该回调一次
@override
void initState() {
super.initState();
print("page2 child initState......");
}
/*
*初始化时,在initState之后立刻调用
*当State的依赖关系发生变化时,会触发此接口被调用
*/
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("page2 child didChangeDependencies......");
}
//状态改变的时候会调用该方法,比如父类调用了setState
@override
void didUpdateWidget(Child oldWidget) {
super.didUpdateWidget(oldWidget);
print("page2 child didUpdateWidget......");
}
//当State对象从树中被移除时,会调用此回调
@override
void deactivate() {
super.deactivate();
print('page2 child deactivate......');
}
//当State对象从树中被永久移除时调用;通常在此回调中释放资源
@override
void dispose() {
super.dispose();
print('page2 child dispose......');
}
}
其使用就是在Parent中用创建好的ShareDataWidget作为父Widget,在父widget更新需要保存的变量时,同时将数据保存在ShareDataWidget的data中,比如上面在parent的build方法中
真正使用这个值的地方是Child类,它的build方法
在parent中触发onPress事件其调用如下
通过使用方式可以看到明显适用于父->子
方法2:Notification
Notification它的数据传递方式是从子Widget向上传递给父Widget。比如先定义一个notification,这里只有一个msg需要传递给父Widget。
class CustomNotification extends Notification{
final String msg;
CustomNotification(this.msg);
}
然后定义一个子Widget来发送这个通知
class CustomChild extends StatelessWidget{
@override
Widget build(BuildContext context) {
return ElevatedButton(onPressed: ()=>CustomNotification("HI").dispatch(context),child: Text("FireNotification"),);
}
}
在父widget中使用这个通知
class ParentPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ParentPageState();
}
}
class _ParentPageState extends State<ParentPage> {
String msg = "通知";
@override
Widget build(BuildContext context) {
return NotificationListener<CustomNotification>(
onNotification: (notification){
setState(() {
msg+=notification.msg+" ";
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text(msg), CustomChild()],
));
}
}
可以看到父widget中的根是NotificationListener这个监听器,刚才定义的CustomChild是他的子Widget。
方法3:EventBus
这种方式在Android中
也经常使用,这个就比较简单了。首先引用该组件event_bus: ^2.0.0
。可以在全局声明一个EventBus
实例,比如在main.dart
中。
EventBus bus = new EventBus();
在需要接收订阅的类中
StreamSubscription subscription;
String msg = "event Msg";
//当Widget第一次插入到Widget树时会被调用。对于每一个State对象,Flutter只会调用该回调一次
@override
void initState() {
subscription = bus.on<CustomEvent>().listen((event) {
setState(() {
msg+=event.msg+" ";
});
});
super.initState();
print("page1 initState......");
}
这里的subscription
其实就是为了防止泄露的,在dispose
的时候我们要解除订阅,CustomEvent其实就是一个类,
class CustomEvent{
String msg;
CustomEvent(this.msg);
}
在需要发送通知的地方
bus.fire(CustomEvent("我是EventBus发送的"));
这个比较适合于父->父。也就是所谓的“页面间”的数据传递
以上就是最常用的三种数据传递方式。
转载自:https://juejin.cn/post/7243611408629792827