找回密码
 立即注册
首页 业界区 安全 flutter学习-day5-认识widget

flutter学习-day5-认识widget

倘伟 2025-6-1 20:47:53
目录

  • 1. 简介
  • 2. StatelessWidget
  • 3. Context上下文
  • 4. StatefulWidget
  • 5. State
  • 6. State生命周期
  • 7. 在widget树中获取State对象

    • 7-1. 通过Context
    • 7-2. 通过GlobalKey

  • 8. 通过RenderObject自定义Widget
  • 9. 常用基础组件

本文学习和引用自《Flutter实战·第二版》:作者:杜文
1. 简介

widget的功能是描述一个UI元素的配置信息。它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector 、用于APP主题数据传递的 Theme 等等。Flutter 中是通过 Widget 嵌套 Widget 的方式来构建UI和进行事件处理的,所以,Flutter 中万物皆为Widget。

  • 根据 Widget 树生成一个 Element 树
  • 根据 Element 树生成 Render 树
  • 根据渲染树生成 Layer 树
  • Flutter 真正的布局和渲染逻辑在 Render 树中
  • Element 是 Widget 和 RenderObject 的粘合剂,可以理解为一个中间代理
2. StatelessWidget

无状态组件tatelessWidget,用于不需要维护状态的场景,它通常在build方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget。它继承自widget类,重写了createElement()方法:

  • StatefulWidget的类定义
  1. // 不可变的 属性必须是 final
  2. @immutable
  3. // 继承DiagnosticableTree诊断树 提供调试信息
  4. abstract class Widget extends DiagnosticableTree {
  5.   // key属性类似于 React/Vue 中的key
  6.   const Widget({ this.key });
  7.   final Key? key;
  8.   // Flutter 框架隐式调用createElement() 开发者无需关注
  9.   @protected
  10.   @factory
  11.   Element createElement();
  12.   @override
  13.   String toStringShort() {
  14.     final String type = objectRuntimeType(this, 'Widget');
  15.     return key == null ? type : '$type-$key';
  16.   }
  17.   // 复写父类的方法,主要是设置诊断树的一些特性
  18.   @override
  19.   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
  20.     super.debugFillProperties(properties);
  21.     properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
  22.   }
  23.   @override
  24.   @nonVirtual
  25.   bool operator ==(Object other) => super == other;
  26.   @override
  27.   @nonVirtual
  28.   int get hashCode => super.hashCode;
  29.   // 更新 决定是否在下一次build时复用旧的 widget
  30.   static bool canUpdate(Widget oldWidget, Widget newWidget) {
  31.     return oldWidget.runtimeType == newWidget.runtimeType
  32.         && oldWidget.key == newWidget.key;
  33.   }
  34.   // ......
  35. }
复制代码
  1. @override
  2. StatelessElement createElement() => StatelessElement(this);
复制代码

  • 例子
  1. class Echo extends StatelessWidget  {
  2.   const Echo({
  3.     Key? key,  
  4.     required this.text,
  5.     this.backgroundColor = Colors.grey,
  6.   }):super(key:key);
  7.    
  8.   final String text;
  9.   final Color backgroundColor;
  10.   @override
  11.   Widget build(BuildContext context) {
  12.     return Center(
  13.       child: Container(
  14.         color: backgroundColor,
  15.         child: Text(text),
  16.       ),
  17.     );
  18.   }
  19. }
  20. Widget build(BuildContext context) {
  21.   return Echo(text: "hello world");
  22. }
复制代码
3. Context上下文

build方法有一个context参数,它是BuildContext类的一个实例,表示当前 widget 在 widget 树中的上下文,每一个 widget 都会对应一个 context 对象。如下:
  1. // 在子树中获取父级 widget 的 title 属性
  2. class ContextRoute extends StatelessWidget  {
  3.   @override
  4.   Widget build(BuildContext context) {
  5.     return Scaffold(
  6.       appBar: AppBar(
  7.         title: Text("Context测试"),
  8.       ),
  9.       body: Container(
  10.         child: Builder(builder: (context) {
  11.           // 在 widget 树中向上查找最近的父级`Scaffold`  widget
  12.           Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
  13.           // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
  14.           return (scaffold.appBar as AppBar).title;
  15.         }),
  16.       ),
  17.     );
  18.   }
  19. }
复制代码
4. StatefulWidget

