I'm experimenting with Provider, but I get the error setState() or markNeedsBuild() called during build
, if I change data and use notifyListeners()
.
What I do:
I initialize mainElementList
holding a simple object when starting the App:
void main() {
List mainElementList = [MainElement()];
runApp(MyApp(
mainElementList: mainElementList,
));
}
For now MainElement
only has one property:
class MainElement {
bool allTasksDone = false;
}
Then, still in main.dart
, I use mainElementlist
as argument to set up a Provider (MainElementList
) wrapping the MaterialApp
:
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MainElementList(widget.mainElementList),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Three Things',
initialRoute: '/',
routes: {
'/': (ctx) => TabsScreen(),
},
),
);
The Provider class:
class MainElementList with ChangeNotifier {
List _mainElementList;
MainElementList(this._mainElementList);
get mainElementList {
return [..._mainElementList];
}
void toggleAllTasksDone() {
_mainElementList[0].allTasksDone = !_mainElementList[0].allTasksDone;
notifyListeners();
}
}
A couple of steps down the widget tree, I'm testing, whether I can toggle allTasksDone
; I do this in a stateless (edit: typo; it's stateful) widget's build method before the return statement:
MainElementList mainElementList = Provider.of<MainElementList>(context);
print('Before: ${mainElementList.mainElementList[0].allTasksDone}');
mainElementList.toggleAllTasksDone();
print('After: ${mainElementList.mainElementList[0].allTasksDone}');
It works in so far that the value is toggled. But if I use notifyListeners in .toggleAllTasksDone, I get this:
> Exception caught by foundation library
> ════════════════════════════════ The following assertion was thrown
> while dispatching notifications for MainElementList: setState() or
> markNeedsBuild() called during build.
>
> This _InheritedProviderScope<MainElementList?> 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<MainElementList?>
> value: Instance of 'MainElementList'
> listening to value The widget which was currently being built when the offending call was made was: MainElementWidget
> dirty
Any help highly appreciated!
__ Edit: Full widget as per request:
class MainElementWidget extends StatefulWidget {
const MainElementWidget({
Key? key,
}) : super(key: key);
@override
State<MainElementWidget> createState() => _MainElementWidgetState();
}
class _MainElementWidgetState extends State<MainElementWidget> {
@override
Widget build(BuildContext context) {
//RESPONSIVENESS
final availableWidth = context.read<DimensionsProvider>().availableWidth;
final mainElementSize = availableWidth * 0.8;
//COLORS
final customColorScheme = context.read<CustomColors>().customColorScheme;
//MAIN ELEMENT LIST
MainElementList mainElementList = Provider.of<MainElementList>(context);
print('Before: ${mainElementList.mainElementList[0].allTasksDone}');
mainElementList.toggleAllTasksDone();
print('After: ${mainElementList.mainElementList[0].allTasksDone}');
return ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Container(
color: customColorScheme['Primary 3'],
child: SizedBox(
width: mainElementSize,
height: mainElementSize,
),
),
);
}
}
Yes, I'm overusing maps ...sue me :)