一、Android界面渲染流程UI樹與FlutterUI樹的設(shè)計(jì)思路對(duì)比
二、Widget組件生命周期詳解
1、Widget組件生命周期
和其他的視圖框架比如android的Activity一樣,flutter中的視圖Widget也存在生命周期,生命周期的回調(diào)函數(shù)體現(xiàn)在了State上面。組件State的生命周期整理如下圖所示:
createState:
當(dāng)一個(gè)StatefulWidget插入到渲染樹結(jié)構(gòu)、或者從渲染樹結(jié)構(gòu)移除時(shí),都會(huì)調(diào)用StatefulWidget.createState方法,從而達(dá)到更新UI的效果;
initState:
initState是StatefulWidget創(chuàng)建后調(diào)用的第一個(gè)方法,而且只執(zhí)行一次。在執(zhí)行initState時(shí),View沒(méi)有渲染,但是StatefulWidget 已經(jīng)被加載到渲染樹里了;
didChangeDependencies:
didChangeDependencies會(huì)在initState后立即調(diào)用,當(dāng)StatefulWidget依賴的InheritedWidget發(fā)生變化之后,didChangeDependencies會(huì)調(diào)用,所以didChangeDependencies可以調(diào)用多次;
build:
build方法會(huì)在didChangeDeoendencies之后立即調(diào)用,在之后setState()刷新時(shí),會(huì)重新調(diào)用build繪制頁(yè)面,所以build方法可以調(diào)用多次。一般不在build中創(chuàng)建除了創(chuàng)建Widget的方法,否則會(huì)影響渲染效率。
setState:
[State] 對(duì)象可以通過(guò)調(diào)用它們的 [setState]方法自發(fā)地請(qǐng)求重建其子樹,這表明它們的某些內(nèi)部狀態(tài)已經(jīng)改變,可能會(huì)影響該子樹中的用戶界面,setState方法會(huì)被多次調(diào)用。
didUpdateWidget:
1、當(dāng)調(diào)用setState更新UI的時(shí)候,都會(huì)調(diào)用didUpdateWidget;
2、框架在調(diào)用[didUpdateWidget]之后總是調(diào)用[build],在[didUpdateWidget]中對(duì)[setState]的任何調(diào)用都是多余的。
deactivate:
1、當(dāng)框架從樹中移除此 State 對(duì)象時(shí)將會(huì)調(diào)用此方法;
2、在某些情況下,框架將重新插入State 對(duì)象到樹的其他位置(例如,如果包含該樹的子樹State 對(duì)象從樹中的一個(gè)位置移植到另一位置),框架將會(huì)調(diào)用 build 方法來(lái)提供 State 對(duì)象適應(yīng)其在樹中的新位置。
dispose:
當(dāng)框架從樹中永久移除此 State對(duì)象時(shí)將會(huì)調(diào)用此方法,與deactivate的區(qū)別是,deactivate 還可以重新插入到樹中,而 dispose 表示此 State對(duì)象永遠(yuǎn)不會(huì)在 build。調(diào)用完 dispose后,mounted 屬性被設(shè)置為false,也代表組件生命周期的結(jié)束,此時(shí)再調(diào)用setState方法將會(huì)拋出異常。子類重寫此方法,釋放相關(guān)資源,比如動(dòng)畫等。
2、生命周期調(diào)用
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.
@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("頁(yè)面跳轉(zhuǎn)"),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
setState(() {
print("調(diào)用....");
});
},
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:
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頁(yè)面"),
),
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 = "測(cè)試";
}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");
}
}
三、頁(yè)面間數(shù)據(jù)傳遞(共享)的幾種常用方式
公共頁(yè)面:
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 = [
"構(gòu)造器傳遞數(shù)據(jù)",
"返回上個(gè)頁(yè)面時(shí)攜帶參數(shù)",
"InheritedWidget方式",
"Singleton方式",
"Singleton結(jié)合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;
}
}
//通過(guò)構(gòu)造器方法傳遞數(shù)據(jù)
_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、通過(guò)構(gòu)造器(constructor)傳遞數(shù)據(jù)
通過(guò)構(gòu)造器傳遞數(shù)據(jù)是一種最簡(jiǎn)單的方式,也是最常用的方式,在第一個(gè)頁(yè)面,我們模擬創(chuàng)建一個(gè)我們需要傳遞數(shù)據(jù)的對(duì)象。當(dāng)點(diǎn)擊跳轉(zhuǎn)的時(shí)候,我們把數(shù)據(jù)傳遞DataTransferByConstructorPage頁(yè)面,并把攜帶過(guò)來(lái)的數(shù)據(jù)展示到頁(yè)面上。
1)創(chuàng)建一個(gè)傳遞數(shù)據(jù)對(duì)象 :final data=TransferDataEntity("001","張三豐",18);
2)定義一個(gè)跳轉(zhuǎn)到DataTransferByConstructorPage頁(yè)面的方法:
3)在DataTransferByConstructorPage頁(yè)面接收到數(shù)據(jù)并展示出來(lái):
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';
///通過(guò)構(gòu)造器的方式傳遞參數(shù)
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("構(gòu)造器方式"),
),
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、當(dāng)一個(gè)頁(yè)面關(guān)閉時(shí)攜帶數(shù)據(jù)到上一個(gè)頁(yè)面(Navigator.pop)
在Android開(kāi)發(fā)中我們需要將數(shù)據(jù)傳遞給上一個(gè)頁(yè)面通常使用的傳統(tǒng)方式是startActivityForResult()方法。但是在flutter就不用這么麻煩了。只需要使用Navigator.pop方法即可將數(shù)據(jù)結(jié)果帶回去。但是我們跳轉(zhuǎn)的時(shí)候需要注意兩點(diǎn):
1)我們需要定義一個(gè)異步方法用于接收返回來(lái)的結(jié)果:
2)在我們要關(guān)閉的頁(yè)面使用Navigator.pop 返回第一個(gè)頁(yè)面:
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("返回上個(gè)頁(yè)面?zhèn)鬟f參數(shù)"),
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("點(diǎn)我返回上一個(gè)頁(yè)面并把數(shù)據(jù)傳回去"))
],
),
);
}
//返回并攜帶數(shù)據(jù)
_backToData(BuildContext context) {
var transferData = TransferDataEntity(name: "嘻嘻哈哈", id: "007", age: 20);
Navigator.pop(context, transferData);
}
}
3、InheritedWidget方式?
使用lnheritedWidget方式如下幾步:
1)繼承l(wèi)nheritedWidget提供一個(gè)數(shù)據(jù)源:
import 'package:flutter/material.dart';
import 'transfer_data_entity.dart';
//所有的子組件共享數(shù)據(jù)
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;
}
//版本問(wèn)題
//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)定義頁(yè)面跳轉(zhuǎn)時(shí)候攜帶數(shù)據(jù)的方法:
3)跳轉(zhuǎn)的到的頁(yè)面并展示數(shù)據(jù)代碼如下:
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方式傳遞數(shù)據(jù)"),
),
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、全局的提供數(shù)據(jù)的方式
這種方式我們還是使用lnheritedWidget,區(qū)別就是我們不是跳轉(zhuǎn)的時(shí)候去創(chuàng)建IGenericDataProvider。而是把他放在最頂層注意:這種方式一定要把數(shù)據(jù)放在頂層;
1)定義頂部數(shù)據(jù):
2)接收數(shù)據(jù)的方式基本和 lnheritedWidget相同:
final data=IGenericDataProvider.of<TransferDataEntity>(context) 獲取數(shù)據(jù)
3)使用代碼:
5、通過(guò)全局單例模式來(lái)使用?
這種方式就是創(chuàng)建一個(gè)全局單例對(duì)象,任何地方都可以操控這個(gè)對(duì)象,存儲(chǔ)和取值都可以通過(guò)這個(gè)對(duì)象。
1)創(chuàng)建單例對(duì)象:
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)給單例對(duì)象存放數(shù)據(jù):
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> {
@override
Widget build(BuildContext context) {
var data = transSingletonData.transData;
return Scaffold(
appBar: AppBar(
title: const Text("全局單例傳遞數(shù)據(jù)"),
),
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、全局單例結(jié)合Stream的方式傳遞數(shù)據(jù)
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)傳遞要攜帶的數(shù)據(jù):
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;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("全局單例結(jié)合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
我們平時(shí)一定接觸過(guò)很多的Widget,比如 Container、Row、Column等,它們?cè)谖覀兝L制界面的過(guò)程中發(fā)揮著重要的作用。但是不知道你有沒(méi)有注意到,在幾乎每個(gè)Widget的構(gòu)造函數(shù)中,都有一個(gè)共同的參數(shù),它們通常在參數(shù)列表的第一個(gè),那就是Key。
在Flutter中,Key是不能重復(fù)使用的,所以Key一般用來(lái)做唯一標(biāo)識(shí)。組件在更新的時(shí)候,其狀態(tài)的保存主要是通過(guò)判斷組件的類型或者key值是否一致。因此,當(dāng)各組件的類型不同的時(shí)候,類型已經(jīng)足夠用來(lái)區(qū)分不同的組件了,此時(shí)我們可以不必使用key。但是如果同時(shí)存在多個(gè)同一類型的控件的時(shí)候,此時(shí)類型已經(jīng)無(wú)法作為區(qū)分的條件了,我們就需要使用到key。?
1、沒(méi)有Key 會(huì)發(fā)生什么現(xiàn)象:
如下面例:定義了一個(gè)StatefulWidget的Box,點(diǎn)擊Box的時(shí)候可以改變Box里面的數(shù)字,當(dāng)我們重新對(duì)Box排序的時(shí)候,F(xiàn)lutter就無(wú)法識(shí)別到Box的變化了,這是什么原因呢?
運(yùn)行后我們發(fā)現(xiàn)改變list Widget順序后,Widget顏色會(huì)變化,但是每個(gè)Widget里面的文本內(nèi)容并沒(méi)有變化,為什么會(huì)這樣呢?當(dāng)我們List重新排序后Flutter檢測(cè)到了Widget的順序變化,所以重新繪制List Widget,但是Flutter發(fā)現(xiàn)List Widget里面的元素沒(méi)有變化,所以就沒(méi)有改變Widget里面的內(nèi)容。
把List 里面的Box的顏色改成一樣,這個(gè)時(shí)候您重新對(duì)list進(jìn)行排序,就很容易理解了。重新排序后雖然 執(zhí)行了setState,但Flutter沒(méi)法通過(guò)Box里面?zhèn)魅氲膮?shù)是代碼和以前是一樣的,所以Flutter不會(huì)重構(gòu)List Widget里面的內(nèi)容,也就是來(lái)識(shí)別Box是否改變。如果要讓FLutter能識(shí)別到List Widget子元素的改變,就需要給每個(gè)Box指定一個(gè)key。?
實(shí)現(xiàn)代碼:
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是不能重復(fù)使用的,所以Key一般用來(lái)做唯一標(biāo)識(shí)。組件在更新的時(shí)候,其狀態(tài)的保存主要是通過(guò)判斷組件的類型或者key值是否一致。因此,當(dāng)各組件的類型不同的時(shí)候,類型已經(jīng)足夠用來(lái)區(qū)分不同的組件了,此時(shí)我們可以不必使用key。但是如果同時(shí)存在多個(gè)同一類型的控件的時(shí)候,此時(shí)類型已經(jīng)無(wú)法作為區(qū)分的條件了,我們就需要使用到key。
?
運(yùn)用key變更上面案例業(yè)務(wù):
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(), //唯一值, 自動(dòng)生成
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----------構(gòu)造");
}
@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,當(dāng)屏幕狀態(tài)改變的時(shí)候把Colum換成了Row,Box的狀態(tài)就會(huì)丟失。一個(gè)Widget狀態(tài)的保存主要是通過(guò)判斷組件的類型或者key值是否一致。 LocalKey只在當(dāng)前的組件樹有效,所以把Colum換成了Row的時(shí)候Widget的狀態(tài)就丟失了。
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(), //程序自動(dòng)生成一個(gè)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"),
),
),
);
}
}
為了解決這個(gè)問(wèn)題我們就可以使用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.
@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(), //程序自動(dòng)生成一個(gè)key
// color: Colors.orange,
// )
// ];
List<Widget> list = [
Box(
key: GlobalKey(),
color: Colors.blue,
),
Box(
key: GlobalKey(),
color: Colors.red,
),
Box(
key: GlobalKey(), //程序自動(dòng)生成一個(gè)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 可以獲取子組件的狀態(tài),執(zhí)行子組件的方法,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.
@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、獲取子組件的狀態(tài) 調(diào)用子組件的屬性
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"),
),
),
);
}
}
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-744858.html文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-744858.html
?
到了這里,關(guān)于Flutter 05 組件狀態(tài)、生命周期、數(shù)據(jù)傳遞(共享)、Key的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!