setState() or markNeedsBuild() called during build. Flutter Provider Package - flutter

homepage.dart
#override
void initState() {
super.initState();
_userProfile = Provider.of<UserDataProvider>(context,listen: false).userData;
if (_userProfile['isPaid'] == false) {
Navigator.of(context).pushNamed(PaymentScreen.routeName);
}
}
Error
setState() or markNeedsBuild() called during build.
This error is bugging me. Please help me resolve this.
Thanks.

You can use your code as below:
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, (){
_userProfile = Provider.of<UserDataProvider>(context,listen: false).userData;
if (_userProfile['isPaid'] == false) {
Navigator.of(context).pushNamed(PaymentScreen.routeName);
}
});
}

Related

LateError (LateInitializationError: Field '_state#590051772' has not been initialized.)

I don't understand why I'm catching an error in _mapController:
final MapController _mapController = MapController();
#override
void initState() {
super.initState();
_mapController.move(widget.latLng, _mapController.zoom);
}
I don't know but try like this ? :
late MapController _mapController;
#override
void initState() {
_mapController = MapController();
_mapController.move(widget.latLng, _mapController.zoom);
super.initState();
}
Try below code
MapController? _mapController;
//or try this-> MapController _mapController;
#override
void initState() {
super.initState();
_mapController.move(widget.latLng, _mapController.zoom);
}
I think the problem arises because you start using the mapcontroller before the build function starts which has the necessary configuration for the map. Try to put this line
_mapController.move(widget.latLng, _mapController.zoom);
inside a method for example (_moveMap) and call it inside the build function when pressing a button.
like this:
child: ElevatedButton(
child:Icon(Icons.location_on_outlined,
color: Colors.blue,) ,
onPressed:_moveMap ,
)),

Using Provider in Widget's initState or initialising life-cycle

So while learning Flutter, it seems that initState() is not a place to use Providers as it does not yet have access to context which must be passed. The way my instructor gets around this is to use the didChangeDependencies() life-cycle hook in conjunction with a flag so that any code inside doesn't run more than once:
bool _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
// Some provider code that gets/sets some state
}
_isInit = false;
super.didChangeDependencies();
}
This feels like a poor development experience to me. Is there no other way of running initialisation code within a Flutter Widget that has access to context? Or are there any plans to introduce something more workable?
The only other way I have seen is using Future.delayed which feels a bit "hacky":
#override
void initState() {
Future.delayed(Duration.zero).then(() {
// Some provider code that gets/sets some state
});
super.initState();
}
I have implemented as follows inside didChangeDependencies
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<Products>(context).fetchAndSetProducts().then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
}
It's possible to schedule code to run at the end of the current frame. If scheduled within initState(), it seems that the Widget is fully setup by the time the code is running.
To do so, you can use the addPostFrameCallback method of the SchedulerBinding instance:
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
// Some provider code that gets/sets some state
})
}
You can also use WidgetsBinding.instance.addPostFrameCallback() for this. They both behave the same for the purpose of running code once after the Widget has been built/loaded, but here is some more detail on the differences.
Note: be sure to import the file needed for SchedulerBinding:
import 'package:flutter/scheduler.dart';
you can have the Provider in a separate function and call that function within the initState()
bool isInit = true;
Future<void> fetch() async {
await Provider.of<someProvider>(context, listen: false).fetch();
}
#override
void initState() {
if (isInit) {
isInit = false;
fetch();
}
isInit = false;
super.initState();
}

Calling Provider custom method inside setState() caused error: or markNeedsBuild() called during build

I have a flutter widget state class as follow. I call Provider.of<AppData>(context, listen: false).recalculateCart(); inside initState().
When I run, I'm getting the error said setState() or markNeedsBuild() called during build.
How should I resolve those?
class CartPageState extends State<CartPage> {
final TextEditingController _couponController = TextEditingController();
#override
void initState() {
// TODO: implement initState
super.initState();
Provider.of<AppData>(context, listen: false).recalculateCart();
}
#override
Widget build(BuildContext context) {
if (Provider.of<AppData>(context, listen: false).selectedStoreId == null){
Provider.of<AppData>(context, listen: false).setPageAfterStoreSelection('cart');
return ChooseStorePage(title: 'Choose Store to Continue');
}
...
}
Also, here is the recalculateCart() function:
void recalculateCart() {
notifyListeners();
}
The problem is that there is a re build when the actual build hasn't finished yet, so you can put you function inside a addPostFrameCallback method to execute the function after the first build has finished, try the next:
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<AppData>(context, listen: false).recalculateCart();
});
}

Unhandled Exception: inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before _ScreenState.initState() completed

