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、生命周期调用
A Page:
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.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const Demo(),);}
}class Demo extends StatefulWidget {const Demo({super.key});@overrideState<Demo> createState() => _DemoState();
}class _DemoState extends State<Demo> {@overrideWidget 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.);}@overridevoid initState() {// TODO: implement initStatesuper.initState();print("------main--------initState");}@overridevoid didChangeDependencies() {// TODO: implement didChangeDependenciessuper.didChangeDependencies();print("-------main-------didChangeDependencies");}@overridevoid setState(VoidCallback fn) {// TODO: implement setStatesuper.setState(fn);print("------main--------setState");}@overridevoid deactivate() {// TODO: implement deactivatesuper.deactivate();print("------main--------deactivate");}@overridevoid dispose() {// TODO: implement disposesuper.dispose();print("------main--------dispose");}
}
B Page:
import 'package:flutter/material.dart';class DemoPage extends StatelessWidget {const DemoPage({super.key}) ;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("Demo页面"),),body: const DemoWidget(),);}
}class DemoWidget extends StatefulWidget {const DemoWidget({super.key});@overrideState<DemoWidget> createState() => _DemoWidgetState();
}class _DemoWidgetState extends State<DemoWidget> {String btnText = "test";@overrideWidget 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("返回"),),],),);}@overridevoid initState() {// TODO: implement initStatesuper.initState();print("------_DemoWidgetState--------initState");}@overridevoid didChangeDependencies() {// TODO: implement didChangeDependenciessuper.didChangeDependencies();print("-------_DemoWidgetState-------didChangeDependencies");}@overridevoid setState(VoidCallback fn) {// TODO: implement setStatesuper.setState(fn);print("------_DemoWidgetState--------setState");}@overridevoid deactivate() {// TODO: implement deactivatesuper.deactivate();print("------_DemoWidgetState--------deactivate");}@overridevoid dispose() {// TODO: implement disposesuper.dispose();print("-----_DemoWidgetState---------dispose");}
}
三、页面间数据传递(共享)的几种常用方式
公共页面:
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);@overrideWidget 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",];@overrideWidget 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页面接收到数据并展示出来:
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';///通过构造器的方式传递参数
class DataTransferByConstructorPage extends StatelessWidget {final TransferDataEntity data;const DataTransferByConstructorPage({super.key, required this.data});@overrideWidget 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 返回第一个页面:
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';class TransferRouterPage extends StatelessWidget {final TransferDataEntity data;const TransferRouterPage({super.key, required this.data});@overrideWidget 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提供一个数据源:
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});@overridebool updateShouldNotify(IDataProvider oldWidget) {return data != oldWidget.data;}//版本问题//InheritedWidgetstatic 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)跳转的到的页面并展示数据代码如下:
import 'package:flutter/material.dart';import 'inherited_data_provider.dart';class IDataWidget extends StatelessWidget {const IDataWidget({super.key});@overrideWidget 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});@overrideWidget 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)创建单例对象:
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)接收并使用传递的值:
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> {@overrideWidget 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)单例模式:
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)接收要传递的值:
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;@overrideWidget 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}"),),],);}));}@overridevoid 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。
实现代码:
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.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<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,)];@overrideWidget 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});@overrideState<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;@overrideWidget 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变更上面案例业务:
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.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<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),//类似于ValueKeycolor: Colors.orange,)];@overrideWidget 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});@overrideState<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;_BoxState() {print("--------_BoxState----------构造");}@overrideWidget 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"),),),);}@overridevoid initState() {// TODO: implement initStatesuper.initState();print("------_BoxState--------initState");}@overridevoid didChangeDependencies() {// TODO: implement didChangeDependenciessuper.didChangeDependencies();print("-------_BoxState-------didChangeDependencies");}@overridevoid setState(VoidCallback fn) {// TODO: implement setStatesuper.setState(fn);print("------_BoxState--------setState");}@overridevoid deactivate() {// TODO: implement deactivatesuper.deactivate();print("------_BoxState--------deactivate");}@overridevoid dispose() {// TODO: implement disposesuper.dispose();print("------_BoxState--------dispose");}
}
3、GlobalKey的使用
如果把LocalKey比作局部变量,GlobalKey就类似于全局变量;
下面使用了LocalKey,当屏幕状态改变的时候把Colum换成了Row,Box的状态就会丢失。一个Widget状态的保存主要是通过判断组件的类型或者key值是否一致。 LocalKey只在当前的组件树有效,所以把Colum换成了Row的时候Widget的状态就丢失了。
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.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {List<Widget> list = [Box(key: const ValueKey(1), //int double stringcolor: Colors.blue,),Box(key: ObjectKey(Object()),color: Colors.red,),Box(key: UniqueKey(), //程序自动生成一个keycolor: Colors.orange,)];@overrideWidget 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});@overrideState<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;@overrideWidget 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。
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.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<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(), //程序自动生成一个keycolor: Colors.orange,)];@overrideWidget 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});@overrideState<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;@overrideWidget 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()可以获取渲染的属性。
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.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}
}class HomePage extends StatefulWidget {const HomePage({super.key});@overrideState<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {final GlobalKey _globalKey = GlobalKey();@overrideWidget 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});@overrideState<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;run() {print("run");}@overrideWidget 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"),),),);}
}
相关文章:

