Flutter 常用 Widget 分类速查表
按图中脉络,将核心组件分为 6 大类,并补充每类典型用途与要点。
分类 | 常见 Widget | 典型作用 / 备注 |
---|---|---|
布局 Widget (Layout) | 单子元素布局 Container 、Center 、Align 、FractionallySizedBox 、SizedBox 、FittedBox 、ConstrainedBox 、OverflowBox 、AspectRatio 、Offstage |
控制 单个子组件 的尺寸、位置或可见性。 Container = 装饰 + 约束 + 边距的一站式方案;Offstage 用于临时隐藏但保留状态。 |
多子元素布局 Row 、Column 、Stack 、IndexedStack 、Flow 、Table 、Wrap 、ListBody |
负责 多个子组件 排版。 Row/Column 线性排列;Stack 层叠定位;Wrap/Flow 流式换行;Table 网格;IndexedStack 仅显示指定索引子项。 |
|
可滚动 Widget | ListView , GridView , NestedScrollView , CustomScrollView |
内建滚动能力。NestedScrollView 适合协同滚动 AppBar;CustomScrollView 能组合多个 Sliver 实现复杂滚动效果。 |
绘制 & 效果 Widget | Opacity , Transform , DecoratedBox , FractionalTranslation , RotatedBox , ClipOval , ClipPath , ClipRect , CustomPaint , BackdropFilter |
图形变换、裁剪、透明度、装饰与自绘。CustomPaint 可绘制任意图形;BackdropFilter 做毛玻璃等高斯模糊。 |
图片与资源 Widget | Image , Icon , RawImage , (资源加载辅助:AssetBundle ) |
加载位图或矢量资源。RawImage 直接显示 ui.Image ;AssetBundle 为多平台统一的资源检索接口。 |
文本 Widget | Text , RichText |
渲染普通文本或富文本;RichText 支持多样式 TextSpan 嵌套。 |
样式 Widget | Padding , Theme , MediaQuery |
提供边距、全局主题与环境信息(屏幕尺寸、文字缩放、暗色模式)。 |
- 尺寸与约束不满足时,优先考虑
SizedBox
/ConstrainedBox
。 - 复杂滚动场景优先研究 Sliver 系列 与
CustomScrollView
。 - 动态裁剪或特效可组合
ClipPath
+BackdropFilter
。 - 跨页面共享配色/字体,统一写在
ThemeData
,并用Theme.of(context)
读取。
StatelessWidget与StatefulWidget
StatelessWidget 与 StatefulWidget 全面对比
维度 | StatelessWidget | StatefulWidget |
---|---|---|
核心定义 | 无可变状态 。构建后只能依赖 build() 里传入的 外部参数 (构造函数、InheritedWidget 、Provider 等)来决定外观。 |
拥有可变状态 。通过内部 State 对象保存数据,调用 setState() 后可触发重新构建自身及其子树。 |
典型场景 | - 纯展示组件 - Button、Icon、Logo 等简单静态 UI - 只依赖父级传值、不需自行更新 | - 表单输入、计时器、动画、Tab 切换 - 网络请求完成后刷新页面 - 需要局部记忆 / 交互的组件 |
生命周期 | ```dart | |
Widget build(BuildContext context) | ||
``` 只有一次 build ,依赖更新 ⇒ 整棵 Widget 重新创建。 |
常用回调: initState() →didChangeDependencies() →build() →setState() →dispose() |
|
更新机制 | 父级的 setState() 或 InheritedWidget 改变后,Flutter 框架会销毁旧实例,创建新实例执行 build() 。 |
调用自身 setState(fn) ⇒ 仅当前 StatefulWidget 重新 build() ,子树中未变化的内容利用 Element 树 Diff 被高效复用。 |
性能 | 占用内存少、构建开销低;如果 UI 真正静态,更利于框架优化。 | 略高,但局部刷新的粒度更细,避免整页重绘。正确拆分可获得更佳整体性能。 |
转换 | 业务增长需本地状态 → 可 快捷替换 : 1. 用 IDE 将类继承改为 StatefulWidget 2. 自动生成 State ,再挪动原 build() |
状态已不再需要 ⇒ 把业务状态上提或放入 Provider/Riverpod/BLoC,然后改为 StatelessWidget ,使组件更轻量。 |
易错点 | - 不支持 setState() ;如直接调用会报错。 |
- 忘记 dispose() 释放资源(AnimationController 、Stream 、Timer )。 - 在 build() 里创建 Future/Stream 导致反复执行;应缓存。 |
1. 快速示例对比
StatelessWidget
dart
class HelloText extends StatelessWidget {
final String name;
const HelloText({required this.name, super.key});
@override
Widget build(BuildContext context) {
return Text('Hello, $name');
}
}
StatefulWidget
dart
class CounterBtn extends StatefulWidget {
const CounterBtn({super.key});
@override
State<CounterBtn> createState() => _CounterBtnState();
}
class _CounterBtnState extends State<CounterBtn> {
int _count = 0;
void _inc() => setState(() => _count++);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _inc,
child: Text('Clicked $_count'),
);
}
}
2. 何时选谁?
-
完全静态或仅依赖外部数据 → Stateless。
-
需在组件内用
setState()
更新 → Stateful。 -
状态要跨组件 / 全局共享 → 把状态上提或使用状态管理,再让 UI 尽量保持 Stateless。
3. 状态拆分的小技巧
- 最小重绘原则 :
把仅因点击计数变化的部分包成StatefulWidget
,周围稳定区域仍用StatelessWidget
,减少重建。 AutomaticKeepAliveClientMixin
:
滑动页签PageView
/TabBarView
中要保持页面状态,可在State
里混入并重写wantKeepAlive => true;
。- Keys :
当控件类型相同但需要唯一身份(动画切换、列表重排)时,使用ValueKey/ObjectKey/UniqueKey
保持状态不丢失。
4. 小结
- StatelessWidget:纯展示、所有数据都从外部流入。
- StatefulWidget :自己的状态自己管,
setState()
后局部刷新。 - 合理拆分与状态上提可以兼顾 可维护性 与 性能。
- 如果组件既要管理内部细节,又要暴露事件给外部,推荐"内部 Stateful + 对外 Stateless API"的封装方式,提高复用度。
Android开发者如何快速上手Flutter布局开发
1. LinearLayout 在 Flutter 中等价于什么?
Android | Flutter | 说明 |
---|---|---|
LinearLayout (horizontal / vertical) |
Row (横向) Column (纵向) |
Row / Column 只负责子组件线性排列 ,额外尺寸/权重需要 Expanded 、Flexible 、Spacer 等配合。 |
dart
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('A'),
Text('B'),
],
)
2. RelativeLayout 在 Flutter 中等价于什么?
Android | Flutter | 用途 |
---|---|---|
RelativeLayout |
Stack + Positioned / Align |
Stack 允许子组件"层叠"摆放;通过 Positioned (绝对偏移)或 Align (相对父尺寸)来模拟 RelativeLayout 效果。 |
dart
Stack(
children: [
Container(color: Colors.blue), // 底层
Positioned(top: 8, right: 8, child: Icon(Icons.close)),
],
)
3. 如何使用 Widget 定义布局属性?
在 Flutter 中,"布局 + 样式"都是由 一层层 Widget 组合实现,而非 XML。常见属性对应的 Widget/参数:
功能 | Widget / 参数示例 |
---|---|
边距、内边距 | Padding(padding: EdgeInsets.all(8)) |
大小约束 | SizedBox(width: 100, height: 50) / ConstrainedBox |
对齐 | Align(alignment: Alignment.centerRight) |
权重(填充剩余) | Expanded(child: ...) 、Flexible(flex: 2, child: ...) |
背景装饰 | Container(decoration: BoxDecoration(color: Colors.red)) |
4. 如何分层布局?
- 透明层叠 :
Stack
- 滚动内部再分层 :
CustomScrollView
+ 各种Sliver
- 浮动/悬浮元素 :
Stack
+Positioned
放在屏幕顶层,或使用Scaffold
的floatingActionButton
、Drawer
、BottomSheet
等 slot。
5. 如何设置布局样式?
- 颜色 / 圆角 / 阴影 →
Container → BoxDecoration
- 字体 / 主题 →
ThemeData
+TextStyle
- 动画样式 →
AnimatedContainer
,AnimatedPositioned
,Hero
- 适配暗黑模式 → 读取
Theme.of(context).brightness
或MediaQuery.platformBrightnessOf(context)
dart
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [BoxShadow(blurRadius: 4, color: Colors.black26)],
),
child: const Text('样式示例'),
)
6. ScrollView 在 Flutter 中等价于什么?
Android | Flutter | 备注 |
---|---|---|
ScrollView / NestedScrollView |
SingleChildScrollView (单子组件) CustomScrollView (多 Sliver 组合滚动) |
若仅需包一段静态内容,用 SingleChildScrollView ;复杂协同滚动(如 AppBar 吸顶)用 NestedScrollView 或 CustomScrollView + Slivers。 |
7. 谁是 Flutter 的列表组件?
-
ListView
:最常用ListView(children: [...])
静态少量ListView.builder(itemCount: n, itemBuilder: ...)
懒加载ListView.separated(...)
带分割线
-
GridView
:网格 -
ListView.custom
/SliverList
:高度自定义 -
ReorderableListView
:可拖拽排序 -
AnimatedList
:插入/删除动画
8. 如何知道点击了列表中哪个 item?
dart
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
onTap: () {
print('点击了第 $index 个:${items[index]}');
},
);
},
)
- 可替换
ListTile
为GestureDetector
,InkWell
等,自由处理onTap
。
9. 如何动态更新 ListView?
-
StatefulWidget + setState
dartsetState(() { items.add('新元素'); });
-
更平滑的插入/删除动画 → 用
AnimatedList
-
大规模异步数据 → 结合
StreamBuilder
/FutureBuilder
或状态管理(Provider、Riverpod、Bloc 等) -
局部刷新 → 使用
ValueNotifier<List<T>>
+ValueListenableBuilder
或者ListView.builder
本身的懒加载特性。
🌟 小结
- 线性布局 :
Row
/Column
- 相对 / 层叠布局 :
Stack + Positioned | Align
- 滚动 :
SingleChildScrollView
、ListView
、CustomScrollView
- 列表 :
ListView.builder
最普遍;AnimatedList
、ReorderableListView
提供更高阶交互 - 点击 & 动态更新 :
InkWell/GestureDetector
+setState()
,或选择更合适的状态管理/动画组件
Android 组件 / 布局 | Flutter 对应 Widget | 备注与补充 |
---|---|---|
LinearLayout (vertical / horizontal) | Column / Row |
线性排列;用 Expanded / Flexible 实现权重,Spacer 占位 |
RelativeLayout / FrameLayout | Stack + Positioned / Align |
层叠摆放、支持绝对或相对父布局定位 |
ConstraintLayout | Stack + 约束插件(flutter_layout_grid / flutter_constraintlayout ) 或直接拆分为多层 Row / Column / Expanded |
官方无完全等价物,复杂场景可引入第三方 |
ScrollView | SingleChildScrollView |
仅能包裹单个子组件(可再套 Column 等) |
NestedScrollView | NestedScrollView (Flutter 同名) 或 CustomScrollView + SliverAppBar/SliverList |
协同滚动吸顶效果 |
ListView (Adapter) | ListView.builder |
懒加载;itemBuilder 相当于 Adapter |
RecyclerView + DiffUtil | ListView.builder + AnimatedList / 第三方 flutter_staggered_grid_view |
默认已复用 item,无需 ViewHolder;动画用 AnimatedList |
GridView | GridView.count / GridView.builder |
crossAxisCount 、SliverGridDelegate 控制列数与间距 |
ViewPager / ViewPager2 | PageView |
支持横纵向、无限轮播(配合 PageController) |
TabLayout + ViewPager | TabBar + TabBarView |
TabController 关联 |
DrawerLayout | Scaffold.drawer / Scaffold.endDrawer |
自动处理滑动抽屉 |
Toolbar / AppBar | AppBar (Scaffold.appBar ) |
内建返回按钮、菜单 |
FloatingActionButton | FloatingActionButton (同名) |
放在 Scaffold.floatingActionButton |
CardView | Card |
带圆角与阴影 |
Snackbar | SnackBar |
ScaffoldMessenger.of(context).showSnackBar() |
AlertDialog | AlertDialog |
showDialog(context: ...) |
Switch (SwitchCompat ) |
Switch / SwitchListTile |
|
CheckBox / RadioButton | Checkbox / Radio (RadioListTile ) |
|
EditText | TextField / TextFormField |
输入装饰用 InputDecoration |
ProgressBar | CircularProgressIndicator / LinearProgressIndicator |