You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中无状态Widget为何在刷新时发生重建?

Stateless Widget在setState后颜色变化的原因与解决办法

问题原因

核心问题在于:Stateless Widget的build方法会在父组件调用setState()重建时被重新执行。你的MyWidget在build方法里直接生成随机颜色,每次build都会重新生成新的颜色值,所以点击刷新按钮触发父组件重建后,MyWidget的颜色也会跟着改变。

解决方案

要让MyWidget的颜色固定,关键是把颜色值的生成从build方法中移出来,确保只初始化一次。下面提供两种可靠的实现方式:

方案1:父组件管理颜色并传递给子组件

在父组件的initState中生成一次MyWidget的颜色,作为参数传递给子组件,后续即使父组件重建,颜色值也不会改变。

修改后的完整代码:

class HomeScreen extends StatefulWidget {
  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  // 初始化MyWidget的固定颜色
  late final Color _myWidgetColor;

  @override
  void initState() {
    super.initState();
    _myWidgetColor = Color.fromRGBO(
      Random().nextInt(255),
      Random().nextInt(255),
      Random().nextInt(255),
      1,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Color.fromRGBO(
          Random().nextInt(255),
          Random().nextInt(255),
          Random().nextInt(255),
          1,
        ),
        title: const Text('Demo'),
        actions: [
          IconButton(
            onPressed: () {
              setState(() {});
            },
            icon: const Icon(Icons.refresh),
          )
        ],
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Container(
            height: 100,
            color: Color.fromRGBO(
              Random().nextInt(255),
              Random().nextInt(255),
              Random().nextInt(255),
              1,
            ),
          ),
          MyWidget(color: _myWidgetColor),
        ],
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  final Color color;

  const MyWidget({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: color,
      child: const Center(child: Text('现在这个颜色不会随刷新变化了')),
    );
  }
}

方案2:子组件自身初始化固定颜色

让MyWidget在构造时生成一次颜色,只要子组件实例不被重新创建,颜色就会保持固定。

修改后的MyWidget代码:

class MyWidget extends StatelessWidget {
  final Color color;

  // 构造时生成一次固定颜色
  MyWidget({super.key}) : color = Color.fromRGBO(
    Random().nextInt(255),
    Random().nextInt(255),
    Random().nextInt(255),
    1,
  );

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: color,
      child: const Center(child: Text('现在这个颜色不会随刷新变化了')),
    );
  }
}

注意:如果父组件每次build都重新创建MyWidget实例(比如直接写MyWidget()),这种方案可能失效。更推荐方案1,由父组件统一管理状态。

核心总结

不要在build方法中生成需要固定的动态值(比如随机颜色),因为build会频繁执行。正确的做法是在组件初始化阶段(initState或构造函数)生成这些值,或者通过参数传递固定值。

内容的提问来源于stack exchange,提问作者Irfan Ganatra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 09:45:50