1、概念

Widget 是 UI 控件的基本抽象,它负责描述 UI 的一部分应该如何构建。每个Widget 都有一个对应的 RenderObject,负责实际的布局和绘制工作。Widget 不直接参与布局或绘制过程;它们只是描述了如何构建用户界面的一部分,并且可以被组合起来创建更复杂的 UI。

2、Widget接口

Wiget类本身是一个抽象类,最核心的部分是定义了createElement()接口,在Flutter实际开发中,我们使用StatelessWidget和StatefulWidget间接继承Widget来实现新组件,比如创建Flutter项目时生成的示例代码。


abstract class Widget extends DiagnosticableTree {
  const Widget({required this.key});

  final Key key;

  
  Element createElement();

  
  String toStringShort() {
    return '$runtimeType';
  }

  void debugFillproperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
  }

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType &&
        oldWidget.key == newWidget.key;
  }
}
  • DiagnosticableTree:诊断树,它在Flutter框架中主要用于生成诊断信息,帮助我们更好的理解程序的运行状态;

  • key:决定是否在下一次创建时复用旧的Widget,条件在canUpdate()中;

  • createElement():Flutter Framework构建UI树时,优先调用此方法生成对应节点的Element对象,Flutter Framework在调用时为隐式调用;

  • DiagnosticableTree 方法:

    方法作用
    toString()返回对象的字符串表示形式。
    toStringShort()返回对象的简短字符串表示形式。
    toStringDeep()返回对象及其子树的详细字符串表示形式。
    toStringShallow()返回对象本身的详细字符串表示形式。
    toStringProperties()返回对象的属性信息。
    debugFillProperties()填充诊断属性构建器,用于生成详细的诊断信息。
  • canUpdate()是否使用旧的Widget对象更新旧UI树上对应Element配置,如果两个Widget的runtimeType和Key同时相等,则认为它们可以更新。

3、StatelessWidget

StatelessWidget继承自Widget类重写了createElement()方法:


class Echo extends StatelessWidget {
const Echo({super.key});


StatelessElement createElement() => StatelessElement(this);


Widget build(BuildContext context) {
 // TODO: implement build
 throw UnimplementedError();
}
}
  • 显示文本

class Echo extends StatelessWidget {
  final String text;
  final Color backgroundColor;

  const Echo(
      {super.key,
      required this.text,
      this.backgroundColor = Colors.grey,
      required String title});

  
  StatelessElement createElement() => StatelessElement(this);

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: backgroundColor,
        child: Text(text),
      ),
    );
  }
}
  • 调用
class _MyHomePageState extends State<MyHomePage> {
  
  Widget build(BuildContext context) {
    return const Echo(text: "示例文本", title: "StatelessWidget");
  }
}

4、StatefulWidget

StatefulWidget和StatelessWidget一样也继承自Widget类 ,StatefulWidget也重写了createElement()方法,返回的Element对象不同;而且StatefulWidget类中添加了一个新的接口createState()

  • StatefulWidget源码
abstract class StatefulWidget extends Widget {
  const StatefulWidget({ super.key });

  
  StatefulElement createElement() => StatefulElement(this);

  State createState();
}
  • StatefulElement:间接调用Element类,作为StatefulWidget的配置数据相对应,StatefulElement是Element的子类,它可以多次调用createState()来创建状态对象;
  • createState():抽象方法,必须在具体的StatefulWidget子类中实现,用于创建Stateful Widget相关的状态,State对象负责维持Widget的状态,并提供一种机制触发Widget的重新构建。

5、State

State类是与StatefulWidget一起工作的核心部分之一,当创建一个createState方法,该方法返回一个State类的实例,这个State类负责管理Widget的状态,并提供了更新UI的方法。

State中保存的状态信息可以进行如下操作:

  • Widget构建时可以被同步读取;
  • Widget生命周期可以被改变,当State发生改变时,可以手动调用build重构Widget树,以达到更新UI的目的。
5.1 State生命周期
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    // return const CounterWidget();
    return const Text("测试文本");
  }
}

class CounterWidget extends StatefulWidget {
  final int initValue;

  const CounterWidget({super.key, required this.initValue});

  
  State<CounterWidget> createState() => _CounterWidgetStates();
}

class _CounterWidgetStates extends State<CounterWidget> {
  late int _counter;
  bool _hotReloadTrigger = false;

  
  void initState() {
    super.initState();
    _counter = widget.initValue;
    if (kDebugMode) {
      print("initState");
    }
  }

  
  Widget build(BuildContext context) {
    if (kDebugMode) {
      print("build");
    }
    // 使用 _hotReloadTrigger 来触发构建变化
    return Scaffold(
      appBar: AppBar(
        title: Text(_hotReloadTrigger ? 'Hot Reload Triggered' : 'Counter'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => setState(() => ++_counter),
          child: Text("$_counter"),
        ),
      ),
    );
  }

  
  void didUpdateWidget(covariant CounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (kDebugMode) {
      print("didUpdateWidget");
    }
  }

  
  void reassemble() {
    super.reassemble();
    if (kDebugMode) {
      print("reassemble");
    }
    // 改变属性以触发 didUpdateWidget
    setState(() {
      _hotReloadTrigger = !_hotReloadTrigger;
      // 创建新的 CounterWidget 实例以触发 didUpdateWidget
      final newWidget = CounterWidget(initValue: widget.initValue);
      if (newWidget != widget) {
        if (kDebugMode) {
          print("$newWidget");
        }
      }
    });
  }

  
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (kDebugMode) {
      print("didChangeDependencies");
    }
  }
}
  • 修改MyHomePage类,添加跳转按钮,控制台的输出日志如下:

    I/flutter ( 5101): initState
    I/flutter ( 5101): didChangeDependencies
    I/flutter ( 5101): build
    
  • 点击热重载,控制台的输出日志如下:

    I/flutter ( 5101): reassemble
    I/flutter ( 5101): build
    
  • 修改build,返回文本:

    class MyHomePage extends StatelessWidget {
      const MyHomePage({super.key});
    
      
      Widget build(BuildContext context) {
        // return const CounterWidget();
        return const Text("测试文本");
      }
    }
    
    • 输出日志为

      I/flutter ( 5101): reassemble
      I/flutter ( 5101): deactivate
      I/flutter ( 5101): dispose
      
5.2 回调函数说明
  • build():负责构建组件的实际UI,它在以下几种情况下调用:

    • 在调用initState()didUpdateWidget()setState()didChangeDependencies()这几种方法之后;
    • 在State对象从树中一个位置移除又插入到树的其他位置之后。
  • initState():当Widget第一次插入到树中调用,执行初始化工作的最佳时机,此方法只会调用一次,之后即使组件重建也不会再次调用;

  • didChangeDependencies():当State对象的依赖发生变化时会被调用,例如当应用的主题或者语言发生变化,Flutter framework会通知Widget调用此回调;

  • reassemble():为开发调试使用,点击热重载后会调用此方法;

  • didUpdateWidget():检测Widget树某一节点是否需要更新,在新旧Widget的Key和runtimeType同时相等时就调用didUpdateWidget()

  • deactivate():当State对象暂时从树中移除但未来可能重新插入的时候调用;

  • dispose():当State对象永久从树中移除时调用,只调用一次,调用后该State对象不能再用于构建UI。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部