Flutter的Widget世界

422 阅读7分钟

Flutter 常用 Widget 分类速查表

按图中脉络,将核心组件分为 6 大类,并补充每类典型用途与要点。

分类常见 Widget典型作用 / 备注
布局 Widget
(Layout)
单子元素布局
ContainerCenterAlignFractionallySizedBoxSizedBoxFittedBoxConstrainedBoxOverflowBoxAspectRatioOffstage
控制 单个子组件 的尺寸、位置或可见性。
Container = 装饰 + 约束 + 边距的一站式方案;Offstage 用于临时隐藏但保留状态。
多子元素布局
RowColumnStackIndexedStackFlowTableWrapListBody
负责 多个子组件 排版。
Row/Column 线性排列;Stack 层叠定位;Wrap/Flow 流式换行;Table 网格;IndexedStack 仅显示指定索引子项。
可滚动 WidgetListView, GridView, NestedScrollView, CustomScrollView内建滚动能力。NestedScrollView 适合协同滚动 AppBar;CustomScrollView 能组合多个 Sliver 实现复杂滚动效果。
绘制 & 效果 WidgetOpacity, Transform, DecoratedBox, FractionalTranslation, RotatedBox, ClipOval, ClipPath, ClipRect, CustomPaint, BackdropFilter图形变换、裁剪、透明度、装饰与自绘。CustomPaint 可绘制任意图形;BackdropFilter 做毛玻璃等高斯模糊。
图片与资源 WidgetImage, Icon, RawImage, (资源加载辅助:AssetBundle)加载位图或矢量资源。RawImage 直接显示 ui.ImageAssetBundle 为多平台统一的资源检索接口。
文本 WidgetText, RichText渲染普通文本或富文本;RichText 支持多样式 TextSpan 嵌套。
样式 WidgetPadding, Theme, MediaQuery提供边距、全局主题与环境信息(屏幕尺寸、文字缩放、暗色模式)。
  1. 尺寸与约束不满足时,优先考虑 SizedBox / ConstrainedBox
  2. 复杂滚动场景优先研究 Sliver 系列CustomScrollView
  3. 动态裁剪或特效可组合 ClipPath + BackdropFilter
  4. 跨页面共享配色/字体,统一写在 ThemeData,并用 Theme.of(context) 读取。

StatelessWidget与StatefulWidget

StatelessWidget 与 StatefulWidget 全面对比

维度StatelessWidgetStatefulWidget
核心定义无可变状态。构建后只能依赖 build() 里传入的 外部参数(构造函数、InheritedWidgetProvider 等)来决定外观。拥有可变状态。通过内部 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() 释放资源(AnimationControllerStreamTimer)。 - 在 build() 里创建 Future/Stream 导致反复执行;应缓存。

1. 快速示例对比

StatelessWidget

class HelloText extends StatelessWidget {
  final String name;
  const HelloText({required this.name, super.key});

  @override
  Widget build(BuildContext context) {
    return Text('Hello, $name');
  }
}

StatefulWidget

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. 状态拆分的小技巧

  1. 最小重绘原则
    把仅因点击计数变化的部分包成 StatefulWidget,周围稳定区域仍用 StatelessWidget,减少重建。
  2. AutomaticKeepAliveClientMixin
    滑动页签 PageView/TabBarView 中要保持页面状态,可在 State 里混入并重写 wantKeepAlive => true;
  3. Keys
    当控件类型相同但需要唯一身份(动画切换、列表重排)时,使用 ValueKey/ObjectKey/UniqueKey 保持状态不丢失。

4. 小结

  • StatelessWidget:纯展示、所有数据都从外部流入。
  • StatefulWidget:自己的状态自己管,setState() 后局部刷新。
  • 合理拆分与状态上提可以兼顾 可维护性性能
  • 如果组件既要管理内部细节,又要暴露事件给外部,推荐“内部 Stateful + 对外 Stateless API”的封装方式,提高复用度。

Android开发者如何快速上手Flutter布局开发

1. LinearLayout 在 Flutter 中等价于什么?

AndroidFlutter说明
LinearLayout (horizontal / vertical)Row(横向) Column(纵向)Row / Column 只负责子组件线性排列,额外尺寸/权重需要 ExpandedFlexibleSpacer 等配合。
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Text('A'),
    Text('B'),
  ],
)

2. RelativeLayout 在 Flutter 中等价于什么?