I am calling initial method to load data from API using initState. But it is resulting me an error. Here is error:
Unhandled Exception: inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before _ScreenState.initState() completed.
When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget.
My code is:
#override
void initState() {
super.initState();
this._getCategories();
}
void _getCategories() async {
AppRoutes.showLoader(context);
Map<String, dynamic> data = await apiPostCall(
apiName: API.addUser,
context: context,
parameterData: null,
showAlert: false,
);
if(data.isNotEmpty){
AppRoutes.dismissLoader(context);
print(data);
}else {
AppRoutes.dismissLoader(context);
}
}
You need to call _getCategories after initState has completed.
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () {
this._getCategories();
});
// Could do this in one line: Future.delayed(Duration.zero, this._getCategories);
}
Also, you could do this on a different way, using addPostFrameCallback.
To make this task easier, you could create a mixin to be added to StatefulWidgets.
mixin PostFrameMixin<T extends StatefulWidget> on State<T> {
void postFrame(void Function() callback) =>
WidgetsBinding.instance?.addPostFrameCallback(
(_) {
// Execute callback if page is mounted
if (mounted) callback();
},
);
}
Then, you just need to plug this mixin to you page, like that:
class _MyPageState extends State<MyPage> with PostFrameMixin {
#override
void initState() {
super.initState();
postFrame(_getCategories);
}
}
Use the didChangeDependencies method which gets called after initState.
For your example:
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
this._getCategories();
}
void _getCategories() async {
// Omitted for brevity
// ...
}
Adding a frame callback might be better than using Future.delayed with a zero duration - it's more explicit and clear as to what is happening, and this kind of situation is what frame callback was designed for:
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
_getCategories();
});
}
an alternative is to put it inside PostFrameCallback which is between initState and Build.
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => getData());
super.initState();
}
getData() async {
}
There are many ways to solve this problem, override initState method:
#override
void initState() {
super.initState();
// Use any of the below code here.
}
Using SchedulerBinding mixin:
SchedulerBinding.instance!.addPostFrameCallback((_) {
// Call your function
});
Using Future class:
Future(() {
// Call your function
});
Using Timer class:
Timer(() {
// Call your function
});
The best solution i think is use the context from the Widget build. And paste the method _getCategories(context) after the build with the context from the tree.
So there is no problem with the widget tree.

setState() called after dispose()

When I click the raised button, the timepicker is showing up. Now, if I wait 5 seconds, for example, and then confirm the time, this error will occur:
setState() called after dispose()
I literally see in the console how flutter is updating the parent widgets, but why? I don't do anything - I just wait 5 seconds?!
The example below will work in a normal project, however in my project which is quite more complex it won't work because Flutter is updating the states while I am waiting... What am I doing wrong? Does anyone have a guess at what it could be that Flutter is updating randomly in my more complex project and not in a simple project?
[UPDATE]
I took a second look at it and found out it is updating from the level on where my TabBar and TabBarView are.
Could it have to do something with the "with TickerProviderStateMixin" which I need for the TabBarView? Could it be that it causes the app to refresh regularly and randomly?
class DateTimeButton extends State<DateTimeButtonWidget> {
DateTime selectedDate = new DateTime.now();
Future initTimePicker() async {
final TimeOfDay picked = await showTimePicker(
context: context,
initialTime: new TimeOfDay(hour: selectedDate.hour, minute: selectedDate.minute),
);
if (picked != null) {
setState(() {
selectedDate = new DateTime(selectedDate.year, selectedDate.month, selectedDate.day, picked.hour, picked.minute);
});
}
}
#override
Widget build(BuildContext context) {
return new RaisedButton(
child: new Text("${selectedDate.hour} ${selectedDate.minute}"),
onPressed: () {
initTimePicker();
}
);
}
}
Just check boolean property mounted of the state class of your widget before calling setState().
if (this.mounted) {
setState(() {
// Your state change code goes here
});
}
Or even more clean approach
Override setState method in your StatelfulWidget class.
class DateTimeButton extends StatefulWidget {
#override
void setState(fn) {
if(mounted) {
super.setState(fn);
}
}
}
If it is an expected behavior that the Future completes when the widget already got disposed you can use
if (mounted) {
setState(() {
selectedDate = new DateTime(selectedDate.year, selectedDate.month, selectedDate.day, picked.hour, picked.minute);
});
}
Just write one line before setState()
if (!mounted) return;
and then
setState(() {
//Your code
});
I had the same problem and i solved changing the super constructor call order on initState():
Wrong code:
#override
void initState() {
foo_bar(); // call setState();
super.initState(); // then foo_bar()
}
Right code:
#override
void initState() {
super.initState();
foo_bar(); // first call super constructor then foo_bar that contains setState() call
}
To prevent the error from occurring, one can make use of the mounted property of the State class to ensure that a widget is mounted before settings its state:
// First Update data
if (!mounted) {
return;
}
setState(() { }
Try this
Widget build(BuildContext context) {
return new RaisedButton(
child: new Text("${selectedDate.hour} ${selectedDate.minute}"),
onPressed: () async {
await initTimePicker();
}
);
}
class MountedState<T extends StatefulWidget> extends State<T> {
#override
Widget build(BuildContext context) {
return null;
}
#override
void setState(VoidCallback fn) {
if (mounted) {
super.setState(fn);
}
}
}
Example
To prevent the error,Instead of using State use MountedState
class ExampleStatefulWidget extends StatefulWidget {
const ExampleStatefulWidget({Key key}) : super(key: key);
#override
_ExampleStatefulWidgetState createState() => _ExampleStatefulWidgetState();
}
class _ExampleStatefulWidgetState extends MountedState<ExampleStatefulWidget> {
....
}
I had this error when I mistakenly called super.initState before the variable. Check this:
#override
void initState() {
super.initState();
bloc = MainBloc();
}
Should be fixed as
#override
void initState() {
bloc = MainBloc();
super.initState();
}
The problem could occur when you have long asynchronous operation in stateful widget that could be closed/disposed before the operation finished.
Futures in Dart are not preemptive, so the only way is to check if a widget mounted before calling setState.
If you have a lot of widgets with asynchrony, adding ton of if (mounted) checks is tedious and an extension method might be useful
extension FlutterStateExt<T extends StatefulWidget> on State<T> {
void setStateIfMounted(VoidCallback fn) {
if (mounted) {
// ignore: invalid_use_of_protected_member
setState(fn);
}
}
}