Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Flutter | 基础Widget

Flutter | 基础Widget

作者头像
345
发布于 2022-02-11 06:07:34
发布于 2022-02-11 06:07:34
1.4K00
代码可运行
举报
运行总次数:0
代码可运行

基础 Widget

在 Fluter 中,几乎所有的都是一个 widget ,与原生开发不同的是,widget 的范围更加广阔,他不仅可以表示 UI 元素,也可以表示一些功能的组件,如手势检测的 widget,用于主题数据传递的 Theme 等等。所以,在大多数时候,可以认为 widget 就是一个控件,不必纠结于概念

Widget 的功能是 “描述一个 UI 元素的配置数据”,widget 并不是表示最终绘制在屏幕上的显示元素,正在代绘制屏幕上的是 Element ,下面就看一下 Element

Widget 与 Element

Flutter 中,Widget 的功能是 "描述一个 UI 元素的配置数据",也就是说,Widget 其实并不是表示最终绘制在设备屏幕上的显示元素,它只是描述显示元素的一个配置数据

实际上,Flutter 中真正代表屏幕上显示元素的类是 Element ,也就是说 Widget 只是描述 Element 的配置数据,前期读者只需要知道:Widget 只是 UI 元素的一个配置数据,并且一个 Widget 可以对应多个 Element。这是因为同一个 Widget 可以被添加到 UI 树的不同部分,而真正渲染时,UI 树的每一个 Element 都会对应一个 Widget 对象 。总结一下:

  • Widget 实际上就是 Element 的配置数据。Widget 树实际上是一个配置数,而真正渲染 UI 树是由 Element 构成 不过由于是 Element 是通过 Widget 生成的,所以他们之间是有对应关系,在大多数场景,我们可以广泛的认为 Widget 树就是指 UI 控件树或 UI 渲染树
  • 一个 Widget 对象可以对应多个 Element。这个很好理解,根据同一份配置(Widget),可以创建多个实例(Element)

Widget 类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  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;
  }

  static int _debugConcreteSubtype(Widget widget) {
    return widget is StatefulWidget ? 1 :
           widget is StatelessWidget ? 2 :
           0;
    }
}
复制代码
  • Widget 类继承自 DiagnosticableTreeDiagnosticableTree 即诊断树,主要作用是提供调试信息
  • Key:这个 key 属性 类似于 React/Vue 中的 key,主要的作用是决定下一次 build 时复用旧的 widget,决定的条件在 canUpdate() 方法中。
  • createElement():正如前文所述,一个 Widget 可以对应多个 Element,Flutter Framework 在构建 UI 树时,会先调用此方法生成对应节点的 Element 对象。此方法是 Flutter FrameWork 隐式调用的,在我们开发过程中基本不会调用到。
  • _debugConcreteSubtype 复写父类的方法,主要是诊断树的一些特性
  • canUpdate() 是一个静态方法,主要用于在 Widget 树重新更新 build 时服复用的 widget,其实具体来说,应该是:是否用新的 Widget 对象去更新旧 UI 树上所对应的 Element 对象的配置;通过其源码我们可以看到,只要 newWidet oldWidgetruntimeTypekey 同时相等时就会用 newWidget 去更新 Element 对象的配置,否则就会创建新的 Element

另外 Widget 类本身是一个抽象类,其中最核心的就是定义了 createElement() 接口,在 Flutter 开发中,我们一般都不用直接继承 Widget 类来 实现一个新组建,想法,我们经常会通过继承 StatelessWidget 或 StatefulWidget 来间接继承 Widget 类,这两个类都继承自 Widget 类,并且这两个是非常重要的抽象类,它们引入了 Widget 中的两种模型。接下来 将重点介绍一下这两个类

StatelessWidget

  • 无状态组件
  • 继承自 Widget 类,重写了 createElement() 方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@override
StatelessElement createElement() => StatelessElement(this);

创建 StatelessElement 对象,间接继承自 Element 类,与 StatelessWidget 相对应(作为其配置数据)

  • StatelessWidget 用于不需要维护状态的场景(也就是UI不可修改),它通常在 build方法中通过嵌套其他 Widget 来构建 UI ,在构建过程中会递归的构建其嵌套的 Widget。
  • 栗子
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Echo extends StatelessWidget {
  final String text;
  final Color backgroundColor;

  const Echo({Key key, @required this.text, this.backgroundColor: Colors.green})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        child: Text("hello word"),
        color: backgroundColor,
      )
    );
  }
}