StatefulWidget也是继承自widget类,并重写了createElement()方法,但是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。

  • StatefulWidget的类定义
  1. abstract class StatefulWidget extends Widget {
  2.   const StatefulWidget({ Key key }) : super(key: key);
  3.    
  4.   // 配置数据
  5.   @override
  6.   StatefulElement createElement() => StatefulElement(this);
  7.    
  8.   // 创建和 StatefulWidget 相关的状态
  9.   @protected
  10.   State createState();
  11. }
复制代码
5. State

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

  • 在 widget 构建时可以被同步读取
  • 在 widget 生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter 框架状态发生改变,Flutter 框架会重新调用其build方法重新构建widget树
State 中有两个常用属性:

  • widget,它表示与该 State 实例关联的 widget 实例,由Flutter 框架动态设置,重新构建时,也会更新。
  • context。StatefulWidget对应的 BuildContext,示当前 widget 在 widget 树中的上下文
6. State生命周期


  • initState:当 widget 第一次插入到 widget 树时会被调用,只会调用一次。
  • didChangeDependencies:当State对象的依赖发生变化时会被调用。
  • build:主要是用于构建 widget 子树,在调用initState、didUpdateWidget、setState、didChangeDependencies,以及移除后重新插入之后会调用。
  • reassemble:热重载时会被调用,生产环境不会调用。
  • didUpdateWidget:在 widget 重新构建时,在新旧 widget 的key和runtimeType同时相等时didUpdateWidget()就会被调用。
  • deactivate:当 State 对象从树中被移除时,会调用此回调。
  • dispose:当 State 对象从树中被永久移除时调用,通常在此回调中释放资源。
  1. // 继承StatefulWidget
  2. class CounterWidget extends StatefulWidget {
  3.   const CounterWidget({Key? key, this.initValue = 0});
  4.   final int initValue;
  5.   @override
  6.   _CounterWidgetState createState() => _CounterWidgetState();
  7. }
  8. // 定义状态
  9. class _CounterWidgetState extends State<CounterWidget> {
  10.   int _counter = 0;
  11.   // 当 widget 第一次插入到 widget 树时会被调用 只会调用一次
  12.   @override
  13.   void initState() {
  14.     super.initState();
  15.     //初始化状态
  16.     _counter = widget.initValue;
  17.     print("initState");
  18.   }
  19.   // 主要是用于构建 widget 子树,在调用initState、didUpdateWidget、setState、didChangeDependencies,以及移除后重新插入之后会调用。
  20.   @override
  21.   Widget build(BuildContext context) {
  22.     print("build");
  23.     return Scaffold(
  24.       body: Center(
  25.         child: TextButton(
  26.           child: Text('$_counter'),
  27.           //点击后计数器自增
  28.           onPressed: () => setState(
  29.             () => ++_counter,
  30.           ),
  31.         ),
  32.       ),
  33.     );
  34.   }
  35.   // 在 widget 重新构建时,在新旧 widget 的key和runtimeType同时相等时didUpdateWidget()就会被调用。
  36.   @override
  37.   void didUpdateWidget(CounterWidget oldWidget) {
  38.     super.didUpdateWidget(oldWidget);
  39.     print("didUpdateWidget ");
  40.   }
  41.   // 当 State 对象从树中被移除时,会调用此回调。
  42.   @override
  43.   void deactivate() {
  44.     super.deactivate();
  45.     print("deactivate");
  46.   }
  47.   // 当 State 对象从树中被永久移除时调用,通常在此回调中释放资源
  48.   @override
  49.   void dispose() {
  50.     super.dispose();
  51.     print("dispose");
  52.   }
  53.   // 热重载时会被调用,生产环境不会调用。
  54.   @override
  55.   void reassemble() {
  56.     super.reassemble();
  57.     print("reassemble");
  58.   }
  59.   // 当State对象的依赖发生变化时会被调用。
  60.   @override
  61.   void didChangeDependencies() {
  62.     super.didChangeDependencies();
  63.     print("didChangeDependencies");
  64.   }
  65. }
复制代码
7. 在widget树中获取State对象

由于 StatefulWidget 的具体逻辑都在其 State 中,所以很多时候,我们需要获取 StatefulWidget 对应的State 对象来调用一些方法。
7-1. 通过Context

