Skip to content

速查 01 · Flutter ​

按问题索引。Ctrl+F 页内搜索。 系统学习见 Flutter 教程系列,原理见 03 篇渲染原理。

目录 ​


常用 Widget 速查 ​

前端 → Flutter 对照 ​

前端Flutter
<div>Container / SizedBox / Padding
<span>Text / Text.rich
flex 布局Row / Column / Flex
position: absoluteStack + Positioned
overflow: autoSingleChildScrollView / ListView
display: gridGridView / Wrap
z-indexStack 中的顺序(后面的在上层)
position: fixedStack 放最外层 或 BottomAppBar
<button onClick>ElevatedButton(onPressed:) / GestureDetector / InkWell
<input>TextField + TextEditingController
<img>Image.network / Image.asset
loading 转圈CircularProgressIndicator
v-if / {cond && <X/>}if (cond) X()(Dart 的 if 可直接写在集合里)
v-for / .map()ListView.builder / for (var x in list) X()
box-shadowBoxShadow in BoxDecoration
border-radiusBorderRadius.circular(8)

高频 Widget 一行写法 ​

dart
const SizedBox(height: 12)                    // 间距(比 Container+height 轻量)
const Spacer()                                // Row/Column 中撑开剩余空间
Expanded(child: X())                          // 占满剩余空间
Flexible(child: X())                          // 可小可大,不强制撑满
Wrap(spacing: 8, runSpacing: 8, children: []) // 自动换行(标签流)
const Divider(height: 1)
ClipRRect(borderRadius: BorderRadius.circular(8), child: Image.network(url))
Opacity(opacity: 0.5, child: X())             // 透明(比改颜色省重建)
Visibility(visible: cond, child: X())
const Center(child: X())
SafeArea(child: X())                          // 避开刘海/状态栏
Material(color: Colors.transparent, child: InkWell(onTap: () {}, child: X()))  // 水波纹点击

布局类 ​

常见布局模板 ​

dart
// 左右结构(左固定宽,右占满)
Row(children: [const SizedBox(width: 80), Expanded(child: right)])

// 上下结构(内容 + 底部按钮)
Column(children: [Expanded(child: content), const SubmitButton()])

// 叠加(右上角角标)
Stack(children: [content, const Positioned(right: 8, top: 8, child: Badge())])

// 居中 + 最大宽度(Web/平板适配)
Center(child: ConstrainedBox(
  constraints: const BoxConstraints(maxWidth: 480),
  child: content,
))

// 滚动 + 键盘不遮挡
SingleChildScrollView(
  padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
  child: form,
)

尺寸与约束 ​

dart
MediaQuery.of(context).size.width          // 屏幕宽(会触发重建,慎用)
MediaQuery.sizeOf(context).width           // ✅ 推荐:只在尺寸变化时重建
LayoutBuilder(builder: (ctx, c) => c.maxWidth > 600 ? wide() : narrow())  // 响应式
double.infinity                            // 撑满父约束允许的最大值
BoxConstraints.tightFor(width: 100)        // 固定尺寸约束

报错速查 ​

报错信息(关键词)原因解法
RenderFlex overflowed by XX pixelsRow/Column 内容超出内容用 Expanded 包;或外层改 SingleChildScrollView
Vertical viewport was given unbounded heightListView 放在 Column 里没有高度约束用 Expanded 包 ListView,或加 shrinkWrap: true
Incorrect use of ParentDataWidgetExpanded/Flexible 的直接父级不是 Row/Column/Flex检查父组件,或移除 Expanded
setState() called after dispose()异步回调时页面已销毁回调里先判 if (!mounted) return;
Looking up a deactivated widget's ancestor用了已卸载页面的 context用全局 navigatorKey,或提前取 Provider 再 await
A RenderFlex overflowed + 键盘弹起键盘挤压Scaffold(resizeToAvoidBottomInset: true) + SingleChildScrollView
Null check operator used on a null value! 用在 null 上改 ?. + ?? 默认值;检查异步时序
type 'X' is not a subtype of 'Y'类型不匹配(常见于 JSON 解析)检查 Map<String, dynamic> 的实际类型
MissingPluginException平台通道未实现,或热重载后原生代码未生效冷启动(Full Restart);核对 channel 名,见21 篇
Waiting for another flutter command有卡住的 flutter 进程删 flutter/bin/cache/lockfile,或重启 IDE

生命周期 ​

StatefulWidget 完整顺序 ​

createState → initState → didChangeDependencies → build
   → (setState / 依赖变化) → didUpdateWidget → build
   → deactivate → dispose
方法调用时机适合做什么
initState插入树时一次初始化 controller、发起首次请求(不能用 context 做依赖查找,用 WidgetsBinding.addPostFrameCallback)
didChangeDependenciesinitState 后、依赖变化时依赖 InheritedWidget/Provider 的初始化
didUpdateWidget父组件重建导致配置变化对比 oldWidget 做响应
dispose永久移除必须释放 controller/stream/timer
dart
late final TextEditingController _ctrl;
late final StreamSubscription _sub;