AndroidFlutter用途
RelativeLayoutStack + Positioned / AlignStack 允许子组件“层叠”摆放;通过 Positioned(绝对偏移)或 Align(相对父尺寸)来模拟 RelativeLayout 效果。
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 放在屏幕顶层,或使用 ScaffoldfloatingActionButtonDrawerBottomSheet 等 slot。

5. 如何设置布局样式?

  • 颜色 / 圆角 / 阴影Container → BoxDecoration
  • 字体 / 主题ThemeData + TextStyle
  • 动画样式AnimatedContainer, AnimatedPositioned, Hero
  • 适配暗黑模式 → 读取 Theme.of(context).brightnessMediaQuery.platformBrightnessOf(context)
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 中等价于什么?

AndroidFlutter备注
ScrollView / NestedScrollViewSingleChildScrollView(单子组件) CustomScrollView(多 Sliver 组合滚动)若仅需包一段静态内容,用 SingleChildScrollView;复杂协同滚动(如 AppBar 吸顶)用 NestedScrollViewCustomScrollView + Slivers。

7. 谁是 Flutter 的列表组件?

  • ListView:最常用

    • ListView(children: [...]) 静态少量
    • ListView.builder(itemCount: n, itemBuilder: ...) 懒加载
    • ListView.separated(...) 带分割线
  • GridView:网格

  • ListView.custom / SliverList:高度自定义

  • ReorderableListView:可拖拽排序

  • AnimatedList:插入/删除动画


8. 如何知道点击了列表中哪个 item?

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
      onTap: () {
        print('点击了第 $index 个:${items[index]}');
      },
    );
  },
)
  • 可替换 ListTileGestureDetector, InkWell 等,自由处理 onTap

9. 如何动态更新 ListView?

  1. StatefulWidget + setState

    setState(() {
      items.add('新元素');
    });
    
  2. 更平滑的插入/删除动画 → 用 AnimatedList

  3. 大规模异步数据 → 结合 StreamBuilder / FutureBuilder 或状态管理(Provider、Riverpod、Bloc 等)

  4. 局部刷新 → 使用 ValueNotifier<List<T>> + ValueListenableBuilder 或者 ListView.builder 本身的懒加载特性。


🌟 小结

  • 线性布局Row / Column
  • 相对 / 层叠布局Stack + Positioned | Align
  • 滚动SingleChildScrollViewListViewCustomScrollView
  • 列表ListView.builder 最普遍;AnimatedListReorderableListView 提供更高阶交互
  • 点击 & 动态更新InkWell/GestureDetector + setState(),或选择更合适的状态管理/动画组件
Android 组件 / 布局Flutter 对应 Widget备注与补充
LinearLayout (vertical / horizontal)Column / Row线性排列;用 Expanded / Flexible 实现权重,Spacer 占位
RelativeLayout / FrameLayoutStack + Positioned / Align层叠摆放、支持绝对或相对父布局定位
ConstraintLayoutStack + 约束插件(flutter_layout_grid / flutter_constraintlayout) 或直接拆分为多层 Row / Column / Expanded官方无完全等价物,复杂场景可引入第三方
ScrollViewSingleChildScrollView仅能包裹单个子组件(可再套 Column 等)
NestedScrollViewNestedScrollView(Flutter 同名) 或 CustomScrollView + SliverAppBar/SliverList协同滚动吸顶效果
ListView (Adapter)ListView.builder懒加载;itemBuilder 相当于 Adapter
RecyclerView + DiffUtilListView.builder + AnimatedList / 第三方 flutter_staggered_grid_view默认已复用 item,无需 ViewHolder;动画用 AnimatedList
GridViewGridView.count / GridView.buildercrossAxisCountSliverGridDelegate 控制列数与间距
ViewPager / ViewPager2PageView支持横纵向、无限轮播(配合 PageController)
TabLayout + ViewPagerTabBar + TabBarViewTabController 关联
DrawerLayoutScaffold.drawer / Scaffold.endDrawer自动处理滑动抽屉
Toolbar / AppBarAppBarScaffold.appBar内建返回按钮、菜单
FloatingActionButtonFloatingActionButton(同名)放在 Scaffold.floatingActionButton
CardViewCard带圆角与阴影
SnackbarSnackBarScaffoldMessenger.of(context).showSnackBar()
AlertDialogAlertDialogshowDialog(context: …)
Switch (SwitchCompat)Switch / SwitchListTile
CheckBox / RadioButtonCheckbox / Radio (RadioListTile)
EditTextTextField / TextFormField输入装饰用 InputDecoration
ProgressBarCircularProgressIndicator / LinearProgressIndicator