上面代码,实现了一个回显字符串的 Echo Widget

widget 的构造函数参数应使用命名参数,命名参数中的必要参数要添加 @required 标注,这样有利于静态代码分析器进行检查。另外,在继承 widget 时,第一个参数通常 key ,另外,如果 Widget 需要接收自 Widget,那么 child 或者 children 参数通常应该放在参数列表的最后。widget 的属性应该尽肯能被声明为 final,防止被意外改变

可以使用如下方式去使用它

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "Widget相关",
        theme: ThemeData(primaryColor: Colors.blue),
        home: Echo(text: "hello word"));
  }
}
 
  • Context build 有一个 context 参数,他是 BuildContext 类的实例,表示当前 widget 在 widget 树种的上下文,每个 widget 都会对应一个 context 对象(因为每个 widget都是 widget 树上的一个节点)。实际上,context 是当前 widget 在 widget 树中位置中执行 “相关操作”的一个句柄,比如它提供了从当前 widget 开始向上遍历widget树,以及查找父类 widget 方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ContextRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Context测试"),
      ),
      body: Container(
        child: Builder(builder: (context) {
          // 在Widget树中向上查找最近的父级`Scaffold` widget
          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
          // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
          return (scaffold.appBar as AppBar).title;
        }),
      ),
    );
  }
}
 

StatefulWidget

  • 有状态的组件
  • 和 StatelessWidget 一样,StatefulWidget 也是继承自 widget 类,并重写了 createElement 方法,不同的是返回的 Element 对象并不相同;另外 StatefulWidget 类中添加了一个新的接口 createState()
  • 至少由两个类组成,一个 StatefulWidget ,一个 state 类
  • StatefulWidget 类本身是不变的,但是 State 类中持有的状态在 widget 生命周期中可能会发生变化
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract class StatefulWidget extends Widget {
    
  const StatefulWidget({ Key? key }) : super(key: key);

  @override
  StatefulElement createElement() => StatefulElement(this);

  @protected
  @factory
  State createState();
}
复制代码
  • StatefulElement 间接继承 Element 类,与 StatefulWidget 相对应(作为配置数据),S他特氟龙Element中可能会多次调用 createState 来创建状态(State)对象
  • createState 用于创建 Stateful widget 相关的状态,他在 Stateful widget 的生命周期中可能会被多次调用。例如,当一个 Stateful widget同时插入到 widget 树的多个未值日时,Flutter framework 就会调用该方法为每一个位置生成一个独立的 State 实例,其实,本质上就是一个 StatefulElement 对应一个 State 实例

Widget 树他可以指 widget 结构树,但是由于 widget 与 Element 有对应关系(一可能对多),在有些场景(Flutter 的 Sdk 文档中) 也代指 "UI树" 的意思

State

一个 StatefulWidget 会对应一个 State 类。State 表示与其对应的 StatefulWidget 要维护的状态,State 中保存的状态信息可以:

  • 在 widget 构建时可以被同步读取
  • 在 Widget 生命周期中可以被改变,当 State 被改变时,可以手动调用 setState() 方法通知 Flutter framework 状态发生改变,flutter framework 收到消息后,会调用其 build 方法重新构建 widget 树,从而达到更新 UI 的目的

State 中两个常用的属性

  • widget :他表示与之关联的 widget 实例,由 Flutter framework 动态设置,不过这种关联并发永久,因为在生命周期中, UI 树上的某一节点 widget 实例自重新构建时可能会发生变化。但 State 实例只会在第一次插入到树中时被创建,当在重新构建时,如果 widget 被修改了,flutter framework 会动态设置 state,widget 为最新的 widget 实例
  • context StatefulWidget 对应的 BuildContext,作用同 StatelessWidget 的 BuildContext 一致

