2.1 Widget 基础

在 Flutter 中,"一切皆 Widget"。理解 Widget 的本质、树结构与生命周期,是掌握 Flutter UI 开发的基石。


一、StatelessWidget 与 StatefulWidget

1.1 StatelessWidget(无状态组件)

StatelessWidget 是不可变的,一旦创建其属性无法改变,适合纯展示型 UI:

dart 复制代码
class UserCard extends StatelessWidget {
  final String name;
  final String avatar;
  final bool isOnline;

  const UserCard({
    super.key,
    required this.name,
    required this.avatar,
    this.isOnline = false,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        leading: CircleAvatar(backgroundImage: NetworkImage(avatar)),
        title: Text(name),
        trailing: Icon(
          Icons.circle,
          color: isOnline ? Colors.green : Colors.grey,
          size: 12,
        ),
      ),
    );
  }
}

使用场景:

  • 展示静态内容(文字、图片、图标)
  • 仅依赖外部传入的参数渲染
  • 无需维护内部状态

1.2 StatefulWidget(有状态组件)

StatefulWidget 与 State 对象分离,State 可变且持久化:

dart 复制代码
class CounterWidget extends StatefulWidget {
  final int initialCount;

  const CounterWidget({super.key, this.initialCount = 0});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  late int _count;

  @override
  void initState() {
    super.initState();
    _count = widget.initialCount; // 通过 widget 访问 StatefulWidget 属性
  }

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('+1'),
        ),
      ],
    );
  }

  @override
  void dispose() {
    // 释放资源(控制器、流订阅等)
    super.dispose();
  }
}

1.3 对比总结

对比项 StatelessWidget StatefulWidget
状态 无内部状态 有可变内部状态
可变性 不可变 State 可变
性能 更高效 状态变化时重建
适用场景 纯展示 交互、动画、异步
生命周期 build() initState/build/dispose

二、Widget 树与 Element 树

2.1 三棵树

Flutter 内部维护三棵并行的树:

复制代码
Widget Tree (不可变描述)
     ↓ 创建/挂载
Element Tree (可变实例,持有 State)
     ↓ 驱动
RenderObject Tree (布局与绘制)
职责 生命周期
Widget Tree UI 声明(不可变的配置描述) 频繁重建
Element Tree 持久化的实例,持有 State 尽量复用
RenderObject Tree 实际布局与绘制 更新最小化

2.2 build() 机制

dart 复制代码
@override
Widget build(BuildContext context) {
  // build() 应该是纯函数,不产生副作用
  // 可以被多次调用,应保持幂等性
  return Container(
    color: Theme.of(context).primaryColor, // 通过 context 读取主题
    child: Text(_count.toString()),
  );
}

重要原则:

  • build() 应为纯函数(无副作用)
  • 不在 build() 中做耗时操作(网络请求、数据库查询)
  • Flutter 会尽可能复用 Element,只重建必要部分

2.3 什么时候触发 build()?

触发条件 说明
setState() 调用 当前 State 及其子树标记为 dirty
父 Widget 重建 父 build() 返回新 Widget 时
InheritedWidget 变化 依赖的 InheritedWidget 数据更新
Hot Reload 开发时强制重建

三、Key 的作用

Key 帮助 Flutter 识别 Widget 在树中的身份,实现正确的 Element 复用与状态保持。

3.1 为什么需要 Key?

dart 复制代码
// 问题示例:交换两个有状态计数器
// 没有 Key 时,Flutter 只看 Widget 类型,可能复用错误的 State
Row(children: [
  CounterWidget(),  // ❌ 交换位置后,State 不随 Widget 移动
  CounterWidget(),
])

// 使用 Key 后,Flutter 能正确识别并移动对应 State
Row(children: [
  CounterWidget(key: ValueKey('counter_a')),  // ✅
  CounterWidget(key: ValueKey('counter_b')),
])

3.2 LocalKey 类型

dart 复制代码
// ValueKey:用于值唯一的场景(ID、字符串等)
ListView.builder(
  itemBuilder: (context, index) => ListItem(
    key: ValueKey(items[index].id), // 使用数据 ID 作为 Key
    item: items[index],
  ),
)

// ObjectKey:用于对象引用唯一的场景
ListItem(key: ObjectKey(userObject))

// UniqueKey:每次构建都生成新 Key(强制重建)
Container(key: UniqueKey()) // 每次 build 都是全新实例

3.3 GlobalKey

GlobalKey 可以在 Widget 树任意位置访问对应的 State 或 RenderObject:

dart 复制代码
final formKey = GlobalKey<FormState>();
final scaffoldKey = GlobalKey<ScaffoldState>();

// 在 build 中
Form(
  key: formKey,
  child: Column(children: [
    TextFormField(...),
    ElevatedButton(
      onPressed: () {
        // 通过 GlobalKey 访问 FormState
        if (formKey.currentState!.validate()) {
          formKey.currentState!.save();
        }
      },
      child: const Text('提交'),
    ),
  ]),
)

⚠️ 注意: GlobalKey 开销较大,不要滥用。优先使用 LocalKey。


四、Widget 的生命周期

4.1 StatefulWidget 生命周期

复制代码
创建 → initState() → didChangeDependencies() → build()
                                                    ↑
