<返回目录     Powered by claud/xia兄

第3课:Widget基础

StatelessWidget、StatefulWidget、Widget树、生命周期

📋 学习前提

🎯 课程目标

通过本课程,你将能够:

🚀 什么是Widget?

Widget是Flutter应用的基本构建块。在Flutter中,一切都是Widget - 从按钮、文本到布局、动画,甚至整个应用本身都是Widget。

Widget的核心特点

💡 重要概念:Widget vs Element vs RenderObject

Flutter框架包含三个核心层次:

  • Widget:描述UI的配置(不可变)
  • Element:管理Widget的生命周期(可变)
  • RenderObject:负责实际的渲染和布局

🌳 Widget树(Widget Tree)

Flutter应用由Widget树构成,每个Widget都可以包含子Widget,形成层次结构:

MaterialApp
├── Scaffold
│ ├── AppBar
│ │ └── Text('我的应用')
│ ├── Body: Column
│ │ ├── Text('欢迎使用Flutter')
│ │ ├── ElevatedButton
│ │ └── Image
│ └── FloatingActionButton
└── ThemeData

Widget树的构建过程

  1. 构建阶段:Flutter调用Widget的build()方法构建Widget树
  2. 布局阶段:确定每个Widget在屏幕上的位置和大小
  3. 绘制阶段:将Widget树绘制到屏幕上
💡 最佳实践:保持Widget树简洁

将复杂的UI拆分成多个小的、可重用的Widget,这样可以提高代码的可读性和维护性。

📊 StatelessWidget(无状态组件)

什么是StatelessWidget?

StatelessWidget是不可变的Widget,一旦创建就不会改变。它适合用于静态内容的展示。

import 'package:flutter/material.dart'; class MyTextWidget extends StatelessWidget { final String text; final double fontSize; // 构造函数 const MyTextWidget({ super.key, required this.text, this.fontSize = 16.0, }); @override Widget build(BuildContext context) { return Text( text, style: TextStyle(fontSize: fontSize), ); } } // 使用示例 class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: MyTextWidget( text: 'Hello, Flutter!', fontSize: 24.0, ), ), ), ); } }

StatelessWidget的特点

📝 示例:创建可重用的按钮组件
class CustomButton extends StatelessWidget { final String text; final VoidCallback onPressed; final Color backgroundColor; const CustomButton({ super.key, required this.text, required this.onPressed, this.backgroundColor = Colors.blue, }); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: backgroundColor, padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( text, style: const TextStyle(fontSize: 16), ), ); } } // 使用 CustomButton( text: '点击我', onPressed: () { print('按钮被点击了'); }, backgroundColor: Colors.green, )

🔄 StatefulWidget(有状态组件)

什么是StatefulWidget?

StatefulWidget是可变的Widget,它包含一个可变的状态对象(State),当状态改变时,Widget会重新构建。

import 'package:flutter/material.dart'; class CounterWidget extends StatefulWidget { final String title; const CounterWidget({ super.key, required this.title, }); @override State createState() => _CounterWidgetState(); } class _CounterWidgetState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } void _decrementCounter() { setState(() { _counter--; }); } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( widget.title, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 20), Text( '计数: $_counter', style: const TextStyle(fontSize: 36), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: _decrementCounter, child: const Text('减少'), ), const SizedBox(width: 20), ElevatedButton( onPressed: _incrementCounter, child: const Text('增加'), ), ], ), ], ); } }

StatefulWidget的生命周期

生命周期方法 调用时机 用途
createState() Widget首次插入到树中时 创建State对象
initState() State对象创建后,build()之前 初始化状态,订阅数据流
didChangeDependencies() initState()之后,或依赖的InheritedWidget改变时 处理依赖关系变化
build() 需要构建UI时 构建Widget树
didUpdateWidget() Widget配置改变时 响应Widget属性变化
setState() 需要更新UI时 通知框架重新构建
deactivate() Widget从树中移除时 清理临时状态
dispose() Widget永久移除时 释放资源,取消订阅
💡 重要概念:setState()的作用

setState()方法通知Flutter框架状态已改变,需要重新构建Widget。它会在当前帧结束时触发build()方法。

⚖️ StatelessWidget vs StatefulWidget

特性 StatelessWidget StatefulWidget
可变性 不可变 可变(通过State对象)
性能 更高(可缓存) 稍低(需要管理状态)
使用场景 静态内容展示 交互式组件,动态内容
复杂度 简单 相对复杂
生命周期 只有build()方法 完整的生命周期方法

选择原则

💡 最佳实践:状态提升(State Lifting)

将状态管理提升到共同的父Widget中,可以使子Widget保持无状态,提高可重用性。

🔧 Widget的属性和方法

常用Widget属性

// key属性:用于标识Widget const MyWidget(key: ValueKey('unique_id')); // 样式属性 Container( width: 100, height: 50, margin: const EdgeInsets.all(10), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.black, width: 1), ), ) // 子Widget属性 Column( mainAxisAlignment: MainAxisAlignment.center, // 主轴对齐 crossAxisAlignment: CrossAxisAlignment.start, // 交叉轴对齐 children: [ Text('第一个子元素'), Text('第二个子元素'), ], )

Widget的约束系统

Flutter使用约束(Constraints)系统来布局Widget:

📝 示例:理解约束系统
// Container在Column中(松约束) Column( children: [ Container( color: Colors.red, child: Text('这个Container会尽可能小'), ), ], ) // Container在Center中(无界约束) Center( child: Container( color: Colors.blue, width: 100, // 必须指定大小 height: 100, child: Text('这个Container有固定大小'), ), )

📚 实践练习

练习1:创建个人信息卡片

创建一个可重用的个人信息卡片组件:

  1. 使用StatelessWidget创建PersonCard组件
  2. 包含头像、姓名、年龄、职业等信息
  3. 支持自定义主题颜色
  4. 添加点击回调函数

练习2:实现购物车功能

使用StatefulWidget实现简单的购物车:

  1. 显示商品列表和数量
  2. 实现增加、减少商品数量的功能
  3. 计算总价并实时更新
  4. 添加清空购物车功能

练习3:Widget生命周期实验

创建一个Widget来观察生命周期方法:

  1. 在每个生命周期方法中添加打印语句
  2. 观察Widget的创建、更新、销毁过程
  3. 测试热重载对生命周期的影响

🔍 常见问题解答

Q: 什么时候应该使用StatefulWidget?

A: 当Widget需要管理内部状态(如用户输入、动画、计时器等)时使用StatefulWidget。对于纯展示性内容,使用StatelessWidget。

Q: setState()为什么要在匿名函数中调用?

A: setState()接受一个回调函数,这样可以确保在状态更新完成后才触发重建,避免竞态条件。

Q: Widget的key属性有什么作用?

A: key用于标识Widget,当Widget树重建时,Flutter使用key来匹配新旧Widget,保持状态。

Q: 如何优化Widget的性能?

A: 使用const构造函数创建Widget、避免在build()方法中创建新对象、合理使用Key等。

📖 总结

在本课程中,我们深入学习了Flutter的核心概念 - Widget:

Widget是Flutter开发的基石,熟练掌握Widget的使用是成为Flutter开发者的关键一步。