State 生命周期

  • nitState 当 Widget 第一次插入到树中 Widget 时调用,对于每一个 State 对象,Flutter framework 只会调用一次该回调,所以通常在该回调中做一些一次性的操作,如状态初始化,订阅子树的时间通知等 不能再回调中调用 BuildContext.dependOnInheritedWidgetOfExactType ,原因是在初始化完成后,Widget 树中的 InheritFromoWidget 也可以会发生变化,所以正确的做法应该是在 build 方法或者 didChangeDependencies 中调用它
  • didChangeDependencies() 当 State 对象依赖发生变化时会被调用 例如:build 中包含了一个 InheritedWidget,在之后的 build 中 InheritedWidget发生了变化,那么此时 InheritedWidget 的子 widget 的 didChangeDependencies 回调都会被调用。 典型的场景是当系统语言 Locale 或应用主题改变时, Flutter framework 会 调用 widget 进行回调
  • build() 主要是用来构建 Widget 子树的,会在如下场景被调用 1,在调用 initState 之后 2,在调用 didUpdateWidget() 之后 3,在调用 setState() 之后 4,在调用 didChangeDependencies() 之后 5,在 State 对象树中一个位置移除后(会调用 deactivate) 又重新插入到树的其他位置之后
  • reassemble() 此回调是专门为了开发调试而提供的,在热重载(hot reload) 时会被调用,此回调在 Release 模式下永远不会被调用
  • didUpdateWidget() 在 widget 重新构建时,Flutter framework 会调用 Widget.canUpdate 来检测 Widget 树中同一个位置的新旧节点,然后去确定是否需要更新,如果 widget.canUpdate 返回 true 则会调用此回调。 正如之前所说, widget.canUpdate 会在新旧 widget 的 key 和 runtimeType 同时相等时返回 true,也就是说在新旧相等的 widget 的 额可以 和 runtimeType 同时相等时 此方法会被调用
  • deactivate() 当 State 对象从树中被移除时,会调用此回调。 在一些场景下,Flutter framework 会将 State 对象重新插入到树中,如果包含次 State 对象的子树在树的一个位置移动到另一个位置时(可以通过 GlobalKey 来实现)。如果移除之后没有重新插入到树中则紧接着就会调用 dispose() 方法
  • dispose() 当 State 对象从树中被永久移除时调用;通常子此回调中释放资源
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class CounterWidget extends StatefulWidget {
  final int counter;

  const CounterWidget({Key key, this.counter: 0}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _CounterWidget();
  }
}

class _CounterWidget extends State<CounterWidget> {
  int _counter;

  @override
  void initState() {
    super.initState();
    //初始化
    _counter = widget.counter;
    print("initState:初始化");
  }

  @override
  void didUpdateWidget(covariant CounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget:widget 重新构建');
  }

  @override
  void deactivate() {
    super.deactivate();
    print('deactivate:State 被移除');
  }

  @override
  void reassemble() {
    super.reassemble();
    print('reassemble:热重载');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies:State 对象依赖发生变化');
  }

  @override
  void dispose() {
    super.dispose();
    print('dispose:State 永久移除');
  }

  @override
  Widget build(BuildContext context) {
    print('build:构建 widget');
    return Scaffold(
        body: Center(
            child: FlatButton(
      child: Text("$_counter"),
      onPressed: () => setState(() => ++_counter),
    )));
  }
}

复制代码

一个计数器的小栗子,用来观察一下生命周期的变化

1,首先,打开这个页面,查看输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
I/flutter ( 6725): initState:初始化
I/flutter ( 6725): didChangeDependencies:State 对象依赖发生变化
I/flutter ( 6725): build:构建 widget
复制代码

2,点击热重载按钮,调用如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
I/flutter ( 6725): reassemble:热重载
I/flutter ( 6725): didUpdateWidget:widget 重新构建
I/flutter ( 6725): build:构建 widget
复制代码

3,点击数字按钮,调用如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
I/flutter ( 6725): build:构建 widget
复制代码

4,在 widget 树中移除 CountWidget

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "Widget相关",
        theme: ThemeData(primaryColor: Colors.blue),
        // home: CounterWidget(counter: 0)
      	home:Text("hello word")
    );
  }
}
复制代码

然后点击热重载,调用如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
I/flutter ( 7366): deactivate:State 被移除
I/flutter ( 7366): dispose:State 永久移除
复制代码

生命周期图如下所示:

获取 State 对象

由于 StatefulWidget 的具体逻辑都在其 State 中,所有很多时候,我们都需要获取 StatefulWidget 对应的 State 对象来调用一些方法,对此,我们有两种方法在子 widget 树中获取父级 StatefulWidget 的 State 对象

