在Hello world中,我们发现flutter的嵌套挺多的,其实Flutter整个开发过程中就是形成一个Widget树,所以形成嵌套是很正常的。我们可以对我们的代码进行封装,将它们封装到自己的Widget中,创建自己的Widget。
在 Flutter 中,⼀切的显示都是 Widget 。Widget 是⼀切的基础,作为响应式的渲染,类似 MVVM 的实现机制。
我们可以通过修改数据,再⽤ setState 设置数据,Flutter 会⾃动通过绑定的数据更新 Widget 。 所以你需要做的就是实现 Widget 界⾯,并且和数据绑定起来。
Widget 分为 有状态 和 ⽆状态 两种,在 Flutter 中每个⻚⾯都是⼀帧,⽆状态就是保持在那⼀帧,⽽有状态的 Widget 当数据更新时,其实是绘制了新的 Widget,只是 State 实现了跨帧的数据同步保 存。
总的来说就是,
StatelessWidget: 没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已。
StatefulWidget: 需要保存状态,并且可能出现状态改变的Widget。
如何创建自己的Widget呢?
在Flutter开发中,我们可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
1、⽆状态StatelessWidget
StatelessWidget通常是一些没有状态(State,也可以理解成data)需要维护的Widget:
它们的数据通常是直接写死(放在Widget中的数据,必须被定义为final)
我们来看一下创建一个StatelessWidget的格式:
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
}
}
build方法的解析:
Flutter在拿到我们自己创建的StatelessWidget时,就会执行它的build方法;
我们需要在build方法中告诉Flutter,我们的Widget希望渲染什么元素,比如一个Text Widget;
StatelessWidget没办法主动去执行build方法,当我们使用的数据发生改变时,build方法会被重新执行;
build方法什么情况下被执行呢?:
Widget 和 Widget 之间通过 child: 进⾏嵌套。其中有的 Widget 只能有⼀个 child,⽐如下⽅的Container ;有的 Widget 可以多个 child ,也就是 children: ,⽐如` Column 布局。下⽅代码便是 Container Widget 嵌套了 Text Widget。
main(List args) {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Widget"),
),
body: Center(
child: DEMOWidget('Hello World'),
),
),
)
);
}
class DEMOWidget extends StatelessWidget {
final String text;
//数据可以通过构造⽅法传递进来
DEMOWidget(this.text);
@override
Widget build(BuildContext context) {
//这⾥返回你需要的控件
//这⾥末尾有没有的逗号,对于格式化代码⽽已是不⼀样的。
return Container(
//⽩⾊背景
color: Colors.white,
//Dart语法中,?? 表示如果text为空,就返回尾号后的内容。
child: Text(text ?? "这就是⽆状态DMEO"),
);
}
}
2、有状态StatefulWidget
如下代码,是有状态的widget的简单实现。
你需要创建管理的是主要是 State , 通过 State 的 build ⽅法去构建控件。在 State 中,你可以动态改变数据,这类似 MVVM 实现,在 setState 之后,改变的数据会触发 Widget 重新构建刷 新。⽽下⽅代码中,是通过延两秒之后,让⽂本显示为 “这就变了数值”。
如下代码还可以看出,State 中主要的声明周期有 :
initState :初始化,理论上只有初始化⼀次。
didChangeDependencies:在 initState 之后调⽤,此时可以获取其他 State 。
dispose :销毁,只会调⽤⼀次。
看到没,Flutter 其实就是这么简单!你的关注点只要在:创建你的 StatelessWidget 或者StatefulWidget ⽽已。你需要的就是在 build 中堆积你的布局,然后把数据添加到 Widget 中, 最后通过 setState 改变数据,从⽽实现画⾯变化。
import 'package:flutter/material.dart';
import 'dart:async';
main(List args) {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Widget"),
),
body: Center(
child: DemoStateWidget('Hello World'),
),
),
)
);
}
class DemoStateWidget extends StatefulWidget { final String text;
通过构造⽅法传值
DemoStateWidget(this.text);
///主要是负责创建state @override
_DemoStateWidgetState createState() => _DemoStateWidgetState(text);
}
class _DemoStateWidgetState extends State {
String text;
_DemoStateWidgetState(this.text);
@override
void initState() {
///初始化,这个函数在⽣命周期中只调⽤⼀次super.initState();
///定时2秒
///
new Future.delayed(const Duration(seconds: 1), () {
setState(() {
text = "这就变了数值";
});
});
}
@override
void dispose() {
///销毁super.dispose();
}
@override
void didChangeDependencies() {
///在initState之后调 Called when a dependency of this [State] object changes. super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Container(
child: Text(text ?? "这就是有状态DMEO"),
);
}
}
3、StatefulWidget生命周期
3.1. 生命周期的理解
什么是生命周期呢?
客户端开发:iOS开发中我们需要知道UIViewController从创建到销毁的整个过程,Android开发中我们需要知道Activity从创建到销毁的整个过程。以便在不同的生命周期方法中完成不同的操作; 前端开发中:Vue、React开发中组件也都有自己的生命周期,在不同的生命周期中我们可以做不同的操作;
Flutter小部件的生命周期:
StatelessWidget可以由父Widget直接传入值,调用build方法来构建,整个过程非常简单;
而StatefulWidget需要通过State来管理其数据,并且还要监控状态的改变决定是否重新build整个Widget;
所以,我们主要讨论StatefulWidget的生命周期,也就是它从创建到销毁的整个过程;
StatefulWidget本身由两个类组成的:StatefulWidget和State。
首先,执行StatefulWidget中相关的方法:
具体通过代码
import 'package:flutter/material.dart';
main(List args) {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("HelloWorld"),
),
body: HomeBody(),
),
);
}
}
class HomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("HomeBody build");
return MyCounterWidget();
}
}
class MyCounterWidget extends StatefulWidget {
MyCounterWidget() {
print("执行了MyCounterWidget的构造方法");
}
@override
State createState() {
print("执行了MyCounterWidget的createState方法");
// 将创建的State返回
return MyCounterState();
}
}
class MyCounterState extends State {
int counter = 0;
MyCounterState() {
print("执行MyCounterState的构造方法");
}
@override
void initState() {
super.initState();
print("执行MyCounterState的init方法");
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("执行MyCounterState的didChangeDependencies方法");
}
@override
Widget build(BuildContext context) {
print("执行执行MyCounterState的build方法");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
color: Colors.redAccent,
child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
onPressed: () {
setState(() {
counter++;
});
},
),
RaisedButton(
color: Colors.orangeAccent,
child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
onPressed: () {
setState(() {
counter--;
});
},
)
],
),
Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
],
),
);
}
@override
void didUpdateWidget(MyCounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("执行MyCounterState的didUpdateWidget方法");
}
@override
void dispose() {
super.dispose();
print("执行MyCounterState的dispose方法");
}
}
执行结果:
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行了MyCounterWidget的createState方法
flutter: 执行MyCounterState的构造方法
flutter: 执行MyCounterState的init方法
flutter: 执行MyCounterState的didChangeDependencies方法
flutter: 执行执行MyCounterState的build方法
// 注意:Flutter会build所有的组件两次(查了GitHub、Stack Overflow,目前没查到原因)
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行MyCounterState的didUpdateWidget方法
flutter: 执行执行MyCounterState的build方法
当我们改变状态,手动执行setState方法后会打印如下结果:
flutter: 执行执行MyCounterState的build方法