context对象有一个findAncestorStateOfType()方法,该方法可以从当前节点沿着 widget 树向上查找指定类型的 StatefulWidget 对应的 State 对象。下面是实现打开 SnackBar 的示例:
  1. class GetStateObjectRoute extends StatefulWidget {
  2.   const GetStateObjectRoute({Key? key}) : super(key: key);
  3.   @override
  4.   State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
  5. }
  6. class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
  7.   @override
  8.   Widget build(BuildContext context) {
  9.     return Scaffold(
  10.       appBar: AppBar(
  11.         title: Text("子树中获取State对象"),
  12.       ),
  13.       body: Center(
  14.         child: Column(
  15.           children: [
  16.             Builder(builder: (context) {
  17.               return ElevatedButton(
  18.                 onPressed: () {
  19.                   // 查找父级最近的Scaffold对应的ScaffoldState对象
  20.                   ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
  21.                   // 打开抽屉菜单
  22.                   _state.openDrawer();
  23.                 },
  24.                 child: Text('打开抽屉菜单1'),
  25.               );
  26.             }),
  27.           ],
  28.         ),
  29.       ),
  30.       drawer: Drawer(),
  31.     );
  32.   }
  33. }
复制代码
在 Flutter 开发中有一个默认的约定:如果 StatefulWidget 的状态是希望暴露出的,应当在 StatefulWidget 中提供一个of 静态方法来获取其 State 对象,开发者便可直接通过该方法来获取;如果 State不希望暴露,则不提供of方法。这个约定在 Flutter SDK 里随处可见。
  1. Builder(builder: (context) {
  2.   return ElevatedButton(
  3.     onPressed: () {
  4.       // 直接通过of静态方法来获取ScaffoldState
  5.       ScaffoldState _state = Scaffold.of(context);
  6.       // 打开抽屉菜单
  7.       _state.openDrawer();
  8.     },
  9.     child: Text('打开抽屉菜单2'),
  10.   );
  11. })
复制代码
7-2. 通过GlobalKey

Flutter还有一种通用的获取State对象的方法——通过GlobalKey来获取! 步骤分两步:

  • 给目标StatefulWidget添加GlobalKey。
  1. //定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
  2. static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
  3. Scaffold(
  4.   // 设置key
  5.   key: _globalKey,
  6.   // ......
  7. )
复制代码

  • 通过GlobalKey来获取State对象
  1. _globalKey.currentState.openDrawer()
复制代码
GlobalKey 是 Flutter 提供的一种在整个 App 中引用 element 的机制。如果一个 widget 设置了GlobalKey,那么我们便可以通过globalKey.currentWidget获得该 widget 对象、globalKey.currentElement来获得 widget 对应的element对象,如果当前 widget 是StatefulWidget,则可以通过globalKey.currentState来获得该 widget 对应的state对象。
8. 通过RenderObject自定义Widget

StatelessWidget 和 StatefulWidget 都是用于组合其他组件的,二自定义组件的方式就是通过定义RenderObject 来实现。
  1. class CustomWidget extends LeafRenderObjectWidget{
  2.   @override
  3.   RenderObject createRenderObject(BuildContext context) {
  4.     // 创建 RenderObject
  5.     return RenderCustomObject();
  6.   }
  7.   @override
  8.   void updateRenderObject(BuildContext context, RenderCustomObject  renderObject) {
  9.     // 更新 RenderObject
  10.     super.updateRenderObject(context, renderObject);
  11.   }
  12. }
  13. class RenderCustomObject extends RenderBox{
  14.   @override
  15.   void performLayout() {
  16.     // 实现布局逻辑
  17.   }
  18.   @override
  19.   void paint(PaintingContext context, Offset offset) {
  20.     // 实现绘制
  21.   }
  22. }
复制代码
9. 常用基础组件

有Material(Android视觉风格)和Cupertino(iOS视觉风格)组件库

  • Text:创建一个带格式的文本。
  • Row:水平行。
  • Column:垂直列。
  • Stack:类似绝对定位,允许子 widget 堆叠。
  • Container:创建矩形视觉元素。可以装饰一个BoxDecoration,比如背景、边框、渐变、阴影等。
本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~
PS:在本页按F12,在console中输入document.querySelectorAll('.diggit')[0].click(),有惊喜哦
公众号
1.jpeg

往期文章

  • flutter学习-day1-环境搭建和启动第一个项目
  • Vue2全家桶+Element搭建的PC端在线音乐网站
  • 超详细的Cookie增删改查
  • 助你上手Vue3全家桶之Vue-Router4教程
  • 助你上手Vue3全家桶之Vue3教程
  • 助你上手Vue3全家桶之VueX4教程
  • 使用nvm管理node.js版本以及更换npm淘宝镜像源
  • 超详细!Vue-Router手把手教程
  • 超详细!Vue的九种通信方式
  • 超详细!Vuex手把手教程
个人主页

  • CSDN
  • GitHub
  • 简书
  • 博客园
  • 掘金

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册