const
keyword首先需要知道 const
和 final
是对立关系, 都是用来声明常量的
在 Flutter(Dart 语言) 中,const
是一个编译时常量关键字,其作用不仅是声明不可变变量,还能在内存和性能优化中发挥关键作用。
const
修饰的变量或对象必须在编译时就能计算出结果,无法依赖运行时的数据。const PI = 3.14159; // ✅ 合法
const currentTime = DateTime.now(); // ❌ 非法(运行时才能确定)
const list = [1, 2, 3];
list.add(4); // ❌ 运行时抛出异常
const
对象共享同一内存地址:var a = const [1, 2];
var b = const [1, 2];
print(identical(a, b)); // ✅ 输出 true(内存地址相同)
Flutter 会跳过重建 const
Widget(因其不可变):
// 推荐写法:对静态无状态的子 Widget 使用 const
class MyPage extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
const Text("Hello"), // ✅ 重建时会被复用
const SizedBox(height: 10),
],
);
}
}
const kDefaultPadding = 16.0;
const kPrimaryColor = Color(0xFF4285F4);
const
创建不可变集合:const validStatusCodes = {200, 304}; // 不可变的 Set
const translations = {'en': 'Hello', 'es': 'Hola'}; // 不可变的 Map
final
的关键区别特性 | const |
final |
---|---|---|
赋值时机 | 编译时 | 运行时(但只赋值一次) |
内存占用 | 共享相同值 | 每次创建新实例 |
集合内部可变性 | 完全不可变(递归) | 变量引用不可变,但对象内部可修改 |
示例对比:
// const:完全冻结
const constList = [1, 2];
// constList.add(3); // ❌ 报错
// final:仅禁止重新赋值
final finalList = [1, 2];
finalList.add(3); // ✅ 允许修改内容
// finalList = [4, 5]; // ❌ 禁止重新赋值
Widget 优化
// ✅ 好的写法:尽可能对静态子 Widget 使用 const
Scaffold(
body: const Center(
child: const Text('优化性能'),
),
);
// ❌ 避免:无谓的重复构建
Scaffold(
body: Center(
child: Text('每次重建'), // 非 const 导致重复创建
),
);
集合常量
// 使用 const 构造器创建不可变集合
var list = const []; // 等同于 List.unmodifiable([])
构造函数标记
// 如果类可能被 const 构造,显式声明 const 构造函数
class Point {
final double x, y;
const Point(this.x, this.y); // ▶️ 可被 const 调用
}
以下代码在 Flutter 性能测试 中差异明显:
// 测试1:非 const Widget(构建耗时较长)
ListView.builder(
itemBuilder: (_, index) => Text('Item $index'),
);
// 测试2:const Widget(构建速度提升 20%-40%)
ListView.builder(
itemBuilder: (_, index) => const Text('Item'),
);
const
只在编译期有用const
导致代码可读性下降通过合理使用 const
,可以显著提升 Flutter 应用的性能和内存效率,尤其是在复杂 Widget 树场景下。
static
是否需要?在 Dart/Flutter 中为类定义静态常量时,推荐使用 static const
,而非仅用 static
。以下是详细对比和最佳实践:
特性 | static const int |
static int (非 const ) |
---|---|---|
不可变性 | ✅ 编译时常量,完全不可变 | ❌ 变量可被重新赋值(即使不推荐) |
内存优化 | ✅ 全局共享同一内存地址 | ❌ 每次访问都是独立值 |
使用场景 | 定义真正的常量(如配置、枚举值) | 需要运行时修改的静态变量 |
线程安全 | ✅ 天然线程安全 | ❌ 需手动控制同步 |
static const
?static const
清晰表达“这是不可变的常量”:class Config {
static const int maxRetryCount = 3; // ✅ 明确表示不可修改
static int timeout = 5000; // ❓ 可能被意外修改(易引发 bug)
}
const
常量在编译时被内联,运行时无额外内存开销:// 编译后直接替换为字面量
print(Config.maxRetryCount); // 等效于 print(3);
const
的 static
变量可能被错误地修改:Config.timeout = -1; // 编译通过,但逻辑错误!
Config.maxRetryCount = 5; // ❌ 编译时报错(安全)
static const
的场景class HttpStatus {
static const int success = 200;
static const int notFound = 404;
}
static int
(无 const
)的场景class AppCache {
static int maxSize = 100; // 允许运行时修改
static void updateCacheSize(int size) => maxSize = size;
}
const
)class AppRoutes {
static const String home = '/home';
static const String profile = '/profile';
}
// 使用时直接内联,无运行时开销
Navigator.pushNamed(context, AppRoutes.home);
const
)class Counter {
static int globalCount = 0; // 需要修改,不能用 const
static void increment() => globalCount++;
}
static final
和 static const
如何选?const
:必须编译时确定值(如数字、字符串字面量)final
:允许运行时赋值一次(如从函数计算结果)class MathConstants {
static const double pi = 3.14159; // ✅ 编译时已知
static final double random = Random().nextDouble(); // 运行时确定
}
static
为 const
?// 测试代码:重复访问 10 万次
class Test {
static const int constValue = 42;
static int mutableValue = 42;
}
void benchmark() {
final stopwatch = Stopwatch()..start();
for (var i = 0; i < 100000; i++) {
Test.constValue; // 0.2ms (编译时内联)
}
print('const: ${stopwatch.elapsedMicroseconds}μs');
stopwatch.reset();
for (var i = 0; i < 100000; i++) {
Test.mutableValue; // 1.8ms (需内存访问)
}
print('non-const: ${stopwatch.elapsedMicroseconds}μs');
}
结果:const
访问速度快 5-10 倍(实际差异随场景变化)。
关键字 | 推荐场景 |
---|---|
static const |
绝对常量(配置、枚举、数学常数等) |
static final |
运行时确定但不可变的量(如初始化后的全局配置) |
static |
需要运行时修改的静态变量(慎用!确保线程安全或加锁) |
黄金法则:
能用
const
就优先用const
,除非必须允许修改。
class Square {
static int sides = 4;
}
class Circle {
// 这里其实dart规范写成 pi 就行
/*
可以加上 double
但是不加也是可以正常运行的
上面的 sides 不带 const 所以需要带上 int
*/
static const PI = 3.14;
}
在 Dart 中,static const
和 static
变量在类型推断上的行为差异,源于 Dart 的类型系统设计 和 const
常量的特殊性。以下是具体原因和背后机制:
const
常量的类型推断规则const
声明的常量 拥有 更强大的类型推断能力const
时会 根据右侧的初始值自动推断类型,因此允许省略类型声明。static const PI = 3.14; // 自动推断为 `double` 类型
static const name = "Flutter"; // 自动推断为 `String` 类型
const
的值必须在编译时确定,编译器可以安全地推断出类型。const
静态变量的限制static
变量 默认不会自动推断类型(即类型是可选的,但如果不初始化必须指定类型)static int sides = 4; // ✅ 明确指定类型(推荐)
static sides = 4; // ✅ 也行(编译器推断为 `int`)
static int sides; // ❌ 不初始化时必须指定类型(否则 `dynamic`)
⚠️ 关键点:
int
,它依然是 100% 合法的 Dart 代码(Dart 2.x 开始支持局部变量类型推断)。lints
规则)或 IDE 可能会 建议显式声明类型(以避免隐式 dynamic
或提高代码可读性)。对于 const
常量
static const defaultTimeout = 1000; // 推断为 `int`
对于 static
可变变量
static int maxConnections = 10; // 而不仅是 `static maxConnections = 10;`
final
变量的场景
static final
变量也会自动推断类型:static final currentTime = DateTime.now(); // 自动推断为 `DateTime`
const
可以省略类型?编译时常量的特性
const
表达式都会被计算并内联到代码中,Dart 能 100% 确定其类型。避免 dynamic
风险
const
值无法修改,类型推断是绝对安全的。const
静态变量建议显式类型?降低 dynamic
的意外使用
dynamic
,可能引发运行时错误:static var uninitialized; // 类型是 `dynamic`(危险!)
代码可维护性
✅ 正确且推荐
class Math {
static const PI = 3.14; // 推断为 `double`
}
➡️ 编译后完全等同于:
static const double PI = 3.14;
场景 | 类型声明建议 | 示例 |
---|---|---|
static const |
可省略(自动推断) | static const PI = 3.14; |
static 变量 |
推荐显式声明(避免隐含 dynamic ) |
static int sides = 4; |
final 变量 |
可省略 | static final now = DateTime.now(); |
黄金法则:
const
/final
) → 类型可省,代码更简洁。