计数器执行流程:当右下角的floatingActionButton按钮被点击之后,会调用_incrementCounter方法。在_incrementCounter方法中,首先会自增_counter计数器(状态),然后setState会通知 Flutter 框架状态发生变化,接着,Flutter 框架会调用build方法以新的状态重新构建UI,最终显示在设备屏幕上。
Flutter 中是通过 Widget 嵌套 Widget 的方式来构建UI和进行实践处理的,所以记住,Flutter 中万物皆为Widget。
import 'package:flutter/material.dart'; //导包
void main() {
runApp(const MyApp()); //应用入口
}
/// 代表 Flutter 应用
class MyApp extends StatelessWidget {
const MyApp({
Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
//应用名称
title: 'Flutter Demo',
theme: ThemeData(
//蓝色主题
primarySwatch: Colors.blue,
),
//应用首页路由
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// 应用的首页
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
/// MyHomePage类对应的状态类
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
//当按钮点击时,会调用此函数,该函数的作用是先自增_counter,然后调用setState 方法。
// setState方法的作用是通知 Flutter 框架,有状态发生了改变,
// Flutter 框架收到通知后,会执行 build 方法来根据新的状态重新构建界面
void _incrementCounter() {
setState(() {
_counter++;
});
}
// 构建UI界面的逻辑
@override
Widget build(BuildContext context) {
// 1、Scaffold 是 Material 库中提供的页面脚手架,它提供了默认的导航栏、标题和包含主屏幕 widget 树(后同“组件树”或“部件树”)的body属性,组件树可以很复杂。
// 2、body的组件树中包含了一个Center 组件,Center 可以将其子组件树对齐到屏幕中心。
// 3、
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
// Center 子组件是一个Column 组件,Column的作用是将其所有子组件沿屏幕垂直方向依次排列;
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
// floatingActionButton是页面右下角的带“+”的悬浮按钮,它的onPressed属性接受一个回调函数,代表它被点击后的处理器,
// 本例中直接将_incrementCounter方法作为其处理函数。
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Widget 的部分源码。
@immutable // 不可变的
abstract class Widget extends DiagnosticableTree {
const Widget({
this.key });
final Key? key;
@protected
@factory
Element createElement();
@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
@override
@nonVirtual
bool operator ==(Object other) => super == other;
@override
@nonVirtual
int get hashCode => super.hashCode;
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
...
}
Flutter 中有三棵树:Widget 树,Element 树和 RenderObject 树。当应用启动时 Flutter 会遍历并创建所有的 Widget 形成 Widget Tree,同时与 Widget Tree 相对应,通过调用 Widget 上的 createElement() 方法创建每个 Element 对象,形成 Element Tree。最后调用 Element 的 createRenderObject() 方法创建每个渲染对象,形成一个 Render Tree。 Element就是Widget在UI树具体位置的一个实例化对象,大多数Element只有唯一的renderObject,但还有一些Element会有多个子节点,如继承自RenderObjectElement的一些类,比如 MultiChildRenderObjectElement。最终所有 Elemen t的RenderObject 构成一棵树,我们称之为”Render Tree“即”渲染树“。总结一下,我们可以认为 Flutter 的UI 系统包含三棵树:Widget 树、Element 树、渲染树。他们的依赖关系是:根据 Widget 树生成Element 树,再依赖于 Element 树生成 RenderObject 树。
在 flutter 中,Container、Text 等组件都属于 Widget,所以这课树就是 Widget 树,也可以叫做控件树,它就表示了我们在 dart 代码中所写的控件的结构。Element 就是 Widget 的另一种抽象。我们在代码中使用的像 Container、Text 等这类组件和其属性只不过是我们想要构建的组件的配置信息,当我们第一次调用 build()`方法想要在屏幕上显示这些组件时,Flutter 会根据这些信息生成该 Widget 控件对应的 Element,同样地,Element 也会被放到相应的 Element 树当中。RenderObject 在 Flutter 当中做组件布局渲染的工作,其为了组件间的渲染搭配及布局约束也有对应的 RenderObject 树,我们也称之为渲染树。
作者:邱穆
链接:https://www.jianshu.com/p/e2c2ea310bdc
在 Flutter 中,Widget 分为两类:Stateless(无状态)和 Stateful(有状态)Widget。StatelessWidget 没有内部状态,Icon、IconButton, 和 Text 都是无状态 Widget, 它们都是 StatelessWidget 的子类。StatefulWidget 是动态的,用户可以和其交互(例如输入一个表单、 或者移动一个 slider 滑块),或者可以随时间改变 (也许是数据改变导致的 UI 更新)。Checkbox, Radio, Slider, InkWell, Form, and TextField 都是 Stateless widgets, 它们都是 StatefulWidget 的子类。
StatelessWidget
StatelessWidget 是不可变的, 这意味着它们的属性不能改变——所有的值都是最终的。如果无状态Widget 里面有子 Widget,并且子 Widget 是有状态的,则子 Widget 的内容是可以通过 setState 来更改的。无状态 Widget 影响的仅仅是自己是无状态的,不会影响他的父 Widget 和子 Widget。
StatefulWidget
StatefulWidget 持有的状态可能在 Widget 生命周期中发生变化。
State
一个 StatefulWidget 类会对应一个 State 类,State 表示与其对应的 StatefulWidget 要维护的状态,State 中的保存的状态信息可以:
State 中有两个常用属性:
Widget 生命周期主要分为 StatefullWidget 的生命周期和 StatelessWidget 的生命周期,StatelessWidget 的生命周期比较简单,它只有一个 build 方法,我们在这儿不做过多关注,以下主要分析 StatefullWidget 的生命周期。
仍然以计数器功能为例,实现一个计数器 CounterWidget 组件 ,点击它可以使计数器加1,由于要保存计数器的数值状态,所以我们应继承 StatefulWidget。
import 'package:flutter/material.dart'; //导包
void main() {
runApp(const MyApp()); //应用入口
}
/// 代表 Flutter 应用
class MyApp extends StatelessWidget {
const MyApp({
Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: StateLifecycleTest(),
);
}
}
/// 路由
class StateLifecycleTest extends StatelessWidget {
const StateLifecycleTest({
Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const CounterWidget();
}
}
/// 计数器组件
class CounterWidget extends StatefulWidget {
const CounterWidget({
Key? key, this.initValue = 0});
final int initValue;
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
/// State 代码
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
@override
void initState() {
super.initState();
//初始化状态
_counter = widget.initValue;
print("initState");
}
@override
Widget build(BuildContext context) {
print("build");
return Scaffold(
body: Center(
child: TextButton(
child: Text('$_counter'),
//点击后计数器自增
onPressed: () => setState(
() => ++_counter,
),
),
),
);
}
@override
void didUpdateWidget(CounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget ");
}
@override
void deactivate() {
super.deactivate();
print("deactivate");
}
@override
void dispose() {
super.dispose();
print("dispose");
}
@override
void reassemble() {
super.reassemble();
print("reassemble");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies");
}
}
监听 App 生命周期
1、组合 WidgetsBindingObserver 类。
class _CounterWidgetState extends State<CounterWidget> with WidgetsBindingObserver
2、在 initState 中添加监听。
WidgetsBinding.instance.addObserver(this);
3、在 dispose 中添加监听。
WidgetsBinding.instance.removeObserver(this);
4、State 中实现状态监听。
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
// The application is not currently visible to the user, not responding to
// user input, and running in the background.
// 不可见,不可操作
}
if (state == AppLifecycleState.resumed) {
// The application is visible and responding to user input.
// 可见,可操作
}
if (state == AppLifecycleState.inactive) {
// The application is in an inactive state and is not receiving user input.
// 可见,不可操作
}
if (state == AppLifecycleState.detached) {
// The application is still hosted on a flutter engine but is detached from any host views.
// 虽然还在运行,但已经没有任何存在的界面。
}
}
使用场景:
场景 1:前台转后台:
场景 2:后台转前台:
生命周期方法说明
使用场景
场景 1:打开页面:
场景 2:退出页面:
场景 3:热重载:
场景 4:横竖屏切换:
————————————————
原文链接:https://blog.csdn.net/haha223545/article/details/105483176
有两种方法在子 widget 树中获取父级 StatefulWidget 的State 对象。
通过 Context 获取
一般来说,如果 StatefulWidget 的状态是私有的(不应该向外部暴露),那么我们代码中就不应该去直接获取其 State 对象;如果 StatefulWidget 的状态是希望暴露出的(通常还有一些组件的操作方法),我们则可以去直接获取其 State 对象。但是通过 context.findAncestorStateOfType 获取 StatefulWidget 的状态的方法是通用的,我们并不能在语法层面指定 StatefulWidget 的状态是否私有,所以在 Flutter 开发中便有了一个默认的约定:如果 StatefulWidget 的状态是希望暴露出的,应当在 StatefulWidget 中提供一个 of 静态方法来获取其 State 对象,开发者便可直接通过该方法来获取;如果 State不希望暴露,则不提供 of 方法。这个约定在 Flutter SDK 里随处可见。
import 'package:flutter/material.dart'; //导包
void main() {
runApp(const MyApp()); //应用入口
}
/// 代表 Flutter 应用
class MyApp extends StatelessWidget {
const MyApp({
Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
//应用首页路由
home: GetStateObjectRoute(),
);
}
}
class GetStateObjectRoute extends StatefulWidget {
const GetStateObjectRoute({
Key? key}) : super(key: key);
@override
State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}
class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("子树中获取State对象"),
),
body: Center(
child: Column(
children: [
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 查找父级最近的Scaffold对应的ScaffoldState对象
ScaffoldState _state =
context.findAncestorStateOfType<ScaffoldState>()!;
// 打开抽屉菜单
_state.openDrawer();
},
child: Text('打开抽屉菜单1'),
);
}),
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 直接通过of静态方法来获取ScaffoldState
ScaffoldState _state = Scaffold.of(context);
// 打开抽屉菜单
_state.openDrawer();
},
child: Text('打开抽屉菜单2'),
);
}),
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("我是SnackBar")),
);
},
child: Text('显示SnackBar'),
);
}),
],
),
),
drawer: Drawer(),
);
}
}
通过 GlobalKey 获取
GlobalKey 是 Flutter 提供的一种在整个 App 中引用 element 的机制。如果一个 widget 设置了GlobalKey,那么我们便可以通过 globalKey.currentWidget 获得该 widget 对象、globalKey.currentElement 来获得 widget 对应的 element 对象,如果当前 widget 是 StatefulWidget,则可以通过 globalKey.currentState 来获得该 widget 对应的state对象。
注意:使用 GlobalKey 开销较大,如果有其他可选方案,应尽量避免使用它。另外,同一个 GlobalKey 在整个 widget 树中必须是唯一的,不能重复。
import 'package:flutter/material.dart'; //导包
void main() {
runApp(const MyApp()); //应用入口
}
/// 代表 Flutter 应用
class MyApp extends StatelessWidget {
const MyApp({
Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
//应用首页路由
home: GetStateObjectRoute(),
);
}
}
class GetStateObjectRoute extends StatefulWidget {
const GetStateObjectRoute({
Key? key}) : super(key: key);
@override
State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}
class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
//定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
static GlobalKey<ScaffoldState> _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
key: _globalKey, //设置key
appBar: AppBar(
title: Text("子树中获取State对象"),
),
body: Center(
child: Column(
children: [
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
//通过GlobalKey来获取State对象
_globalKey.currentState?.openDrawer();
},
child: Text('打开抽屉菜单1'),
);
}),
],
),
),
drawer: Drawer(),
);
}
}
StatelessWidget 和 StatefulWidget 都是用于组合其它组件的,它们本身没有对应的 RenderObject。Flutter 组件库中的很多基础组件都不是通过 StatelessWidget 和 StatefulWidget 来实现的,比如 Text 、Column、Align 等。
如果组件不会包含子组件,则我们可以直接继承自 LeafRenderObjectWidget ,它是 RenderObjectWidget 的子类,而 RenderObjectWidget 继承自 Widget 。
class CustomWidget extends LeafRenderObjectWidget{
@override
RenderObject createRenderObject(BuildContext context) {
// 创建 RenderObject
return RenderCustomObject();
}
@override
void updateRenderObject(BuildContext context, RenderCustomObject renderObject) {
// 更新 RenderObject
super.updateRenderObject(context, renderObject);
}
}
class RenderCustomObject extends RenderBox{
@override
void performLayout() {
// 实现布局逻辑
}
@override
void paint(PaintingContext context, Offset offset) {
// 实现绘制
}
}
如果自定义的 widget 可以包含子组件,则可以根据子组件的数量来选择继承SingleChildRenderObjectWidget 或 MultiChildRenderObjectWidget。通常,我们的 Widget 可以继承自以下三种类
Flutter 提供了一套丰富、强大的基础组件,在基础组件库之上 Flutter 又提供了一套 Material 风格( Android 默认的视觉风格)和一套 Cupertino 风格(iOS视觉风格)的组件库。要使用基础组件库,需要先导入:
import 'package:flutter/widgets.dart';
基础组件
Material 组件
Flutter 提供了一套丰富 的 Material 组件,它可以帮助我们构建遵循 Material Design 设计规范的应用程序。Material 应用程序以 MaterialApp (opens new window) 组件开始, 该组件在应用程序的根部创建了一些必要的组件,比如 Theme 组件,它用于配置应用的主题。 是否使用 MaterialApp (opens new window)完全是可选的,但是使用它是一个很好的做法。要使用 Material 组件,需要先引入它:
import 'package:flutter/material.dart';
Cupertino 组件
Flutter 也提供了一套丰富的 Cupertino 风格的组件,尽管目前还没有 Material 组件那么丰富,但是它仍在不断的完善中。值得一提的是在 Material 组件库中有一些组件可以根据实际运行平台来切换表现风格,比如 MaterialPageRoute,在路由切换时,如果是 Android 系统,它将会使用 Android 系统默认的页面切换动画(从底向上);如果是 iOS 系统,它会使用 iOS 系统默认的页面切换动画(从右向左)。