@override
void initState() {
  super.initState();
  _ctrl = TextEditingController();
  _sub = stream.listen((_) => setState(() {}));
}

@override
void dispose() {
  _ctrl.dispose();       // 漏了就内存泄漏
  _sub.cancel();
  super.dispose();       // 写在最后
}

原理与 Key 体系见 03 篇。


状态与重建 ​

dart
// Riverpod 精确订阅(只有 name 变才重建)
final name = ref.watch(userProvider.select((u) => u.name));

// 读一次,不订阅
ref.read(counterProvider.notifier).increment();

// 监听副作用(弹窗/导航)
ref.listen(errorProvider, (prev, next) { if (next != null) showSnackBar(next); });

// 只在 build 之外刷新
if (mounted) setState(() {});

减少重建三件套:const 构造 · select 精确订阅 · 把变化部分下沉成小 Widget。 性能排查见 22 篇。


导航路由 ​

dart
// GoRouter(本项目方案,见 13 篇)
context.push('/detail/$id');              // 入栈
context.go('/home');                      // 替换栈
context.pop();                            // 返回
context.replace('/login');                // 替换当前页

// 原生 Navigator
Navigator.of(context).push(MaterialPageRoute(builder: (_) => const DetailPage()));
Navigator.of(context).pushNamed('/detail', arguments: {'id': 1});

// 返回值
final result = await context.push<bool>('/edit');

异步与网络 ​

dart
// FutureUI:三态渲染
FutureBuilder<Data>(
  future: repo.load(),
  builder: (ctx, snap) {
    if (snap.connectionState == ConnectionState.waiting) return const CircularProgressIndicator();
    if (snap.hasError) return Text('出错: ${snap.error}');
    return Content(snap.data!);
  },
)

// 并发等待
final (a, b) = await (repo.loadA(), repo.loadB()).wait;   // Dart 3 Records

// 超时
final r = await api.fetch().timeout(const Duration(seconds: 10));

// 防抖(搜索框)
Timer? _t;
void onChanged(String v) {
  _t?.cancel();
  _t = Timer(const Duration(milliseconds: 300), () => search(v));
}

数据层链路见 09 篇。


列表与滚动 ​

dart
// 长列表(懒加载,必用 builder)
ListView.builder(itemCount: n, itemBuilder: (ctx, i) => Tile(data: items[i]))

// 定高列表(性能更好)
ListView.builder(itemExtent: 72, ...)

// 下拉刷新
RefreshIndicator(onRefresh: () => notifier.refresh(), child: ListView(...))

// 滚动到底部
_scrollController.animateTo(_scrollController.position.maxScrollExtent, ...)

// 监听到底加载更多
_scrollController.addListener(() {
  if (_scrollController.position.pixels >=
      _scrollController.position.maxScrollExtent - 200) notifier.loadMore();
})

分页骨架见 12 篇。


样式与主题 ​

dart
// 圆角 + 阴影 + 边框
Container(
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(12),
    border: Border.all(color: const Color(0xFFE5E5E5)),
    boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 8)],
  ),
  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
  child: content,
)

// 文本样式
const Text('标题', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600))
Text('单行省略', maxLines: 1, overflow: TextOverflow.ellipsis)

// 主题色
Theme.of(context).colorScheme.primary
Theme.of(context).textTheme.titleMedium

设计 Token 规范见 11 篇。


平台判断与适配 ​

dart
import 'dart:io' show Platform;
if (Platform.isAndroid) ...
if (kIsWeb) ...                        // package:flutter/foundation
if (Platform.isIOS) ...

// 屏幕适配(本项目用 ScreenUtil,见 11 篇)
24.w      // 按宽度适配
12.h      // 按高度适配
14.sp     // 字体适配

⚠️ kIsWeb 判断时不要用 dart:io(Web 不支持),用 universal_io 或条件导入。


常用命令 ​

bash
fvm flutter pub get                     # 装依赖(项目统一加 fvm 前缀)
fvm flutter pub add <package>           # 添加依赖
fvm flutter run                         # 运行
fvm flutter run -t lib/main_playground.dart   # 运行练习场入口
fvm flutter analyze lib                 # 静态检查
fvm flutter test                        # 跑测试
fvm flutter build apk --release         # 打 Android 包
fvm flutter build ipa --release         # 打 iOS 包
fvm dart run build_runner build --delete-conflicting-outputs   # 代码生成
fvm flutter clean                       # 清缓存(构建异常时先试这个)
fvm flutter pub outdated                # 查看可升级依赖

⚠️ 以上命令在本项目都要加 fvm 前缀(版本锁定),见 16 篇。

📦 更全的命令(模拟器启停、adb、构建打包、救急排障等)见 09 · Flutter 命令行速查。


相关:Dart 速查 | 踩坑记录 | 返回速查区