通过 Context 获取

context 对象有一个 findAncestorStateOfType() 方法,该方法可以从当前节点沿着 widget 树向上查找指定类型的 StatefulWidget 对应 的 State 对象,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 // 查找父级最近的Scaffold对应的ScaffoldState对象
ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>();
复制代码

通过 of 静态方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 ScaffoldState _state = Scaffold.of(context);
复制代码
通过 GlobalKey

1,给目标 StatefulWidget 添加 GlobalKey

2,通过 GlobalKey 来获取 State 对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
...
Scaffold(
    key: _globalKey , //设置key
    ...  
)
复制代码

注意:使用 GlobalKey 开销很大,如果有其他方案,应该去避免它,另外同一个 GlobalKey 在整个 widget 树中必须是惟一的,不能重复

本文参考自 Flutter实战(书籍)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Flutter | 和小老弟一起玩转Widget
你可以理解为在Flutter中一切都可以通过组合的方式实现,对于我们开发者,只需声明,或者说告诉框架这个组件是什么,它要怎么显示,携带了哪些参数,而Widget 就是用来帮你承载配置的东西。
Petterp
2022/02/09
9940
Flutter | 和小老弟一起玩转Widget
【Flutter 组件】001-关于 Widget 的一切
当 Widget 状态发生变化,需要更新界面时,框架会先计算从上一个状态转换到下一个状态所需的最小更改,然后再去刷新界面。
訾博ZiBo
2025/01/06
3680
【Flutter 组件】001-关于 Widget 的一切
Flutter跨平台移动端开发丨Widget、Element、State、状态管理
widget 的主要工作是通过实现 build 函数 来构建自身。一个 widget 通常由一些低级别的 widget 组成,flutter 框架依次的构建这些低级别的 widget,直到构建到最底层的子 widget 时,它会计算并描述 widget 的几何形状
码脑
2019/05/25
2.1K0
Flutter State生命周期
前面说过了StatefullWidget,这节我们来说说State的生命周期,这在flutter开发中是非常重要的。
用户1974410
2022/09/20
9820
Flutter State生命周期
Flutter 生命周期详解
Flutter 跟 Android 的 Activity、iOS 的 ViewController 一样都拥有自己的生命周期。在 Flutter 中几乎所有的对象都是一个 Widget,其中 Widget 又分为 StatelessWidget(即:无状态的 Widget) 和 StatefulWidget (即:有状态的 Widget),这里所说的 Flutter 的生命周期其实就是讲 StatefulWidget 的生命周期,它存在于 framework.dart 的 State 类中。
子晋
2022/01/19
1.4K0
Flutter 生命周期详解
Flutter Widget源码解析及实战
这是一篇投稿文章,近日,国内外都掀起了Flutter的学习热潮。本文作者分享了自己在学习Flutter Widget时的心得与体会。
岛哥的质量效能笔记
2021/08/18
2.3K0
Flutter Widget源码解析及实战
Flutter--Flutter中Widget、App的生命周期
  在App的开发过程中,我们通常都需要了解App以及各个页面的生命周期,方便我们在App进入前台时启动一些任务,在进入后台后暂停一些任务。同时,各个页面的生命周期也很重要,每个页面消失时要做一些内存清理、计时器清除、通知清除等操作。所以,本文主要就是学习一下在flutter开发App的时候,如何去怼App以及各个页面的生命周期进行监听和回调。
