likes
comments
collection
share

flutter:调用子组件的内部方法

作者站长头像
站长
· 阅读数 71

在编写flutter应用时,父子组件通信在实现复杂功能时是非常必要的,其中子组件调用父组件的方法实现较为简单,只需要将方法作为参数传入子组件的构造函数即可。 而很多时候父组件需要调用子组件的内部方法,让子组件自己变更某些状态或数据, 比如一个表单(父Widget)定义了一个选择菜单(子Widget),用户在外层执行了保存数据、校验等操作,需要每个子组件执行自己的检查/保存方法,如果将这些方法都保存在父组件内部会使父组件冗余不堪,因此父组件也需要能够在此时能够调用子组件的内部方法。 接下来我们将介绍两种方法来实现这种需求。

1 利用GlobalKey(常见方法)

常用的方法是使用GlobalKey——几乎所有的google教程都会这么教你,其核心是利用key的唯一性,来来获取到子组件的element。

1.1 Key

keyWigets不断变化时可以用于保存状态,最常见的用法是用于stateful wigets的Element树更新,当Element树检查key不一样时会触发视图更新(类似前端中Vue的key)。 如下图所示,当没有key时,如果想更新两个不同state的widget,两个Element的比较会相同,因为他们都是satefulElement,因此更新不会生效,而给两个Widget以不同的key,那他们就会完全不同了,更新会立即生效。 flutter:调用子组件的内部方法

flutter:调用子组件的内部方法 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 
		); 
	} 
} 

受到该编辑控制器的启发,我们可以自定义一个控制器类,如下图所示: flutter:调用子组件的内部方法 在子组件中定义一个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
评论
请登录