flutter:调用子组件的内部方法
在编写flutter应用时,父子组件通信在实现复杂功能时是非常必要的,其中子组件调用父组件的方法实现较为简单,只需要将方法作为参数传入子组件的构造函数即可。 而很多时候父组件需要调用子组件的内部方法,让子组件自己变更某些状态或数据, 比如一个表单(父Widget)定义了一个选择菜单(子Widget),用户在外层执行了保存数据、校验等操作,需要每个子组件执行自己的检查/保存方法,如果将这些方法都保存在父组件内部会使父组件冗余不堪,因此父组件也需要能够在此时能够调用子组件的内部方法。 接下来我们将介绍两种方法来实现这种需求。
1 利用GlobalKey(常见方法)
常用的方法是使用GlobalKey——几乎所有的google教程都会这么教你,其核心是利用key的唯一性,来来获取到子组件的element。
1.1 Key
key
在Wigets
不断变化时可以用于保存状态,最常见的用法是用于stateful wigets
的Element树更新,当Element树检查key
不一样时会触发视图更新(类似前端中Vue的key)。
如下图所示,当没有key时,如果想更新两个不同state的widget,两个Element的比较会相同,因为他们都是satefulElement,因此更新不会生效,而给两个Widget以不同的key,那他们就会完全不同了,更新会立即生效。
key
分为以下几个种类:
ValueKey
:以一个值为key
ObjectKey
:以一个对象为key
UniqueKey
:生成唯一的随机数作为key
PageStorageKey
:专用于存储页面滚动位置的key
GlobalKey
每个Globalkey
都是一个在整个应用内唯一的key
,类似于全局变量,成本比较高
1.2 GlobalKey的用途
GlobalKey一般有两种用途:
- 允许
widget
在应用程序中更改其在wiget树
中的位置,同时不丢失其状态。 应用场景:在两个不同的屏幕上显示相同的widget
,并保持状态相同。 - 在
wiget树
的其他部分访问一个wiget
的信息。 应用场景:跨widget
访问状态 我们这里就是用GlobalKey
的第二种用途,可以跨widget来获取子Widget
的状态信息,调用其方法,从而实现其私有方法的调用,具体例子如下: 父组件parent.dart
import 'package:flutter/material.dart';
import 'child.dart'; //这里引入子组件
class ParentScreen extends StatefulWidget {
@override
_ParentScreenState createState() => _ParentScreenState();
}
class _ParentScreenState extends State<ParentScreen> {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ChildScreen(
key: childKey // 将Key传递给子组件
),
TextButton(
onPressed: (){
childKey.currentState.childFunction(); // 以Key调用子组件的方法
},
child: Text('点击我调用子组件方法'),
)],
);
}
}
子组件child.dart
import 'package:flutter/material.dart';
GlobalKey<_ChildScreenState> childKey = GlobalKey(); // 在这里定义child状态的GlobalKey
class ChildScreen extends StatefulWidget {
ChildScreen({ Key key, }) : super(key: key);
@override
_ChildScreenState createState() => _ChildScreenState();
}
class _ChildScreenState extends State<ChildScreen> {
@override
Widget build(BuildContext context) {
return Container();
}
childFunction(){
print('this is a childFunction');
}
}
Globalkey的方法虽然常用,但这种方法一是在组件多起来的时候会产生大量的key,污染全局环境;二是当一个子组件实现了很多实例时,key的维护就显得较为困难了。
2 自定义Controller
如果你接触过文字输入框TextFiled
组件,那你一定听说过TextEditingController
,用于在外部组件获取输入框内部正在编辑的文字,对文字内容进行各类控制操作。在官方文档里中给出了如下示例:
class _MyCustomFormState extends State<MyCustomForm> {
...
final myController = TextEditingController(); // 定义controller
...
@override
Widget build(BuildContext context) {
return Scaffold(
...
body: TextField(
controller: myController, // 传入controller
),// TextField
...
floatingActionButton: FloatingActionButton(
...
onPressed: () {
print(myController.text); // 调用controller的属性(方法同理)
},
...
),// FloatingActionButton
);
}
}
受到该编辑控制器的启发,我们可以自定义一个控制器类,如下图所示:
在子组件中定义一个controller类,其中定义需要实现的子组件空方法,在父组件中将其实例化后作为参数传递给子组件,子组件在构造函数中对这个类中的方法进行最终实现,我们就可以在父组件中,通过调用这个实例的各方法,来调用子组件的私有方法了。我们用与方法1同样的例子来实现这种方法:
父组件parent.dart
import 'package:flutter/material.dart';
import 'child.dart'; //这里引入子组件
class ParentScreen extends StatefulWidget {
@override
_ParentScreenState createState() => _ParentScreenState();
}
class _ParentScreenState extends State<ParentScreen> {
final ChildController myController = ChildController(); // 定义一个控制器用于调用子组件
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ChildScreen(
controller: myController, // 传入控制器对象
),
TextButton(
onPressed: (){
myController.childFunction(); // 调用子组件的方法
},
child: Text('点击我调用子组件方法'),
)
],
);
}
}
子组件child.dart
import 'package:flutter/material.dart';
class ChildController {
// 定义控制器类的字段和方法
void Function() childFunction;
}
class ChildScreen extends StatefulWidget {
final ChildController controller;
ChildScreen({
Key key,
this.controller, // 传入控制器的参数
}) : super(key: key);
@override
_ChildScreenState createState() => _ChildScreenState(controller); // 将控制器传入状态
}
class _ChildScreenState extends State<ChildScreen> {
_ChildScreenState(ChildController _controller) {
_controller.childFunction = childFunction; // 将child状态中的函数(也就是要调用的函数),装载到控制器上,这样就实现了外层调用 }
@override
Widget build(BuildContext context) {
return Container();
}
childFunction(){ // child方法的具体定义
print('this is a childFunction');
}
}
本文介绍了两种实现子组件私有方法调用的方法,其中最常用的是GlobalKey方法,笔者则更推荐自定义控制器的方法,读者可以在不同的场景灵活运用这两种方法。
转载自:https://juejin.cn/post/7270061728896663610