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

如何在Flutter的build方法初期根据条件设置AppBar颜色?

Flutter Build阶段调用Provider触发异常的解决方法

问题说明

在测试代码中,尝试在build方法初期根据条件设置AppBar颜色,由于状态需要全项目共享,必须使用Provider。但运行时出现以下异常:

Exception has occurred.
FlutterError (setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<AppBarColorProvider?> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
  _InheritedProviderScope<AppBarColorProvider?>
The widget which was currently being built when the offending call was made was:
  MyHomePage)

同时存在限制:无法将颜色设置代码放入initState(后续会涉及带延迟的网络请求/Future变量),单纯延迟执行也不可行。


相关代码

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:scaffold_color_after_a_delay_using_provider/scaffold_color_provider.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<AppBarColorProvider>(
          create: (context) => AppBarColorProvider(),
        ),
      ],
      child: const MyApp(),
    ),
  );
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: MyHomePage());
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late TextEditingController controllerText01;
  late AppBarColorProvider appBarColorProviderObj;

  @override
  void initState() {
    super.initState();
    controllerText01 = TextEditingController();
    appBarColorProviderObj = Provider.of<AppBarColorProvider>(
      context,
      listen: false,
    );
  }

  @override
  void dispose() {
    controllerText01.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    DateTime now = DateTime.now();
    appBarColorProviderObj.setAppBarColorToRed(now.toString());

    return Scaffold(
      appBar: AppBar(
        title: const Text("example"),
        backgroundColor:
            appBarColorProviderObj.getIsAppBarRed ? Colors.red : null,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: [Text(now.toString())],
      ),
    );
  }
}

lib/scaffold_color_provider.dart

import 'package:flutter/material.dart';

class AppBarColorProvider with ChangeNotifier {
  bool _isAppBarRed = false;
  bool get getIsAppBarRed => _isAppBarRed;
  void setAppBarColorToRed(String myStr) {
    String seconds_milli = myStr.split(":")[2];
    String seconds = seconds_milli.split(".")[0];
    int intSeconds = int.tryParse(seconds) ?? 0;

    if (intSeconds > 0 && intSeconds < 29) {
      _isAppBarRed = true;
    } else {
      _isAppBarRed = false;
    }
    notifyListeners();
  }
}

解决方法

核心原因

异常是因为在build方法执行过程中调用了notifyListeners(),触发了组件重建,而Flutter不允许在build周期内修改状态并通知重建。


方案1:使用PostFrameCallback延迟执行状态修改

将状态修改逻辑放到当前build周期完成后执行,避免在build过程中触发重建:

修改_MyHomePageState的build方法:

@override
Widget build(BuildContext context) {
  DateTime now = DateTime.now();
  // 放到当前帧构建完成后执行
  WidgetsBinding.instance.addPostFrameCallback((_) {
    appBarColorProviderObj.setAppBarColorToRed(now.toString());
  });

  return Scaffold(
    appBar: AppBar(
      title: const Text("example"),
      backgroundColor:
          appBarColorProviderObj.getIsAppBarRed ? Colors.red : null,
    ),
    body: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      mainAxisSize: MainAxisSize.min,
      children: [Text(now.toString())],
    ),
  );
}

如果后续需要处理网络请求(Future),可以直接在回调中发起请求:

WidgetsBinding.instance.addPostFrameCallback((_) {
  fetchNetworkData().then((data) {
    appBarColorProviderObj.setAppBarColorBasedOnData(data);
  });
});

方案2:在didChangeDependencies中执行初始化逻辑

如果逻辑仅需执行一次(或依赖变化时执行),可以放到didChangeDependencies中,并添加标记避免重复执行:

class _MyHomePageState extends State<MyHomePage> {
  late TextEditingController controllerText01;
  late AppBarColorProvider appBarColorProviderObj;
  bool _hasInitialized = false; // 标记是否已执行过初始化

  @override
  void initState() {
    super.initState();
    controllerText01 = TextEditingController();
    appBarColorProviderObj = Provider.of<AppBarColorProvider>(
      context,
      listen: false,
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (!_hasInitialized) {
      _hasInitialized = true;
      // 处理同步逻辑
      DateTime now = DateTime.now();
      appBarColorProviderObj.setAppBarColorToRed(now.toString());
      // 处理异步网络请求
      // fetchNetworkData().then((data) {
      //   appBarColorProviderObj.setAppBarColorBasedOnData(data);
      // });
    }
  }

  // 其余代码不变
}

方案3:优化Provider逻辑,避免无意义的通知

在Provider的状态修改方法中,先判断状态是否真的发生变化,再调用notifyListeners(),减少不必要的重建,也能降低异常触发概率:

修改AppBarColorProvider的setAppBarColorToRed方法:

void setAppBarColorToRed(String myStr) {
  String seconds_milli = myStr.split(":")[2];
  String seconds = seconds_milli.split(".")[0];
  int intSeconds = int.tryParse(seconds) ?? 0;

  bool newState = intSeconds > 0 && intSeconds < 29;
  // 仅当状态变化时才更新并通知
  if (newState != _isAppBarRed) {
    _isAppBarRed = newState;
    notifyListeners();
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:56:01