I my test code I am trying to set the color of the appbar according to the result of a test that takes place early in the build method. I need to use provider
because in my actual code, the code resides in a widget's code file which is called in the main page, and also I need to share the state across the entire project.
Currently, I am getting an exception as follows:
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)
A delay in executing color setting code would not be actual solution in my case.
The condition test in actual code requires to test variable involving Future. Currently, despite future, there's no delay. But in near future I might implement internet fetch resulting in some delay. So, I think the code can't go into initState.
Below, is the code:
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();
}
}
Kindly refactor your MyHomePage
class, here's the code snippet:
initState()
method, Initialize Onceimport 'package:flutter/material.dart';
import 'package:scaffold_color_after_a_delay_using_provider/scaffold_color_provider.dart';
import 'package:provider/provider.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late TextEditingController controllerText01;
late AppBarColorProvider appBarColorProviderObj;
final now = DateTime.now();
@override
void initState() {
super.initState();
controllerText01 = TextEditingController();
appBarColorProviderObj = context.read<AppBarColorProvider>();
WidgetsBinding.instance.addPostFrameCallback((_) {
appBarColorProviderObj.setAppBarColorToRed(now.toString());
});
}
@override
void dispose() {
controllerText01.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
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())],
),
);
}
}
initState()
Method to Avoid One-time State Changes:import 'package:flutter/material.dart';
import 'package:scaffold_color_after_a_delay_using_provider/scaffold_color_provider.dart';
import 'package:provider/provider.dart';
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();
}
@override
void dispose() {
controllerText01.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
appBarColorProviderObj = Provider.of<AppBarColorProvider>(
context,
listen: false,
); // NOTE: you can initialize this inside the build()
final 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())],
),
);
}
}
This will resolve the exception:
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)
Also, provide what you're trying to achieve.
I hope this helps!