Flutter 基础知识参考字典

要点

Flutter 入门阶段的名词按用途分成七组:Widget 基础、布局、滚动、状态、导航、调试、报错。每个条目给签名、一句话定位、最小示例和坑。字典是查的不是读的——遇到具体问题从分组标题跳进去,文末速查表做全量索引。

Widget 基础

组内共性:Widget 分无状态(StatelessWidget,纯配置)与有状态(StatefulWidget,数据跨帧保留)两种。生命周期说的是 State 对象的生命周期,不是 Widget 的——Widget 随时可能被重建,State 在树结构不变期间被保留复用。

StatelessWidget

class TitleText extends StatelessWidget {
  const TitleText(this.text, {super.key});
  final String text;

  @override
  Widget build(BuildContext context) => Text(text,
      style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold));
}

build 输出完全由构造参数决定时用它;框架可以随时丢弃重建它,不替你保存任何东西。

⚠️ 坑:在 StatelessWidget 里持有可变字段(非 final)并指望界面跟着变——没有任何机制通知它重建,改了也是白改。

StatefulWidget 与 State

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  var count = 0;

  @override
  Widget build(BuildContext context) => TextButton(
      onPressed: () => setState(() => count++),   // 改数据必须包进 setState
      child: Text('点了 $count 次'));
}

数据要跨帧保留(计数、输入内容、动画进度)时用它;状态放 State 字段里,setState 标记后下一帧重跑 build。

⚠️ 坑:直接写 count++ 不包 setState,数据变了界面不动;这是新手第一大”为什么没反应”。

State 生命周期

顺序:initState(一次性初始化、订阅、controller 创建)→ didChangeDependencies(依赖的 InheritedWidget 变化时再触发)→ build(一帧内可能多次)→ didUpdateWidget(父重建传入新配置)→ dispose(取消订阅、释放 controller)。

⚠️ 坑:把网络请求、订阅写进 build——build 会被反复调用,副作用会成倍执行;初始化只放 initState,清理只放 dispose。忘了 dispose controller 是内存泄漏的标准来源(Dart 的 GC 不会替你调 dispose)。

布局

组内共性:布局分单子(Container、Stack)与多子(Row、Column)。底层心智是约束传递(constraints go down, sizes go up):父给子约束,子在约束内选尺寸,父决定位置。多数溢出报错是约束没谈拢,不是像素算错。

Container

Container(
  padding: const EdgeInsets.all(12),
  decoration: BoxDecoration(
    color: Colors.blue.shade50,
    borderRadius: BorderRadius.circular(8),
  ),
  child: const Text('卡片'),
)

装饰盒子的快捷组合:padding、margin、边框、背景、圆角一体;什么都不传时它基本什么都不做。

⚠️ 坑:width/height 与 constraints、alignment 同时给会互相打架,最终尺寸以 constraints 优先;只用单一装饰时直接用 Padding/ColoredBox 更省一层。

Row / Column

Column(
  mainAxisAlignment: MainAxisAlignment.center,   // 主轴对齐
  crossAxisAlignment: CrossAxisAlignment.start,   // 交叉轴对齐
  children: const [Text('第一行'), Text('第二行')],
)

主轴水平(Row)/垂直(Column)排列子项;两个方向参数名字相同,行为随主轴切换。

⚠️ 坑:Row 里再嵌 Row、或 ListView 里直接放 Column 高度未定,会得到 unbounded(无界约束)类报错;内容超出可用宽度时就是著名的黄黑条纹溢出。

Stack

Stack(
  children: [
    Image.network(url),                          // 非定位子项决定 Stack 尺寸
    const Positioned(right: 8, bottom: 8, child: Icon(Icons.edit)),
  ],
)

子项叠放;非定位子项按 alignment 摆,Positioned 子项按四边偏移定位。

⚠️ 坑:Positioned 子项不参与 Stack 的尺寸计算——只放 Positioned 子项的 Stack 尺寸为零;角标场景记得底下有个”撑尺寸”的子项。

Expanded / Flexible

Row(children: [
  const Expanded(child: Text('标题很长的')),      // tight:强制占满剩余空间
  Flexible(child: IconButton(onPressed: null, icon: const Icon(Icons.share))),
])

在 Row/Column 主轴上分配剩余空间。Expanded 等于 Flexible(fit: FlexFit.tight)——强制填满;默认 loose 允许子项按自己意愿更小。

⚠️ 坑:多个 Expanded 默认 flex 均 1 平分空间,比例用 flex 参数调;Expanded 内子项拿到的是无限主轴约束,里面再放需要固有尺寸的东西(如没约束的 ListView)会报无界错误。

滚动

组内共性:滚动内容交给 ListView 家族或更低层的 sliver。两个高频疑问——”Column 超屏为什么不能滚”(Column 不是滚动组件)与”列表为什么卡”(构建策略不对)都能在本组找到答案。

ListView(children: …)

ListView(
  children: items.map((e) => ListTile(title: Text(e))).toList(),
)

