Flutter 05 组件状态、生命周期、数据传递(共享)、Key

一、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、生命周期调用 ![](https://file.jishuzhan.net/article/1719838102769373185/57520714233e65ce46f8d8a8a70ff12c.webp) ##### 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 createState() => _DemoState(); } class _DemoState extends State { @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: [ 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 createState() => _DemoWidgetState(); } class _DemoWidgetState extends State { String btnText = "test"; @override Widget build(BuildContext context) { print("------_DemoWidgetState--------build"); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ 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 { var data = TransferDataEntity(name:"张三丰", id:"001", age:18); List itemNames = [ "构造器传递数据", "返回上个页面时携带参数", "InheritedWidget方式", "Singleton方式", "Singleton结合Stream", ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( children: [ 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页面的方法: ![](https://file.jishuzhan.net/article/1719838102769373185/a3804db44874475259599303583b6634.webp) 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: [ 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)我们需要定义一个异步方法用于接收返回来的结果: ![](https://file.jishuzhan.net/article/1719838102769373185/86bae94cf093503c4548a3fed66a3bdf.webp) 2)在我们要关闭的页面使用Navigator.pop 返回第一个页面: ![](https://file.jishuzhan.net/article/1719838102769373185/31c4d6adde8da19dee998dfb5421f7c9.webp) ```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: [ 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方式 ![](https://file.jishuzhan.net/article/1719838102769373185/0e1bbc01ac061489e0630dc3c03deae8.webp) 使用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(); } // static _InheritedProviders of(BuildContext context) { // final widget = context.inheritFromWidgetOfExactType(_InheritedProviders); // return widget is _InheritedProviders ? widget : null; // } } ``` 2)定义页面跳转时候携带数据的方法: ![](https://file.jishuzhan.net/article/1719838102769373185/d40331132879a4823ce420b099c843e1.webp) 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: [ 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)定义顶部数据: ![](https://file.jishuzhan.net/article/1719838102769373185/63c3caecba3eb3ee345267d0eeb902ea.webp) 2)接收数据的方式基本和 lnheritedWidget相同: final data=IGenericDataProvider.of\(context) 获取数据 3)使用代码: ![](https://file.jishuzhan.net/article/1719838102769373185/9296764a4109f9b9194f401a1e7293d7.webp) #### 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)给单例对象存放数据: ![](https://file.jishuzhan.net/article/1719838102769373185/ecaa2b885c073494e6038cf9322e67b6.webp) 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 { @override Widget build(BuildContext context) { var data = transSingletonData.transData; return Scaffold( appBar: AppBar( title: const Text("全局单例传递数据"), ), body: Column( children: [ 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的方式传递数据 ![](https://file.jishuzhan.net/article/1719838102769373185/a052af3e3784bbf9bf31ac7d9bca8a8b.webp) 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(); streamController!.sink.add(transData); } factory TransferStreamSingleton() { return _instanceTransfer; } TransferStreamSingleton.__internal(); } final streamSingletonData = TransferStreamSingleton(); ``` 2)传递要携带的数据: ![](https://file.jishuzhan.net/article/1719838102769373185/a6685edcaf7557742d4abe27eebf4b28.webp)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 { 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: [ 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的变化了,这是什么原因呢? ![](https://file.jishuzhan.net/article/1719838102769373185/d4f5b5119871b25fe298214b4a3ef641.webp) 运行后我们发现改变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 createState() => _MyHomePageState(); } class _MyHomePageState extends State { List 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 createState() => _BoxState(); } class _BoxState extends State { 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。 ![](https://file.jishuzhan.net/article/1719838102769373185/c06af8ebdd50482bef78fd8b462cc7c5.webp) ![](https://file.jishuzhan.net/article/1719838102769373185/4d6a7e863866e92847c60fab5880d1d8.webp) ![](https://file.jishuzhan.net/article/1719838102769373185/37b731ec770a92d92b473ec61192707e.webp) 运用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 createState() => _MyHomePageState(); } class _MyHomePageState extends State { List 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 createState() => _BoxState(); } class _BoxState extends State { 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 createState() => _MyHomePageState(); } class _MyHomePageState extends State { List 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 createState() => _BoxState(); } class _BoxState extends State { 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 createState() => _MyHomePageState(); } class _MyHomePageState extends State { // List 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 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 createState() => _BoxState(); } class _BoxState extends State { 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 createState() => _HomePageState(); } class _HomePageState extends State { 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 createState() => _BoxState(); } class _BoxState extends State { 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"), ), ), ); } } ```

相关推荐
bst@微胖子12 小时前
Flutter项目之登录注册功能实现
开发语言·javascript·flutter
小墙程序员13 小时前
Flutter 教程(十一)多语言支持
flutter
无知的前端16 小时前
Flutter 一文精通Isolate,使用场景以及示例
android·flutter·性能优化
yidahis16 小时前
Flutter 运行新建项目也报错?
flutter·trae
木马不在转16 小时前
Flutter-权限permission_handler插件配置
flutter
江上清风山间明月19 小时前
一周掌握Flutter开发--9. 与原生交互(下)
flutter·交互·原生·methodchannel
GeniuswongAir20 小时前
Flutter极速接入IM聊天功能并支持鸿蒙
flutter·华为·harmonyos
sayen20 小时前
记录 flutter 文本内容展示过长优化
前端·flutter
勤劳打代码20 小时前
剑拔弩张——焦点竞争引的发输入失效
flutter·客户端·设计
张风捷特烈1 天前
Flutter 伪 3D 绘制#02 | 地平面与透视
android·flutter