更新 → setState() / didUpdateWidget() → build() ───┘
                                                    
销毁 → deactivate() → dispose()
dart 复制代码
class LifecycleDemo extends StatefulWidget {
  final String param;
  const LifecycleDemo({super.key, required this.param});

  @override
  State<LifecycleDemo> createState() => _LifecycleDemoState();
}

class _LifecycleDemoState extends State<LifecycleDemo> {
  late StreamSubscription _sub;

  @override
  void initState() {
    super.initState();
    // ✅ 初始化:只调用一次
    // 订阅流、初始化控制器、获取初始数据
    _sub = someStream.listen((data) => setState(() {}));
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // ✅ 依赖的 InheritedWidget 变化时调用
    // 第一次 build 前也会调用
  }

  @override
  void didUpdateWidget(covariant LifecycleDemo oldWidget) {
    super.didUpdateWidget(oldWidget);
    // ✅ 父 Widget 重建导致本 Widget 配置更新时调用
    if (oldWidget.param != widget.param) {
      // 响应参数变化
    }
  }

  @override
  Widget build(BuildContext context) {
    return Text(widget.param);
  }

  @override
  void deactivate() {
    // ✅ 从树中移除时调用(可能重新插入,如 Navigator)
    super.deactivate();
  }

  @override
  void dispose() {
    // ✅ 永久销毁,释放所有资源
    _sub.cancel();
    super.dispose();
  }
}

4.2 生命周期方法详解

方法 调用时机 常见用途
initState() 创建后第一次调用,仅一次 初始化数据、订阅流
didChangeDependencies() 依赖变化时 读取 InheritedWidget
build() 有变化需要重绘时 返回 Widget 树
didUpdateWidget() 父传参变化时 响应参数更新
deactivate() 从树中暂时移除 暂停操作
dispose() 永久销毁 取消订阅、关闭控制器

五、组合优于继承

Flutter 强调通过组合 Widget 而非继承来构建复杂 UI。

5.1 Widget 组合模式

dart 复制代码
// ✅ 组合方式:将多个小 Widget 组合成大 Widget
class ProfileHeader extends StatelessWidget {
  final User user;
  const ProfileHeader({super.key, required this.user});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        AvatarWidget(url: user.avatarUrl),      // 独立头像组件
        UserNameWidget(name: user.name),         // 独立名称组件
        FollowButton(userId: user.id),           // 独立关注按钮
      ],
    );
  }
}

5.2 自定义 Widget 最佳实践

dart 复制代码
// ✅ 职责单一:一个 Widget 做一件事
class PrimaryButton extends StatelessWidget {
  final String label;
  final VoidCallback? onPressed;
  final bool isLoading;

  const PrimaryButton({
    super.key,
    required this.label,
    this.onPressed,
    this.isLoading = false,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: isLoading ? null : onPressed,
      style: ElevatedButton.styleFrom(
        minimumSize: const Size.fromHeight(48),
      ),
      child: isLoading
          ? const SizedBox(
              width: 20,
              height: 20,
              child: CircularProgressIndicator(strokeWidth: 2),
            )
          : Text(label),
    );
  }
}

小结

知识点 核心要点
StatelessWidget 不可变,纯展示,性能高
StatefulWidget State 可变,setState 触发重建
三棵树 Widget(描述)→ Element(实例)→ RenderObject(渲染)
Key 帮助 Flutter 正确识别和复用 Element,避免状态错误
GlobalKey 跨 Widget 访问 State,慎用
生命周期 initState → build → didUpdateWidget → dispose
组合优于继承 小组件组合成大组件,保持职责单一

👉 下一节:2.2 布局系统

相关推荐
亚历克斯神2 小时前
Flutter 组件 genkit 的适配 鸿蒙Harmony 深度进阶 - 驾驭模型幻觉审计、实现鸿蒙端多维 RAG 向量对齐与端云协同 AI 指挥中心方案
flutter·harmonyos·鸿蒙·openharmony
浮芷.2 小时前
开源鸿蒙跨平台Flutter开发:考试资料共享平台应用
科技·flutter·华为·开源·harmonyos·鸿蒙
AI_零食2 小时前
开源鸿蒙跨平台Flutter开发:快递单号批量查询应用
学习·flutter·华为·开源·harmonyos·鸿蒙
旺仔大牛2 小时前
Flutter中StatefulWidget的生命周期
flutter·statefulwidget
浮芷.2 小时前
开源鸿蒙跨平台Flutter开发:校园兼职信息发布应用
科技·flutter·华为·开源·harmonyos·鸿蒙
AI_零食2 小时前
开源鸿蒙跨平台Flutter开发:密码生成器应用
网络·学习·flutter·华为·开源·harmonyos·鸿蒙
AI_零食2 小时前
开源鸿蒙跨平台Flutter开发:生日纪念日提醒应用
运维·flutter·开源·harmonyos·鸿蒙
世人万千丶2 小时前
Flutter 框架跨平台鸿蒙开发 - AR寻宝探险游戏应用
学习·flutter·游戏·华为·开源·ar·harmonyos
李李李勃谦2 小时前
Flutter 框架跨平台鸿蒙开发 - 天气生活指数应用
flutter·华为·harmonyos