饿汉式:children 全量立刻构建。适合十条八条的固定内容。

⚠️ 坑:上百条或每条构建成本高(图片、复杂布局)时,首帧把所有子项都建出来,滚动掉帧、内存上升——这是构建策略问题,换手机解决不了。

ListView.builder

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, i) => ListTile(
    title: Text(items[i]),
    // 列表项含输入框等有状态内容时给 key:滚动复用时不串状态
    key: ValueKey(items[i].id),
  ),
)

惰性构建:itemBuilder 只在可见范围与缓存区被调用,itemCount 必须给准。性能语义上与 children 全量构建是两个世界,长列表的默认答案。

⚠️ 坑:itemCount 报多了越界崩、报少了尾部静默丢失;在 itemBuilder 里做订阅或创建 controller——itemBuilder 会随滚动反复调用,副作用会翻倍。

状态

组内共性:按”谁读得到”分层——组件内部用 setState,子树共享用 InheritedWidget,跨页面/全局再上 Provider 或 Riverpod。选型原则是先用小的,痛点出现了再上大的。

setState

一句话定位:组件内部状态的最小方案,标记当前 State 为脏、下一帧重跑 build。上面的计数器就是完整用法,没有更多秘密。

⚠️ 坑:dispose 之后调用(异步回调回来页面已销毁)与 build 过程中调用都会直接报错,见文末报错速查;mounted 检查是标准防御。

InheritedWidget

// 主题、Locale、屏幕信息都是它下发的;自己写的话样板极多
Theme.of(context)     // 内部就是沿树查找 InheritedWidget 并建立依赖
MediaQuery.of(context)

一句话定位:沿树高效下发的只读共享数据,子树 O(1) 查找,依赖它的子树在其变化时自动重建。

⚠️ 坑:它只负责”分发与通知”,不负责”更新触发”;直接拿它当全局状态用,样板代码量会劝退你——这正是 Provider 存在的原因。

Provider / Riverpod

一句话定位:Provider 在 InheritedWidget 上封装了依赖注入,是”局部状态升级到共享状态”的中间站;Riverpod 把状态做成可组合、可测试、不依赖 Widget 树的 Provider 网络,是当前社区主流的大状态方案。安装均不钉版本:flutter pub add provider 或 flutter pub add flutter_riverpod。

⚠️ 坑:把一切塞进全局——能 setState 解决的局部状态不要提升,全局状态每变一次,监听它的子树都跟着重建;重建成本的分析思路与《React 渲染与重渲染》一致,谁订阅谁重建。

导航

组内共性:命令式(压栈弹栈)与声明式(页面是路由状态的函数)两条路线。官方当前建议:多数应用尤其带深链或 Web 目标时用 go_router,纯移动端简单跳转用命令式 Navigator 也完全够。

Navigator.push / pop(命令式)

final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => const DetailPage()),
);
// 返回时带值:Navigator.pop(context, 'ok')

一句话定位:把页面压栈、弹栈,await 还能拿返回值;最少代码完成跳转。

⚠️ 坑:Web 上没有”栈”,浏览器前进后退、刷新、分享链接都与 push 的栈对不齐——这是该换 go_router 的信号,不是要坚持修 Navigator 的理由。

Navigator 2 / Router(声明式)

一句话定位:页面列表 = f(路由状态),为深链与 Web 而生;直接裸写 RouterDelegate 与 RouteInformationParser 的样板量出名地大。

⚠️ 坑:它是 go_router 的地基而不是给应用开发者的日常 API;除非做高度定制的路由器,否则不裸用。

go_router

final router = GoRouter(
  routes: [
    GoRoute(path: '/', builder: (c, s) => const HomePage()),
    GoRoute(
      path: '/todo/:id',                          // 路径参数都是 String
      builder: (c, s) => DetailPage(id: s.pathParameters['id']!),
    ),
  ],
);
// MaterialApp.router(routerConfig: router)
// 跳转:context.go('/todo/3') 替换栈;context.push('/todo/3') 压栈

一句话定位:Flutter 团队维护的路由包,路由表、声明式重定向、嵌套路由、深链一站式解决,是 Navigator 2 的易用封装。安装:flutter pub add go_router。

⚠️ 坑:pathParameters/queryParameters 拿到的都是 String,数字与布尔要自己解析校验;context.go 是”栈替换”、context.push 是”压栈”,混用时返回行为不一致。

快捷键与调试

组内共性:调试主入口是 DevTools 面板,快捷键围绕热重载;静态防线是 flutter analyze。

热重载 / 热重启

操作 flutter run 终端 效果边界
热重载 r(IDE 里保存文件即触发) 保留 State 与导航;build 内的改动生效
热重启 R 丢 State 回首页;State 初始值、main 改动要它才生效
退出 q 改原生代码、依赖后需重跑 flutter run

⚠️ 坑:把”热重载没反应”当 bug——先判断改动属于哪一层(界面 / 初始化 / 原生),边界见上表。

DevTools(Widget Inspector / Performance / Memory)

