Flutter学习笔记(第二天)

本文详细比较了Flutter中StatelessWidget与StatefulWidget的区别,介绍了组件的生命周期方法如initState、didChangeDependencies等,以及如何在widget树中获取State对象,最后讨论了状态管理的不同层次。
摘要由CSDN通过智能技术生成

1.StatelessWidget与StatefulWidget

在 Flutter 中,StatelessWidget 和 StatefulWidget 是两种用于构建用户界面的基本组件类型,它们有以下区别:

StatelessWidget:StatelessWidget 是一个无状态的组件,意味着它的属性(inputs)一旦被设置,就不会发生改变。StatelessWidget 通常用于构建静态内容不会随时间变化的界面部分。例如,如果你有一个固定的文本或图标,可以使用 StatelessWidget 来表示它,因为它们不需要响应用户交互或动态更新。
StatefulWidget:StatefulWidget 是一个有状态的组件,它可以持有一些状态数据。StatefulWidget 具有一个与之关联的可变状态对象(State),可以根据状态的变化来更新界面。当状态数据发生变化时,Flutter 会调用 build() 方法重新构建界面,以反映最新的状态。StatefulWidget 适用于需要处理用户输入、展示动态数据或实现交互逻辑的场景。

2.Flutter中的四棵树

Flutter 框架的处理流程是这样的:
1.根据Widget树生成一个 Element 树,Element 树中的节点都继承自 Element 类。
2.根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自RenderObject 类。
3.根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自 Layer 类。
真正的布局和渲染逻辑在 Render 树中,Element 是 Widget 和 RenderObject 的粘合剂,可以理解为一个中间代理。

3.State生命周期

  • initState:当 widget 第一次插入到 widget 树时会被调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。不能在该回调中调用BuildContext.dependOnInheritedWidgetOfExactType(该方法用于在 widget 树上获取离当前 widget 最近的一个父级InheritedWidget,关于InheritedWidget我们将在后面章节介绍),原因是在初始化完成后, widget 树中的InheritFrom widget也可能会发生变化,所以正确的做法应该在在build()方法或didChangeDependencies()中调用它。

  • didChangeDependencies():当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget (第七章介绍),然后在之后的build() 中Inherited widget发生了变化,那么此时InheritedWidget的子 widget 的didChangeDependencies()回调都会被调用。典型的场景是当系统语言 Locale 或应用主题改变时,Flutter 框架会通知 widget 调用此回调。需要注意,组件第一次被创建后挂载的时候(包括重创建)对应的didChangeDependencies也会被调用。

  • build():此回调读者现在应该已经相当熟悉了,它主要是用于构建 widget 子树的,会在如下场景被调用:

    在调用initState()之后。
    在调用didUpdateWidget()之后。
    在调用setState()之后。
    在调用didChangeDependencies()之后。
    在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其他位置之后。
    reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在 Release模式下永远不会被调用。

  • didUpdateWidget ():在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。正如之前所述,widget.canUpdate会在新旧 widget 的 key 和 runtimeType 同时相等时会返回true,也就是说在在新旧 widget 的key和runtimeType同时相等时didUpdateWidget()就会被调用。

  • deactivate():当 State 对象从树中被移除时,会调用此回调。在一些场景下,Flutter 框架会将 State 对象重新插到树中,如包含此 State 对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey 来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。

  • dispose():当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。
    在这里插入图片描述

4.在 widget 树中获取State对象

  • 通过Context获取
    context对象有一个findAncestorStateOfType()方法,该方法可以从当前节点沿着 widget 树向上查找指定类型的 StatefulWidget 对应的 State 对象。
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'),
             );
           }),
         ],
       ),
     ),
     drawer: Drawer(),
   );
 }
}

上面示例中的Scaffold也提供了一个of方法

Builder(builder: (context) {
  return ElevatedButton(
    onPressed: () {
      // 直接通过of静态方法来获取ScaffoldState
      ScaffoldState _state=Scaffold.of(context);
      // 打开抽屉菜单
      _state.openDrawer();
    },
    child: Text('打开抽屉菜单2'),
  );
}),
  • 通过GlobalKey,两步骤
    1.给目标StatefulWidget添加GlobalKey。
//定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
...
Scaffold(
    key: _globalKey , //设置key
    ...  
)

2.通过GlobalKey来获取State对象

_globalKey.currentState.openDrawer()

GlobalKey 是 Flutter 提供的一种在整个 App 中引用 element 的机制。如果一个 widget 设置了GlobalKey,那么我们便可以通过globalKey.currentWidget获得该 widget 对象、globalKey.currentElement来获得 widget 对应的element对象,如果当前 widget 是StatefulWidget,则可以通过globalKey.currentState来获得该 widget 对应的state对象。
tips:使用 GlobalKey 开销较大,如果有其他可选方案,应尽量避免使用它。另外,同一个 GlobalKey 在整个 widget 树中必须是唯一的,不能重复。

5.状态管理

  • Widget管理自身状态
  • 父Widget管理子Widget的状态
  • 混合状态管理
  • 全局状态管理
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值