const 修饰的 StatelessWidget 和未修饰的区别

98 阅读4分钟

🚀 const 修饰的 StatelessWidget 和未修饰的区别

在 Flutter 中,const StatelessWidget普通的 StatelessWidget实例复用性能优化 方面有很大的区别。我们从 创建机制、Element 复用、性能优化 三个方面来详细总结它们的不同。


1️⃣ const 修饰的 StatelessWidget 和 未修饰的区别

const 主要作用是让 Widget 变成编译时常量,可以复用已创建的实例

区别普通 StatelessWidgetconst 修饰的 StatelessWidget
实例创建每次 build() 触发,都会新建 Widget 实例相同的 const Widget 只会创建一次,后续复用这个实例
Element 复用只要 runtimeTypeKey 相同,Element 可以复用和普通 Widget 的 Element 复用规则相同!
性能优化每次 build() 触发都会新建对象,占用额外内存复用已创建的 const Widget,节省内存,减少不必要的 Diff 操作

📌 结论:

  • const 主要优化的是 Widget 实例的创建,和 Element 是否复用没有直接关系
  • 即使 StatelessWidget 不是 const,但 runtimeTypeKey 不变,它的 Element 仍然可能复用
  • 使用 const 可以避免创建不必要的 Widget 实例,从而减少内存占用,提高性能

2️⃣ 代码示例

❌ 普通 StatelessWidget(不使用 const,每次 build() 都会创建新的 Widget 实例)

class MyWidget extends StatelessWidget {
  MyWidget() {
    print("🔥 MyWidget 实例被创建");
  }

  @override
  Widget build(BuildContext context) {
    print("🔄 MyWidget.build() 被调用了");
    return Text("Hello");
  }
}

class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  bool toggle = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        MyWidget(),    // 每次 `setState()` 都会创建新实例
        ElevatedButton(
          onPressed: () {
            setState(() {}); // 没有改变 Widget 结构
          },
          child: Text("刷新"),
        ),
      ],
    );
  }
}
📢 点击 "刷新" 按钮后,日志输出
🔥 MyWidget 实例被创建  <-- 重新创建 StatelessWidget
🔄 MyWidget.build() 被调用了  <-- 重新执行 build()

每次 setState() 都会重新创建 MyWidget 实例,增加不必要的内存开销


const 修饰的 StatelessWidget(会复用实例,不会重复创建)

class MyWidget extends StatelessWidget {
  const MyWidget(); // 使用 const 构造函数

  @override
  Widget build(BuildContext context) {
    print("🔄 MyWidget.build() 被调用了");
    return Text("Hello");
  }
}

class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  bool toggle = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const MyWidget(),  // 使用 const
        ElevatedButton(
          onPressed: () {
            setState(() {}); // 没有改变 Widget 结构
          },
          child: Text("刷新"),
        ),
      ],
    );
  }
}
📢 点击 "刷新" 按钮后,日志输出
🔄 MyWidget.build() 被调用了  <-- 直接复用之前创建的实例,无需新建

即使 setState() 触发了重建,const 修饰的 MyWidget 依然不会被重复创建实例


3️⃣ const 关键点总结

是否使用 constWidget 实例是否复用?Element 是否复用?优化效果
不使用 const每次 setState() 都会新建可能复用(如果 runtimeTypeKey 没变)占用额外内存,降低性能
使用 constWidget 只创建一次,后续直接复用可能复用(如果 runtimeTypeKey 没变)减少不必要的 Widget 创建,节省内存,提高性能

4️⃣ const 会影响 Element 复用吗?

🚨 不会!const 只优化 Widget 实例创建,并不会影响 Element 复用规则! Flutter 判断 Element 是否复用的标准:

  1. 如果 Widget 的 runtimeTypeKey 没变,Element 仍然可以复用
  2. 即使 StatelessWidget 不是 const,但 runtimeType 相同,它的 Element 仍然可能复用
  3. 如果 Widget 结构变化(runtimeType 变了或者 Key 变了),Flutter 才会销毁 Element,创建新的

📌 总结:

  • const 只会帮助 Flutter 复用 Widget 实例,而 Element 是否复用由 runtimeTypeKey 决定
  • const + Element 复用 = 更好的性能优化!

🔥 终极总结

特性普通 StatelessWidgetconst 修饰的 StatelessWidget
Widget 实例是否会复用?❌ 每次 build() 都会新建✅ 只创建一次,后续复用
Element 是否会复用?✅ 可能复用(runtimeType & Key 不变)✅ 可能复用(runtimeType & Key 不变)
性能优化❌ 每次 build() 都会新建对象,浪费性能✅ 避免重复创建 Widget,节省内存,提高性能

🔥 最佳实践

适用 const 的情况

无状态(StatelessWidget)并且创建后不会更改的 Widget
常见的基本 UI 组件,如 TextIconContainerRowColumn
列表项、界面静态部分等不会变动的组件

示例

const Text("Hello World");      // 推荐使用 `const`
const Icon(Icons.home);         // 推荐使用 `const`
const SizedBox(height: 10);     // 推荐使用 `const`

不适用 const 的情况

需要动态更新的 Widget(setState() 后会变化)
包含 StatefulWidget,因为状态会改变

Widget build(BuildContext context) {
  return Text(DateTime.now().toString()); // ❌ 不能用 const,因为时间会变化
}

💡 总结:尽可能使用 const 来优化 StatelessWidget 的性能,但它不会影响 Element 复用! 🚀