一句话定位:flutter run 时自动起服务,浏览器打开面板;Inspector 看 Widget 树与每个节点的实际约束,Performance 看帧曲线,Memory 抓内存泄漏。

⚠️ 坑:测性能要用 flutter run --profile——debug 模式自带的断言与检查开销会把帧率拉低到失真;release 模式则连不上 DevTools。

flutter analyze / dart fix

flutter analyze         # 静态检查,提交前与 CI 里都该跑
dart fix --dry-run       # 预览可自动修复的 lint 与弃用 API 用法

一句话定位:analyze 是 IDE 波浪线的命令行版本,适合当 CI 闸门;dart fix 自动迁移过时写法。

⚠️ 坑:只靠 IDE 提示不看 analyze——CI 与队友的 IDE 插件配置不同,闸门要立在仓库层面。

debugPrint 与 print

一句话定位:debugPrint 会截断超长输出避免被平台日志系统整行丢弃,是调试打印的正确默认。

⚠️ 坑:print 长字符串在部分平台直接丢内容;两类都别进生产代码,正式日志用日志库。

常见报错速查

组内共性:报错按来源分三类——生命周期违规、布局约束冲突、树结构违规。Flutter 的报错信息自带修复建议与文档链接,先读第一段再动手,不要见红就搜。

报错(节选关键词) 触发场景 修法
setState() called after dispose() 异步回调回来时页面已销毁 回调里先查 if (!mounted) return; 再 setState
setState() called during build build 过程里同步调了 setState 挪到事件回调;确实要”build 后立刻改”用 addPostFrameCallback
A RenderFlex overflowed by N pixels Row/Column 子项超出可用空间,屏幕现黄黑条纹 让某个子项 Expanded/Flexible,或包进可滚动容器
Vertical viewport was given unbounded height ListView 放在 Column 或无界高度里 外面包 Expanded 给定高度;确实要”占内容高度”用 shrinkWrap(注意性能)
Incorrect use of ParentDataWidget Expanded 用在非 Flex 父级、Positioned 用在非 Stack 父级 检查直接父级类型,中间别隔别的 Widget
Looking up a deactivated widget’s ancestor is unsafe 异步间隙后使用过期的 context mounted 检查后重新取 context 再用
Duplicate GlobalKey 同一个 GlobalKey 挂到树上两处 GlobalKey 全局唯一;多数场景 ValueKey 够用
列表滚动卡顿(非报错) ListView children 全量构建、缺 const 构造 换 ListView.builder;构造加 const 减少重建对象

速查表

条目 一句话用途 有什么坑
StatelessWidget 纯配置组件,参数决定输出 持有可变字段无人通知重建
StatefulWidget 状态跨帧保留 改数据不包 setState 界面不动
State 生命周期 initState → build → dispose 副作用放 build 会翻倍执行
Container 装饰盒子组合 width 与 constraints 打架
Row / Column 主轴排列子项 无界约束与溢出高发
Stack 子项叠放定位 Positioned 不参与尺寸
Expanded / Flexible 分配剩余空间,tight/loose 之分 内部约束变无限
ListView(children:) 少量内容全量构建 长列表掉帧
ListView.builder 惰性构建长列表 itemCount、itemBuilder 副作用
setState 局部状态最小方案 dispose 后与 build 中调用报错
InheritedWidget 子树只读共享数据 只分发不触发更新
Provider / Riverpod 共享状态与依赖注入 局部状态不必全局化
Navigator.push/pop 命令式压栈弹栈 Web 深链对不齐
Navigator 2 / Router 声明式路由地基 样板多,不裸用
go_router 官方路由包,深链嵌套一站式 参数全是 String 要自查
热重载 r / 热重启 R 秒级改代码反馈 初始值改动需 R
DevTools 树 / 帧率 / 内存检查 性能用 profile 模式看
flutter analyze 静态检查闸门 别只依赖 IDE 提示
debugPrint 安全的调试打印 print 长输出会被截断丢弃

下一步

这一篇是 Flutter 入门系列的收尾参考:Widget 与生命周期的实感来自《第一个 Flutter 应用》,语言层的机制(null safety、isolate、模式匹配)在《Dart 语法要点》里展开,环境问题回《Flutter 环境搭建与工具链》查。字典的边界也要诚实:它覆盖入门到日常开发的查表需求,平台通道、自定义 sliver、动画系统这些深水区不在其列,用到时以官方文档为准。

版本核实(撰写时检索,2024-05-06):Flutter 当前稳定版 3.19.x(Dart 3.3),3.22 预计 5 月发布。go_router 为 Flutter 团队(flutter packages 仓库)维护,官方导航文档对多数应用推荐 go_router 或命令式 Navigator、不推荐命名路由;Provider 与 Riverpod 为社区主流方案;material/cupertino 仍内置在 SDK 中,本篇 package:flutter/material.dart 写法直接可用。包安装一律 flutter pub add <name> 不钉版本,API 细节以 docs.flutter.dev 与各包 changelog 为准。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