mukekeheart
2021/02/02
4K0
Flutter入门三部曲(2) - 界面开发基础
上一节我们熟悉了初始化后的flutter的界面。这一节,我们就来重点了解一下这部分的内容。
deep_sadness
2018/08/17
2.8K0
Flutter入门三部曲(2) - 界面开发基础
widget简介
在 Flutter 中一切的显示都是 Widget ,Widget 是一切的基础,利用响应式模式进行渲染。Flutter 从 React 中吸取灵感,通过现代化框架创建出精美的组件。它的核心思想是用 widget 来构建你的 UI 界面。Widget 描述了在当前的配置和状态下视图所应该呈现的样子。当 widget 的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。
用户1974410
2022/09/20
1.6K0
【Flutter 专题】100 何为 Flutter Widgets ?
和尚学习 Flutter 有一段时间了,其中 Flutter 的核心思想是 Everything is Widget;但是什么是 Widget 它与我们常说的 Element 和 RenderObject 有什么关系呢,和尚就个人理解简单整理一下;
阿策小和尚
2020/09/17
8990
Flutter框架分析(三)-- Widget,Element和RenderObject
前面两篇Flutter框架分析的文章介绍了渲染流水线,window和框架的初始化。这篇文章继续来理一下对Flutter app开发者来说比较重要的Widget,Element和RenderObject体系。Flutter的理念是一切都是Widget(Everythin is Widget)。开发者在开发Flutter app的时候主要都是在写很多Widget。那么这三者之间是什么关系?它们是怎么工作的呢?让我们来一探究竟。
HowHardCanItBe
2020/09/15
1.5K0
flutter系列之:用来管理复杂状态的State详解
Flutter的基础是widget,根据是否需要跟用户进行交互,widget则可以分为StatelessWidget和StatefulWidget。StatelessWidget只能根据传入的状态进行简单的初始化widget,如果要实现跟用户交互这种复杂的功能,则需要用到StatefulWidget。
程序那些事
2022/08/22
5470
【Flutter 专题】101 何为 Flutter Elements ?
和尚前段时间简单了解了一下 Widget 的相关知识,其中 Widget 是 immutable 不可变的,而 Widget 是如何做到更新重绘的,这就离不开 Element 和 RenderObject;和尚简单了解一下 Element 的相关小知识;
阿策小和尚
2020/10/23
5490
StatefulWidget与State
首先来看下StatefulWidget,它是一个抽象类,当然它十分的简单。它和StatelessWidget一样它只有两个方法,都有一个createElement的方法,但是和StatelessWidget不同的是,StatelessWidget可以直接通过build方法来构建Widget,但是StatefulWidget没有build方法,所以它只能使用State的build来构建Widget。
flyou
2020/05/29
1.6K0
Stateful 组件的生命周期​
此篇文章介绍 StatefulWidget 组件的生命周期, StatefulWidget 组件的生命周期时非常重要的知识点,就像 Android 中 Activity 的生命周期一样,不仅在以后的工作中经常用到,面试也会经常被问到。
老孟Flutter
2020/12/31
1.1K0
Flutter之旅:认识Widget(源码级)
1.Widget的第一印象 1.1:初次的见面 首先我们来到第一次看到Widget类的场景,那时还对这个世界一无所知, 进入程序的入口时runApp函数中需要传入一个Widget对象,这便是第一眼。 初始项目中的做法是自定义了一个MyApp类继承自StatelessWidget。 void main()=>runApp(MyApp()); ---->[flutter/lib/src/widgets/binding.dart:778]---- void runApp(Widget app) {
张风捷特烈
2020/04/30
1.5K0
Flutter之旅:认识Widget(源码级)
【Flutter】Flutter 页面生命周期 ( 初始化期 | createState | initState | 更新期 | build | 销毁期 | dispose)
Flutter 页面生命周期就是 Flutter 页面组件 Widget 的生命周期 ;
韩曙亮
2023/03/28
5.1K0
【Flutter】Flutter 页面生命周期 ( 初始化期 | createState | initState | 更新期 | build | 销毁期 | dispose)
Flutter中的Key
本文内容主要翻译自Keys in Flutter, 最初翻译动机是原作者写的比较通俗,其次 key 知识点在 Flutter 中比较重要,但在翻译过程中发现不配合相关源码很难理解作者意思而且看完容易忘,所以加了些注释和理解(详见引述),有什么不对的地方欢迎各位大佬交流指正,多谢!
睡觉谁叫
2023/04/22
1.7K0
Flutter中的Key
Flutter漫说:组件生命周期、State状态管理及局部重绘的实现(Inherit)
flutter的生命周期其实有两种:StatefulWidget和StatelessWidget。
BennuCTech
2021/12/10
1.8K0
Flutter漫说:组件生命周期、State状态管理及局部重绘的实现(Inherit)
【源码分析】系列之 InheritedWidget
为了使源码系列的文章不那么枯燥,文章中会有很多流程图,流程图比纯文字更直观,一图胜千言。
老孟Flutter
2021/01/28
1.2K0
相关推荐
Flutter | 和小老弟一起玩转Widget
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验