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"),),),);}
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/161542.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA创建Springboot多模块项目

一、创建父模块 File --> New --> Project &#xff0c;选择 “ Spring Initalizr ” &#xff0c;点击 Next Next Next --> Finish 二、创建子模块 右键根目录&#xff0c;New --> Module 选择 “ Spring Initializr ”&#xff0c;点击Next 此处注意T…

IDEA集成Docker插件打包服务镜像与运行【附Docker命令汇总】

Docker官网 Docker官网&#xff1a;https://www.docker.com/ Docker Hub官网&#xff1a;http://hub.docker.com/ 什么是Docker Docker 是一个开源的容器引擎&#xff0c;可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者和系统管理员在笔记本上编…

D-link未授权访问以及远程代码执行

随便输入一个错误密码&#xff0c;会跳转到页面&#xff1a; /page/login/login.html?errorfail继续访问有效页面漏洞url&#xff1a; /Admin.shtml然后访问管理页面去更改管理密码 直接构造payload访问漏洞url&#xff1a; /cgi-bin/execute_cmd.cgi?cmdid执行命令&#…

JAVA 实现PDF转图片(spire.pdf.free版)

1.引入jar包 导入方法1&#xff1a; 手动引入。将Free Spire.PDF for Java下载到本地&#xff0c;解压&#xff0c;找到lib文件夹下的Spire.PDF.jar文件。在IDEA中打开如下界面&#xff0c;将本地路径中的jar文件引入Java程序&#xff1a; 导入方法2&#xff1a;如果您想通过…

linux 创建git项目并提交到gitee(保姆式教程)

01、git安装与初始化设置 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ apt install mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.name "用户名" mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.ema…

python脚本监听域名证书过期时间,并将通知消息到钉钉

版本一&#xff1a; 执行脚本带上 --dingtalk-webhook和–domains后指定钉钉token和域名 python3 ssl_spirtime.py --dingtalk-webhook https://oapi.dingtalk.com/robot/send?access_tokenavd345324 --domains www.abc1.com www.abc2.com www.abc3.com脚本如下 #!/usr/bin…

Java配置47-Spring Eureka 未授权访问漏洞修复

文章目录 1. 背景2. 方法2.1 Eureka Server 添加安全组件2.2 Eureka Server 添加参数2.3 重启 Eureka Server2.4 Eureka Server 升级版本2.5 Eureka Client 配置2.6 Eureka Server 添加代码2.7 其他问题 1. 背景 项目组使用的 Spring Boot 比较老&#xff0c;是 1.5.4.RELEASE…

D-Link管理系统默认账号密码

默认口令为 admin:admin 登陆成功 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或利用此文所提供的信息、技术或方法而造成的任何直接或间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c; 文章…

贝叶斯网络:利用变量消除(Variable Elimination)进行推理

贝叶斯网络简介 贝叶斯网络(Bayesian network)也叫贝氏网路、信念网络&#xff08;belief network&#xff09;或是有向无环图&#xff08;DAG&#xff09;模型&#xff0c;是一种概率图模型。它利用DAG的结构&#xff0c;得到一组随机变量{X1,X2,...,Xn}的条件概率分布&#…

matlab双目标定中基线物理长度获取

在MATLAB进行双目摄像机标定时,通常会获得相机的内参,其中包括像素单位的焦距(focal length)以及物理单位的基线长度(baseline)。对于应用中的深度估计和测量,基线长度的物理单位非常重要,因为它直接影响到深度信息的准确性。有时候,您可能只能获取像素单位的焦距和棋…

如何使用CodeceptJS、Playwright和GitHub Actions构建端到端测试流水线

介绍 端到端测试是软件开发的一个重要方面&#xff0c;因为它确保系统的所有组件都能正确运行。CodeceptJS是一个高效且强大的端到端自动化框架&#xff0c;与Playwright 结合使用时&#xff0c;它成为自动化Web、移动甚至桌面 (Electron.js) 应用程序比较好用的工具。 在本文中…

JavaScript的作用域和作用域链

作用域 ● 作用域&#xff08;Scoping&#xff09;&#xff1a;我们程序中变量的组织和访问方式。"变量存在在哪里&#xff1f;“或者"我们可以在哪里访问某个变量&#xff0c;以及在哪里不能访问&#xff1f;” ● 词法作用域&#xff08;Lexical scoping&#xff…