一、Android界面渲染流程UI树与FlutterUI树的设计思路对比
二、Widget组件生命周期详解
1、Widget组件生命周期
和其他的视图框架比如android的Activity一样,flutter中的视图Widget也存在生命周期,生命周期的回调函数体现在了State上面。组件State的生命周期整理如下图所示:
createState:
当一个StatefulWidget插入到渲染树结构、或者从渲染树结构移除时,都会调用StatefulWidget.createState方法,从而达到更新UI的效果;
initState:
initState是StatefulWidget创建后调用的第一个方法,而且只执行一次。在执行initState时,View没有渲染,但是StatefulWidget 已经被加载到渲染树里了;
didChangeDependencies:
didChangeDependencies会在initState后立即调用,当StatefulWidget依赖的InheritedWidget发生变化之后,didChangeDependencies会调用,所以didChangeDependencies可以调用多次;
build:
build方法会在didChangeDeoendencies之后立即调用,在之后setState()刷新时,会重新调用build绘制页面,所以build方法可以调用多次。一般不在build中创建除了创建Widget的方法,否则会影响渲染效率。
setState:
[State] 对象可以通过调用它们的 [setState]方法自发地请求重建其子树,这表明它们的某些内部状态已经改变,可能会影响该子树中的用户界面,setState方法会被多次调用。
didUpdateWidget:
1、当调用setState更新UI的时候,都会调用didUpdateWidget;
2、框架在调用[didUpdateWidget]之后总是调用[build],在[didUpdateWidget]中对[setState]的任何调用都是多余的。
deactivate:
1、当框架从树中移除此 State 对象时将会调用此方法;
2、在某些情况下,框架将重新插入State 对象到树的其他位置(例如,如果包含该树的子树State 对象从树中的一个位置移植到另一位置),框架将会调用 build 方法来提供 State 对象适应其在树中的新位置。
dispose:
当框架从树中永久移除此 State对象时将会调用此方法,与deactivate的区别是,deactivate 还可以重新插入到树中,而 dispose 表示此 State对象永远不会在 build。调用完 dispose后,mounted 属性被设置为false,也代表组件生命周期的结束,此时再调用setState方法将会抛出异常。子类重写此方法,释放相关资源,比如动画等。
2、生命周期调用
A Page:
Dart
import 'package:flutter/material.dart';
import 'package:flutter_app/pages/DemoPages.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Demo(),
);
}
}
class Demo extends StatefulWidget {
const Demo({super.key});
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
print("------main--------build");
return Scaffold(
appBar: AppBar(
title: const Text("StatefulWidget key"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(builder:(setting){
return const DemoPage();
}
)
);
},
child: const Text("页面跳转"),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
setState(() {
print("调用....");
});
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
print("------main--------initState");
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("-------main-------didChangeDependencies");
}
@override
void setState(VoidCallback fn) {
// TODO: implement setState
super.setState(fn);
print("------main--------setState");
}
@override
void deactivate() {
// TODO: implement deactivate
super.deactivate();
print("------main--------deactivate");
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
print("------main--------dispose");
}
}
B Page:
Dart
import 'package:flutter/material.dart';
class DemoPage extends StatelessWidget {
const DemoPage({super.key}) ;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Demo页面"),
),
body: const DemoWidget(),
);
}
}
class DemoWidget extends StatefulWidget {
const DemoWidget({super.key});
@override
State<DemoWidget> createState() => _DemoWidgetState();
}
class _DemoWidgetState extends State<DemoWidget> {
String btnText = "test";
@override
Widget build(BuildContext context) {
print("------_DemoWidgetState--------build");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: (){
setState(() {
if(btnText == "test"){
btnText = "测试";
}else{
btnText = "test";
}
});
},
child: Text(btnText),
),
ElevatedButton(
onPressed: (){
print("返回......");
Navigator.of(context).pop();
},
child: const Text("返回"),
),
],
),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
print("------_DemoWidgetState--------initState");
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("-------_DemoWidgetState-------didChangeDependencies");
}
@override
void setState(VoidCallback fn) {
// TODO: implement setState
super.setState(fn);
print("------_DemoWidgetState--------setState");
}
@override
void deactivate() {
// TODO: implement deactivate
super.deactivate();
print("------_DemoWidgetState--------deactivate");
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
print("-----_DemoWidgetState---------dispose");
}
}
三、页面间数据传递(共享)的几种常用方式
公共页面:
Dart
import 'package:flutter/material.dart';
import 'transfer_constructor_page.dart';
import 'transfer_data_entity.dart';
import 'transfer_data_inherited.dart';
import 'transfer_data_singleton.dart';
import 'transfer_router_page.dart';
import 'transfer_singleton_page.dart';
import 'transfer_stream_singleton.dart';
import 'transfer_stream_singleton_page.dart';
import 'inherited_data_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
var params = TransferDataEntity(name:"王五", id:"009", age:16);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Data Transfer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Data Transfer Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var data = TransferDataEntity(name:"张三丰", id:"001", age:18);
List<String> itemNames = [
"构造器传递数据",
"返回上个页面时携带参数",
"InheritedWidget方式",
"Singleton方式",
"Singleton结合Stream",
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 60.0,
color: Colors.black12,
child: Text("${data.id},${data.name},${data.age}")),
Expanded(
child: ListView.separated(
separatorBuilder: (BuildContext contex, int index) {
return const Divider(
color: Colors.black12,
height: 0.5,
);
},
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () => _onClick(index, data, context),
child: Container(
alignment: Alignment.center,
height: 48.0,
width: double.infinity,
child: Text(
itemNames[index],
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
),
),
);
},
itemCount: itemNames.length,
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
_onClick(int index, TransferDataEntity data, BuildContext context) {
switch (index) {
case 0:
_transferDataByConstructor(context, data);
break;
case 1:
_toTransferForResult(context, data);
break;
case 2:
_inheritedToPage(context, data);
break;
case 3:
_singletonDataTransfer(context);
break;
case 4:
_streamDataTransfer(context);
break;
}
}
//通过构造器方法传递数据
_transferDataByConstructor(BuildContext context, TransferDataEntity data) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DataTransferByConstructorPage(data: data)));
}
_toTransferForResult(BuildContext context, TransferDataEntity data) async {
final dataFromOtherPage = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => TransferRouterPage(data: data)),
) as TransferDataEntity;
setState(() {
data.name = dataFromOtherPage.name;
data.id = dataFromOtherPage.id;
data.age = dataFromOtherPage.age;
});
}
_inheritedToPage(BuildContext context, TransferDataEntity data) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IDataProvider(
data: data,
child: IDataWidget(),
)));
}
_singletonDataTransfer(BuildContext context) {
var transferData = TransferDataEntity(name:"三喵2", id:"002", age:20);
transSingletonData.transData = transferData;
Navigator.push(context,
MaterialPageRoute(builder: (context) => TransferSingletonPage()));
}
_streamDataTransfer(BuildContext context) {
var transferData = TransferDataEntity(name:"三喵", id:"005", age:20);
streamSingletonData.setTransferData(transferData);
Navigator.push(context,
MaterialPageRoute(builder: (context) => TransferStreamPage()));
}
}
1、通过构造器(constructor)传递数据
通过构造器传递数据是一种最简单的方式,也是最常用的方式,在第一个页面,我们模拟创建一个我们需要传递数据的对象。当点击跳转的时候,我们把数据传递DataTransferByConstructorPage页面,并把携带过来的数据展示到页面上。
1)创建一个传递数据对象 :final data=TransferDataEntity("001","张三丰",18);
2)定义一个跳转到DataTransferByConstructorPage页面的方法:
3)在DataTransferByConstructorPage页面接收到数据并展示出来:
Dart
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';
///通过构造器的方式传递参数
class DataTransferByConstructorPage extends StatelessWidget {
final TransferDataEntity data;
const DataTransferByConstructorPage({super.key, required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("构造器方式"),
),
body: Column(
children: <Widget>[
Container(
width: double.infinity,
height: 40.0,
alignment: Alignment.center,
child: Text(data.id),
),
Container(
width: double.infinity,
height: 40.0,
alignment: Alignment.center,
child: Text(data.name),
),
Container(
width: double.infinity,
height: 40.0,
alignment: Alignment.center,
child: Text("${data.age}"),
)
],
),
);
}
}
2、当一个页面关闭时携带数据到上一个页面(Navigator.pop)
在Android开发中我们需要将数据传递给上一个页面通常使用的传统方式是startActivityForResult()方法。但是在flutter就不用这么麻烦了。只需要使用Navigator.pop方法即可将数据结果带回去。但是我们跳转的时候需要注意两点:
1)我们需要定义一个异步方法用于接收返回来的结果:
2)在我们要关闭的页面使用Navigator.pop 返回第一个页面:
Dart
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';
class TransferRouterPage extends StatelessWidget {
final TransferDataEntity data;
const TransferRouterPage({super.key, required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("返回上个页面传递参数"),
leading: Builder(builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
_backToData(context);
});
}),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
ElevatedButton(
onPressed: () {
_backToData(context);
},
child: const Text("点我返回上一个页面并把数据传回去"))
],
),
);
}
//返回并携带数据
_backToData(BuildContext context) {
var transferData = TransferDataEntity(name: "嘻嘻哈哈", id: "007", age: 20);
Navigator.pop(context, transferData);
}
}
3、InheritedWidget方式
使用lnheritedWidget方式如下几步:
1)继承lnheritedWidget提供一个数据源:
Dart
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';
//所有的子组件共享数据
class IDataProvider extends InheritedWidget {
final TransferDataEntity data;
const IDataProvider({super.key, required super.child, required this.data});
@override
bool updateShouldNotify(IDataProvider oldWidget) {
return data != oldWidget.data;
}
//版本问题
//InheritedWidget
static IDataProvider? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<IDataProvider>();
}
// static _InheritedProviders of(BuildContext context) {
// final widget = context.inheritFromWidgetOfExactType(_InheritedProviders);
// return widget is _InheritedProviders ? widget : null;
// }
}
2)定义页面跳转时候携带数据的方法:
3)跳转的到的页面并展示数据代码如下:
Dart
import 'package:flutter/material.dart';
import 'inherited_data_provider.dart';
class IDataWidget extends StatelessWidget {
const IDataWidget({super.key});
@override
Widget build(BuildContext context) {
final data = IDataProvider.of(context)!.data;
return Scaffold(
appBar: AppBar(
title: const Text("Inherited方式传递数据"),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
const IDataChildWidget()
],
),
);
}
}
class IDataChildWidget extends StatelessWidget {
const IDataChildWidget({super.key});
@override
Widget build(BuildContext context) {
final data = IDataProvider.of(context)!.data;
return Text(data.name);
}
}
4、全局的提供数据的方式
这种方式我们还是使用lnheritedWidget,区别就是我们不是跳转的时候去创建IGenericDataProvider。而是把他放在最顶层注意:这种方式一定要把数据放在顶层;
1)定义顶部数据:
2)接收数据的方式基本和 lnheritedWidget相同:
final data=IGenericDataProvider.of<TransferDataEntity>(context) 获取数据
3)使用代码:
5、通过全局单例模式来使用
这种方式就是创建一个全局单例对象,任何地方都可以操控这个对象,存储和取值都可以通过这个对象。
1)创建单例对象:
Dart
import 'transfer_data_entity.dart';
class TransferDataSingleton {
static final TransferDataSingleton _instanceTransfer =
TransferDataSingleton.__internal();
TransferDataEntity? transData;
factory TransferDataSingleton() {
return _instanceTransfer;
}
TransferDataSingleton.__internal();
}
final transSingletonData = TransferDataSingleton();
2)给单例对象存放数据:
3)接收并使用传递的值:
Dart
import 'package:flutter/material.dart';
import 'transfer_data_singleton.dart';
class TransferSingletonPage extends StatefulWidget {
const TransferSingletonPage({super.key});
@override
_TransferSingletonPageState createState() => _TransferSingletonPageState();
}
class _TransferSingletonPageState extends State<TransferSingletonPage> {
@override
Widget build(BuildContext context) {
var data = transSingletonData.transData;
return Scaffold(
appBar: AppBar(
title: const Text("全局单例传递数据"),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data!.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
],
),
);
}
}
6、全局单例结合Stream的方式传递数据
1)单例模式:
Dart
import 'dart:async';
import 'transfer_data_entity.dart';
class TransferStreamSingleton {
static final TransferStreamSingleton _instanceTransfer =
TransferStreamSingleton.__internal();
StreamController? streamController;
void setTransferData(TransferDataEntity transData) {
streamController = StreamController<TransferDataEntity>();
streamController!.sink.add(transData);
}
factory TransferStreamSingleton() {
return _instanceTransfer;
}
TransferStreamSingleton.__internal();
}
final streamSingletonData = TransferStreamSingleton();
2)传递要携带的数据:
3)接收要传递的值:
Dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';
import 'transfer_stream_singleton.dart';
class TransferStreamPage extends StatefulWidget {
const TransferStreamPage({super.key});
@override
_TransferStreamPageState createState() => _TransferStreamPageState();
}
class _TransferStreamPageState extends State<TransferStreamPage> {
final StreamController? _streamController =
streamSingletonData.streamController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("全局单例结合Stream"),
),
body: StreamBuilder(
stream: _streamController!.stream,
initialData: TransferDataEntity(),
builder: (context, snapshot) {
return Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(snapshot.data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(snapshot.data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${snapshot.data.age}"),
),
],
);
}));
}
@override
void dispose() {
_streamController?.close();
super.dispose();
}
}
四、Flutter Key
我们平时一定接触过很多的Widget,比如 Container、Row、Column等,它们在我们绘制界面的过程中发挥着重要的作用。但是不知道你有没有注意到,在几乎每个Widget的构造函数中,都有一个共同的参数,它们通常在参数列表的第一个,那就是Key。
在Flutter中,Key是不能重复使用的,所以Key一般用来做唯一标识。组件在更新的时候,其状态的保存主要是通过判断组件的类型或者key值是否一致。因此,当各组件的类型不同的时候,类型已经足够用来区分不同的组件了,此时我们可以不必使用key。但是如果同时存在多个同一类型的控件的时候,此时类型已经无法作为区分的条件了,我们就需要使用到key。
1、没有Key 会发生什么现象:
如下面例:定义了一个StatefulWidget的Box,点击Box的时候可以改变Box里面的数字,当我们重新对Box排序的时候,Flutter就无法识别到Box的变化了,这是什么原因呢?
运行后我们发现改变list Widget顺序后,Widget颜色会变化,但是每个Widget里面的文本内容并没有变化,为什么会这样呢?当我们List重新排序后Flutter检测到了Widget的顺序变化,所以重新绘制List Widget,但是Flutter发现List Widget里面的元素没有变化,所以就没有改变Widget里面的内容。
把List 里面的Box的颜色改成一样,这个时候您重新对list进行排序,就很容易理解了。重新排序后虽然 执行了setState,但Flutter没法通过Box里面传入的参数是代码和以前是一样的,所以Flutter不会重构List Widget里面的内容,也就是来识别Box是否改变。如果要让FLutter能识别到List Widget子元素的改变,就需要给每个Box指定一个key。
实现代码:
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Widget> list = [
// Center(
// child: Box(
// color: Colors.blue,
// ),
// ),
Box(
color: Colors.blue,
),
Box(
color: Colors.red,
),
Box(
color: Colors.orange,
)
];
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
list.shuffle(); //打乱list的顺序
});
},
child: const Icon(Icons.refresh),
),
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: list,
),
),
);
}
}
class Box extends StatefulWidget {
Color color;
Box({super.key, required this.color});
@override
State<Box> createState() => _BoxState();
}
class _BoxState extends State<Box> {
int _count = 0;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(widget.color)),
onPressed: () {
setState(() {
_count++;
});
},
child: Center(
child: Text("$_count"),
),
),
);
}
}
2、LocalKey与GlobalKey
在Flutter中,Key是不能重复使用的,所以Key一般用来做唯一标识。组件在更新的时候,其状态的保存主要是通过判断组件的类型或者key值是否一致。因此,当各组件的类型不同的时候,类型已经足够用来区分不同的组件了,此时我们可以不必使用key。但是如果同时存在多个同一类型的控件的时候,此时类型已经无法作为区分的条件了,我们就需要使用到key。
运用key变更上面案例业务:
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Widget> list = [
Center(
key: UniqueKey(),
child: Box(
color: Colors.blue,
),
),
// Box(
// // key: const ValueKey("1"),//指定值
// color: Colors.blue,
// ),
Box(
key: UniqueKey(), //唯一值, 自动生成
color: Colors.red,
),
Box(
key: const ObjectKey("2"), //类似于ValueKey
// key: const ObjectKey(2),//类似于ValueKey
color: Colors.orange,
)
];
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
list.shuffle(); //打乱list的顺序
});
},
child: const Icon(Icons.refresh),
),
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: list,
),
),
);
}
}
class Box extends StatefulWidget {
Color color;
Box({super.key, required this.color});
@override
State<Box> createState() => _BoxState();
}
class _BoxState extends State<Box> {
int _count = 0;
_BoxState() {
print("--------_BoxState----------构造");
}
@override
Widget build(BuildContext context) {
print("------_BoxState--------build");
return SizedBox(
height: 100,
width: 100,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(widget.color)),
onPressed: () {
setState(() {
_count++;
});
},
child: Center(
child: Text("$_count"),
),
),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
print("------_BoxState--------initState");
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("-------_BoxState-------didChangeDependencies");
}
@override
void setState(VoidCallback fn) {
// TODO: implement setState
super.setState(fn);
print("------_BoxState--------setState");
}
@override
void deactivate() {
// TODO: implement deactivate
super.deactivate();
print("------_BoxState--------deactivate");
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
print("------_BoxState--------dispose");
}
}
3、GlobalKey的使用
如果把LocalKey比作局部变量,GlobalKey就类似于全局变量;
下面使用了LocalKey,当屏幕状态改变的时候把Colum换成了Row,Box的状态就会丢失。一个Widget状态的保存主要是通过判断组件的类型或者key值是否一致。 LocalKey只在当前的组件树有效,所以把Colum换成了Row的时候Widget的状态就丢失了。
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Widget> list = [
Box(
key: const ValueKey(1), //int double string
color: Colors.blue,
),
Box(
key: ObjectKey(Object()),
color: Colors.red,
),
Box(
key: UniqueKey(), //程序自动生成一个key
color: Colors.orange,
)
];
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
list.shuffle(); //打乱list的顺序
});
},
child: const Icon(Icons.refresh),
),
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: MediaQuery.of(context).orientation == Orientation.portrait
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: list,
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: list,
),
),
);
}
}
class Box extends StatefulWidget {
Color color;
Box({super.key, required this.color});
@override
State<Box> createState() => _BoxState();
}
class _BoxState extends State<Box> {
int _count = 0;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(widget.color)),
onPressed: () {
setState(() {
_count++;
});
},
child: Center(
child: Text("$_count"),
),
),
);
}
}
为了解决这个问题我们就可以使用GlobalKey。
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// List<Widget> list = [
// Box(
// key: const ValueKey(1),
// color: Colors.blue,
// ),
// Box(
// key: ObjectKey(Box(color: Colors.red)),
// color: Colors.red,
// ),
// Box(
// key:UniqueKey(), //程序自动生成一个key
// color: Colors.orange,
// )
// ];
List<Widget> list = [
Box(
key: GlobalKey(),
color: Colors.blue,
),
Box(
key: GlobalKey(),
color: Colors.red,
),
Box(
key: GlobalKey(), //程序自动生成一个key
color: Colors.orange,
)
];
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
list.shuffle(); //打乱list的顺序
});
},
child: const Icon(Icons.refresh),
),
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: MediaQuery.of(context).orientation == Orientation.portrait
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: list,
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: list,
),
),
);
}
}
class Box extends StatefulWidget {
Color color;
Box({super.key, required this.color});
@override
State<Box> createState() => _BoxState();
}
class _BoxState extends State<Box> {
int _count = 0;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(widget.color)),
onPressed: () {
setState(() {
_count++;
});
},
child: Center(
child: Text("$_count"),
),
),
);
}
}
4、GlobalKey 获取子组件
globalKey.currentState 可以获取子组件的状态,执行子组件的方法,globalKey.currentWidget可以获取子组件的属性,_globalKey.currentContext!.findRenderObject()可以获取渲染的属性。
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final GlobalKey _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
//1、获取子组件的状态 调用子组件的属性
var state = (_globalKey.currentState as _BoxState);
setState(() {
state._count++;
});
//2、获取子组件的属性
var box = (_globalKey.currentWidget as Box);
print(box.color);
//3、获取子组件渲染的属性
var renderBox =
(_globalKey.currentContext?.findRenderObject() as RenderBox);
print(renderBox.size);
},
),
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: Box(
key: _globalKey,
color: Colors.red,
),
),
);
}
}
class Box extends StatefulWidget {
final Color color;
const Box({super.key, required this.color});
@override
State<Box> createState() => _BoxState();
}
class _BoxState extends State<Box> {
int _count = 0;
run() {
print("run");
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(widget.color)),
onPressed: () {
setState(() {
_count++;
});
},
child: Center(
child: Text("$_count"),
),
),
);
}
}