一、参数
| 分类 | 修饰符 | 核心使用场景 | 极简示例 |
|---|---|---|---|
| 变量 / 空安全(核心) | final | 变量仅赋值一次(运行时确定值),不可修改 | final String name = '张三';(无法再赋值 name = '李四') |
const | 编译时常量(值固定),不可变,适合全局常量 | static const int maxAge = 100;(编译期确定,内存复用) | |
late | 延迟初始化非空变量(空安全),避免声明时赋值 | late User user = Provider.of(context);(使用时才初始化) | |
? | 标记变量为可空类型,允许赋值 null | String? username; username = null;(可空,使用需判空) | |
! | 强制解包可空变量(断言非空,慎用) | String? name = '张三'; print(name!.length);(确保非空时用) | |
required | 构造函数参数必须传值(替代旧版 @required) | User({required this.name});(实例化必须传 name) | |
| 类 / 继承(高频) | static | 静态成员(属于类,非实例),全局复用 | class Math { static int add(int a, int b) => a + b; }(调用:Math.add(1,2)) |
abstract | 抽象类(不可实例化),定义接口规范 | abstract class Shape { void draw(); }(子类必须实现 draw 方法) | |
extends | 单继承类(Dart 唯一继承方式) | class Student extends Person {}(继承 Person 的属性 / 方法) | |
implements | 实现接口(多实现),必须重写所有成员 | class Dog implements Animal { @override void eat() {} } | |
mixin/with | 混入复用方法(多混入),替代多继承 | mixin Flyable { void fly() {} } class Bird with Flyable {} | |
| 函数 / 方法(高频) | async/await | 异步函数,处理网络 / IO 等耗时操作 | Future<String> fetchData() async { return await http.get('url'); } |
factory | 工厂构造函数,控制实例创建(单例 / 缓存) | factory Singleton() => _instance ??= Singleton._internal(); | |
@override | 标记重写父类方法(编译器检查正确性) | @override void draw() {}(确保正确重写抽象方法) | |
| 访问控制(核心) | _(私有) | 文件级私有成员,跨文件不可访问 | class User { String _password; }(仅当前文件能访问 _password) |
| 默认(公有) | 无修饰符,跨库 / 跨类可见 | class User { String name; }(任意地方可访问 name) |
1、访问控制修饰符
| 修饰方式 | 语法示例 | 可见范围 | 说明 |
|---|---|---|---|
| 公开 (Public) | var name class MyClass | 全局可见 | 默认情况。只要不以 _ 开头,任何导入该文件的代码都可以访问。 |
| 私有 (Private) | var _name class _MyClass | 库内可见 | 标识符前加下划线 _。 仅在当前文件 (.dart) 内可见。 即使是同一个包下的其他文件也无法访问。 |
2、成员变量修饰符
| 可变性与常量修饰符 | 含义 | 典型场景 |
|---|---|---|
final | 运行时常量。 变量只能被赋值一次,值在运行时确定。 | 构造函数传入的参数、需要在 initState 中初始化的状态。 |
const | 编译时常量。 变量必须在编译时就知道确切值,且对象本身必须是不可变的。 | 配置项、数学常数、纯静态的 Widget 树。 |
late | 延迟初始化。 告诉编译器:“我保证在使用前会赋值,请先别报空安全错误”。 类型是非空的,但初始化被推迟了。 | 依赖注入、需要在 initState 异步加载后赋值的变量、循环引用。 |
| 作用域与生命周期修饰符 | 含义 | 典型场景 |
|---|---|---|
static | 静态成员。 属于类本身,不属于某个实例。所有实例共享同一份内存。 | 工具方法、单例模式、全局计数器、常量池。 |
3、dynamic参数
dynamic 并不是一个“修饰符” (像 final、static 或 @override 那样放在变量前面改变其行为的关键字),而是一个特殊的类型关键字,声明为 dynamic 的变量,编译器会跳过所有的类型检查。
什么时候使用 dynamic 参数?
- 处理 JSON/反序列化:JSON 数据结构复杂且嵌套,字段类型不固定时。
- 通用工具函数:例如一个打印日志的函数,需要接受任何类型的对象。
- 反射或元编程:需要动态调用对象方法时。
- 遗留代码兼容:迁移旧代码时暂时绕过类型检查。
| 特性 | dynamic | Object? | var (推断) |
|---|---|---|---|
| 类型检查 | 无 (关闭检查) | 有 (严格检查) | 有 (基于初始值锁定类型) |
| 方法调用 | 允许调用任何方法/属性 | 只能调用 Object 的方法 (如 toString, hashCode) | 只能调用推断出的类型的方法 |
| 赋值灵活性 | 可以随时赋值为任何类型 | 可以赋值为任何对象 (因为是 Object?) | 一旦推断完成,不能赋值为其他类型 |
| 安全性 | 低 (运行时才报错) | 高 (编译时报错) | 高 (编译时报错) |
| 典型用途 | 黑盒数据、反射、JSON | 接收任意对象但只进行通用操作 | 局部变量,类型明确 |
二、任务队列
| 特性 | 微任务队列 (Microtask) | 事件队列 (Event Queue) | 主队列 (同步代码) |
|---|---|---|---|
| 优先级 | ⭐⭐⭐ (最高) | ⭐⭐ (中) | ⭐⭐⭐ (正在执行) |
| 典型 API | scheduleMicrotask(), async/await (部分), Future 内部回调 | Future.delayed(), Timer, HttpClient, GestureDetector, vsync | 普通函数调用, print, 变量赋值 |
| 执行时机 | 当前同步代码结束后,立即执行,且在下一个事件任务之前。 | 微任务清空后,按顺序逐个执行。 | 立即执行,阻塞后续所有任务。 |
| 主要用途 | - 状态管理的微小更新 - 需要在当前逻辑结束后立即触发的内部回调 - 避免竞态条件的临时锁 | - I/O 操作 (网络/文件) - 定时器 - 用户交互事件 - 延时任务 | - 业务逻辑计算 - UI 构建 - 数据转换 |
| 潜在风险 | 阻塞事件队列:如果微任务太多或太慢,会导致 UI 事件(点击、滚动)和网络回调延迟处理。 | 堆积延迟:如果单个事件任务耗时过长,会推迟后续事件和渲染。 | 直接卡死:长时间运行会直接导致掉帧、ANR。 |
| 类比 | "插队":办完手头的事,马上处理这批紧急内部文件,处理不完不让别人进来。 | "排队":按顺序办理业务,办完一个再办下一个。 | "正在办理":柜台正在服务的客户。 |
- 默认不用管:90% 的场景下,你只需要写
async/await或使用Future,Dart 会自动帮你安排到合适的队列(通常是微任务处理回调,事件任务处理 I/O)。 - 避免手动
scheduleMicrotask:除非你是框架开发者或处理极其特殊的竞态问题,否则不要手动将任务放入微任务队列。 - 警惕微任务爆炸:不要在微任务中进行耗时计算或递归调用,这会直接饿死事件队列,导致 App 无响应。
- 耗时操作去 Isolate:如果任务真的很重(无论是微任务还是事件任务),请将其移动到单独的 Isolate 中运行,不要阻塞主队列。
三、InheritedWidget
InheritedWidget 是 Flutter 框架中状态共享和数据传递的基石。
简单来说,它的核心作用是:允许子组件(Subtree)高效地“向上”查找并监听父组件(Ancestor)中的数据,当父组件数据变化时,自动通知依赖该数据的子组件重建。
它是 Flutter 中所有状态管理方案(如 Provider, Riverpod, BuildContext.dependOnInheritedWidgetOfExactType 等)的底层实现原理。
| 特性 | InheritedWidget | Riverpod |
|---|---|---|
| 定位 | 底层原语 (Primitive) Flutter SDK 的一部分。 | 高层解决方案 (Solution) 第三方库 (pub.dev)。 |
| 依赖关系 | 无依赖,是框架基石。 | 不再依赖 InheritedWidget (v2.0+)。 它自己维护了一个独立于 Widget 树的“提供者容器 (ProviderContainer)”。 |
| 主要职责 | 仅负责:数据传递 + 依赖通知。 不负责状态的创建、销毁或复杂逻辑。 | 负责:状态创建、依赖注入、生命周期管理、缓存、自动 dispose、测试支持。 |
Riverpod 解决了 InheritedWidget 所有的痛点(Context 依赖、测试难、组合难、生命周期管理繁琐),是现代 Flutter 开发的首选状态管理方案之一。
| 维度 | Provider | Riverpod | 优势分析 |
|---|---|---|---|
| 依赖上下文 | 强依赖 BuildContext 必须在 Widget 树内使用。 | 零依赖 BuildContext 通过 ref 访问,可在任何地方使用。 | Riverpod 胜:解决了在路由、服务层、定时器中无法访问状态的痛点。 |
| 安全性 | 运行时安全 缺少 Provider 会编译通过,运行时报错崩溃。 | 编译时安全 如果 Provider 未定义或类型错误,编译直接报错。 | Riverpod 胜:大幅减少线上崩溃,重构更安全。 |
| 代码样板 | 较多 需定义类、继承 ChangeNotifier、手动 notifyListeners。 | 较少 全局变量定义,自动通知,无需手动管理监听器。 | Riverpod 胜:代码更简洁,逻辑更清晰。 |
| 状态组合 | 困难 一个 Provider 依赖另一个需要嵌套或复杂逻辑。 | 原生支持 ref.watch(otherProvider) 轻松组合,自动处理依赖图。 | Riverpod 胜:适合复杂业务逻辑。 |
| 生命周期 | 手动/被动 需配合 Disposable 等模式手动清理。 | 自动管理 支持 autoDispose,无监听时自动销毁,防止内存泄漏。 | Riverpod 胜:资源管理更智能。 |
| 测试难度 | 较难 需构建 Widget 树并包裹 Provider。 | 极易 可创建独立的 ProviderContainer 进行纯单元测试,无需 UI。 | Riverpod 胜:测试驱动开发 (TDD) 友好。 |
| 学习曲线 | 低 概念简单,官方推荐,文档丰富。 | 中 概念较多 (Provider, Ref, Notifier, Family 等),但一旦掌握效率极高。 | Provider 胜 (入门阶段)。 |
四、Isolate、compute、async/await
| 特性 | 原生 Isolate (手动创建) | compute 函数 (Flutter 封装) | Dart 异步 (async/await) |
|---|---|---|---|
| 本质 | Dart 轻量级隔离线程,独立内存 / 事件循环 | 对 Isolate 的单层封装(简化版) | 单 Isolate 内的事件循环调度 |
| 内存模型 | 完全隔离,不共享内存 | 完全隔离(底层仍是 Isolate) | 共享 Main Isolate 内存 |
| 通信方式 | SendPort/ReceivePort 消息传递 | 自动封装消息传递(仅单次返回) | 无跨 Isolate 通信 |
| 生命周期 | 手动创建 / 销毁 / 管理 | 自动创建→执行任务→销毁 | 无独立生命周期 |
| 任务类型 | 支持单次 / 持续 / 双向通信任务 | 仅支持单次、无状态任务 | 非耗时 IO 任务(如网络请求) |
| 返回值 | 支持多次返回(如进度) | 仅单次返回(任务结束后) | 单次返回 |
| 资源消耗 | 可复用(手动控制),轻量 | 每次调用创建新 Isolate,用完销毁 | 无额外资源消耗 |
| 使用复杂度 | 高(需手动管理通信 / 生命周期) | 低(一行代码调用) | 极低 |
总结:
-
Isolate是 Dart/Flutter 的并发机制,核心特点是内存隔离、消息通信,避免了传统线程的锁问题; -
轻量级使用推荐
compute函数,复杂场景手动创建Isolate并管理SendPort/ReceivePort; -
选择原则:简单单次耗时任务用
compute,需进度 / 持续通信用原生Isolate,非 CPU 密集型异步任务用async/await;async/await仅适用于 “等待型” 任务(IO、延迟),CPU 密集型任务(如循环计算)仍会阻塞 UI;本质是 Main Isolate 内的事件调度,不是真正的并发。 -
核心差异:
Isolate/compute是跨隔离区并发(内存隔离),async/await是单隔离区异步(内存共享); -
性能优化:高频耗时任务优先复用原生
Isolate,避免频繁创建compute。