Flutter 05 组件状态、生命周期、数据传递(共享)、Key
一、Android界面渲染流程UI树与FlutterUI树的设计思路对比 二、Widget组件生命周期详解 1、Widget组件生命周期 和其他的视图框架比如android的Activity一样,flutter中的视图Widget也存在生命周期,生命周期的回调函数体现在了State上面。组件State的生命…...
2.Vue3项目(二):vue项目创建,项目必需的基础依赖配置,项目集成各种第三方依赖
目录 一、环境配置 1.下载node.js 2.pnpm的配置 二、创建项目 1.先创建好项目文件夹...
【Mybatis源码】注册器 - TypeAliasRegistry
Mybatis中使用TypeAliasRegistry注册器用于管理类型与别名,Mybatis中许多功能的实现都需要从TypeAliasRegistry注册器中找到别名对应的类型,本篇我们介绍一下TypeAliasRegistry注册器的原理与使用 一、构造方法 TypeAliasRegistry注册器类提供了一个无参数的构造方法用于创…...

【wp】2023鹏城杯初赛 Web web1(反序列化漏洞)
考点: 常规的PHP反序列化漏洞双写绕过waf 签到题 源码: <?php show_source(__FILE__); error_reporting(0); class Hacker{private $exp;private $cmd;public function __toString(){call_user_func(system, "cat /flag");} }class A {p…...

三顾茅庐,七面阿里,成功上岸25k16薪,我行你也行~
写在片头:声明,勿杠 首先简单说一下,这三次面试阿里并不是一次性去面的,实际上第一次面试时候还在大四,找的实习岗,不太清楚是什么部门,别问我为什么还记得面试题,有记录和复盘的习…...

儿童听力损伤了,家长怎么办?
很多家长对儿童听力损伤问题存在较大误区,认为儿童除了先天性耳聋以外不会有听力问题。家长总认为孩子上课或做事不专心是因为注意力不集中、多动等问题所致,大部分家长没有意识到孩子可能出现了听力损伤问题。 儿童听力损伤主要是指因各种原因导致双耳不…...

【实验记录】为了混毕业·读读论文叭
PR曲线 1. Robust_Place_Recognition_using_an_Imaging_Lidar 在第三节方法中,提到了一些列处理步骤,分析来与vins相似,在vins中是关键帧检索、特征提取、DBoW查询、描述子匹配、PnP RANSAC求解。 第四节的实验部分,没有绘制pr…...
asr翱捷LORA系列芯片选型参考推荐ASR6601/asr6505/asr6501/asr6500
ASR6601 SoC是国内首颗支持LoRa的LPWAN SoC。ASR6601芯片中集成的超低功耗收发机,除了支持LoRa调制方式外,还可以支持FSK收发、MSK收发和BPSK发射等。在3.3V电源供电的情况下,通过高功率PA,最大可发射22dBM的输出功率。ASR6601与A…...

Prometheus+Node_exporter+Grafana实现监控主机
PrometheusNode_exporterGrafana实现监控主机 如果没有安装相关的配置,首先要进行安装配置,环境是基于Linux,虚拟机的相关环境配置在文末给出,现在先讲解PrometheusNode_exporterGrafana的安装和使用。 一.Prometheus安装 虽然…...
odoo启动-加载模块(load_modules)
odoo启动-加载模块(load_modules) odoo每次启动的时候都会加载模块,加载模块的过程就是调用load_modules 函数 在函数位于 odoo\modules\loading.py 代码中注释也写的很清楚,共分了9个步骤,其实是8个步骤。 这个函…...

【入门Flink】- 02Flink经典案例-WordCount
WordCount 需求:统计一段文字中,每个单词出现的频次 添加依赖 <properties><flink.version>1.17.0</flink.version></properties><dependencies><dependency><groupId>org.apache.flink</groupId><…...
go语言将cmd stdout和stderr作为字符串返回而不是打印到控制台
go语言将cmd stdout和stderr作为字符串返回而不是打印到控制台 1、直接打印到控制台 从 golang 应用程序中执行 bash 命令,现在 stdout 和 stderr 直接进入控制台: cmd.Stdout os.Stdout cmd.Stderr os.Stderrpackage mainimport ("fmt"…...

OpenGL ES入门教程(二)之绘制一个平面桌子
OpenGL ES入门教程(二)之绘制一个平面桌子 前言0. OpenGL绘制图形的整体框架概述1. 定义顶点2. 定义着色器3. 加载着色器4. 编译着色器5. 将着色器链接为OpenGL程序对象6. 将着色器需要的数据与拷贝到本地的数组相关联7. 在屏幕上绘制图形8. 让桌子有边框…...

el-select 搜索无选项时 请求接口添加输入的值
el-select 搜索无选项时 请求接口添加输入的值 <template><div class"flex"><el-select class"w250" v-model"state.brand.id" placeholder"请选择" clearable filterable :filter-method"handleQu…...

基于单片机的商场防盗防火系统设计
收藏和点赞,您的关注是我创作的动力 文章目录 概要 一、系统分析二、系统总设计2.1基于单片机的商场防火防盗系统的总体功能2.2系统的组成 三 软件设计4.1软件设计思路4.2软件的实现4.2.1主控模块实物 四、 结论五、 文章目录 概要 本课题设计一种商场防火防盗报警…...

【Java|golang】2103. 环和杆---位运算
总计有 n 个环,环的颜色可以是红、绿、蓝中的一种。这些环分别穿在 10 根编号为 0 到 9 的杆上。 给你一个长度为 2n 的字符串 rings ,表示这 n 个环在杆上的分布。rings 中每两个字符形成一个 颜色位置对 ,用于描述每个环: 第 …...

[SSD综述 1.4] SSD固态硬盘的架构和功能导论
依公知及经验整理,原创保护,禁止转载。 专栏 《SSD入门到精通系列》 <<<< 返回总目录 <<<< 前言 机械硬盘的存储系统由于内部结构, 其IO访问性能无法进一步提高,CPU与存储器之间的性能差距逐渐扩大。以Nand Flash为存储介质的固态硬盘技术的发展,…...

【C++那些事儿】类与对象(1)
君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们,这里是君兮_,我之前看过一套书叫做《明朝那些事儿》,把本来枯燥的历史讲的生动有趣。而C作为一门接近底层的语言,无疑是抽象且难度颇…...

集简云x slack(自建)无需API开发轻松连接OA、电商、营销、CRM、用户运营、推广、客服等近千款系统
slack是一个工作效率管理平台,让每个人都能够使用无代码自动化和 AI 功能,还可以无缝连接搜索和知识共享,并确保团队保持联系和参与。在世界各地,Slack 不仅受到公司的信任,同时也是人们偏好使用的平台。 官网&#x…...
JS模块化,ESM模块规范的 导入、导出、引用、调用详解
JS模块化,ESM模块规范的 导入、导出、引用、调用详解 写在前面实例代码1、模块导出 - export导出之 - 独立导出导出之 - 集中多个导出导出之 - 默认导出导出之 - 集中默认导出导出之 - 混合导出 2、模块导入 - import导入之 - 全部导入导入之 - 默认导入导入之 - 指…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
板凳-------Mysql cookbook学习 (十--2)
5.12 模式匹配中的大小写问题 mysql> use cookbook Database changed mysql> select a like A, a regexp A; ------------------------------ | a like A | a regexp A | ------------------------------ | 1 | 1 | --------------------------…...