issues with consumers and providers - flutter

Hello I am facing an issue with my flutter app using providers and consumer.
everything works fine how ever I have this issue in my debugger:
════════ Exception caught by foundation library ════════════════════════════════
The following assertion was thrown while dispatching notifications for CatergoryProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<CatergoryProvider> 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<CatergoryProvider>
value: Instance of 'CatergoryProvider'
listening to value
The widget which was currently being built when the offending call was made was: Consumer<CatergoryProvider>
dirty
dependencies: [_InheritedProviderScope<CatergoryProvider>]
When the exception was thrown, this was the stack
#0 Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:4138
#1 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4153
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents
package:provider/src/inherited_provider.dart:496
#3 ChangeNotifier.notifyListeners
package:flutter/…/foundation/change_notifier.dart:243
#4 CatergoryProvider.setLoading
package:quizz_app/provider/catergoryProvider.dart:43
...
The CatergoryProvider sending notification was: Instance of 'CatergoryProvider'
here is the consumer in question:
Consumer<CatergoryProvider>(
builder: (context, catergory, child) {
if (!catergory.isLoading() && !catergory.isList()) {
catergory.fetchCatergory();
}
catergories = catergory.getList();
return catergory.isLoading()
? ModalProgressHUD(
opacity: 0,
inAsyncCall: catergory.isLoading(),
child: Container())
: catergoryPage(catergories, context);
},
),
and this is where the stack trace leads me to:
void setLoading(value) {
loading = value;
notifyListeners();
}

It looks like you are probably calling setLoading(value) inside of a build method.. This would try to trigger a rebuild of the widget when notifyListeners() is invoked during the build of the widget.
Consider using a FutureBuilder or a StreamBuilder to change state depending when waiting on an async action.

Related

Flutter Navigation 2.0 + Bloc

I am currently trying to learn Navigation 2.0 in conjunction with BLoC.
I've followed the raywenderlich's guide [1] successfully (it's a bit outdated) and i tried to move forward managing the state with BLoC (this guide uses Provider) and when i did it successfully, i tried to take a step further and i tried to follow JalalOkbi's guide [2] because of a more advanced abstraction level provided. The third link [3] is the github repo with the full (now failing) project.
But after 5 days of trying i stumbled in several errors and i can't figure this out: i am currently facing this error:
I/flutter (10212): looking for /
I/flutter (10212): found Splash("null", null, null)
I/flutter (10212): looking for /
I/flutter (10212): found Splash("null", null, null)
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Builder:
The settings getter of a page-based Route must return a Page object. Please set the settings to the Page in the Page.createRoute method.
'package:flutter/src/widgets/navigator.dart':
package:flutter/…/widgets/navigator.dart:1
Failed assertion: line 3361 pos 9: 'entry.route.settings == page'
════════ Exception caught by widgets library ═══════════════════════════════════
A GlobalKey was used multiple times inside one widget's child list.
The relevant error-causing widget was
MaterialApp
lib\main.dart:40
════════════════════════════════════════════════════════════════════════════════
[1] https://www.raywenderlich.com/19457817-flutter-navigator-2-0-and-deep-links
[2] https://medium.com/#JalalOkbi/flutter-navigator-2-0-with-bloc-the-ultimate-guide-6672b115adf
[3] https://github.com/msimoncini90/flutter_navigation_2.0
If your page object is defined like
class SplashPage extends Page {
#override
Route createRoute(BuildContext context) {
return MaterialPageRoute(
builder: (BuildContext context) => const SplashScreen(),
);
}
}
The issue is that there is a missing setting in the page definition, which is what the error is showing. The correct code looks like
class SplashPage extends Page {
#override
Route createRoute(BuildContext context) {
return MaterialPageRoute(
settings: this,
builder: (BuildContext context) => const SplashScreen(),
);
}
}
Notice that the settings: this parameter, which solves what the error is describing.

showModalBottomSheet and Unhandled Exception: setState() called after dispose() on parent widget

Context:
I have a modal bottom sheet that pops up, upon selection of Camera/Gallery acquires/selects an image XFile and returns it for processing (uploading) done with the help of image_picker.
This is done with a sample line:
ListTile(
onTap: () {
// definition: Future<XFile?> showCamera(IdPhotoOrientation orientation);
showCamera(orientation).then((value) => Navigator.of(context).pop<XFile?>(value));
},
...
),
Picking an image with showModalBottomSheet is done by returning the selected XFile and processing it on a chained function _handleFile(XFile, enum):
return showModalBottomSheet<XFile?>(
context: context,
builder: (context) {
return SingleChildScrollView(
child: ListBody(
children: [
...
ListTile(
onTap: () {
showCamera(orientation).then((value) => Navigator.of(context).pop<XFile?>(value));
},
leading: Icon(Icons.camera),
title: Text("From Camera"),
),
...
],
),
);
},
).then((value) => _handleFile(value, orientation));
What is the problem:
While processing file in _handle(XFile?, int), I need to update the state of the app to show progress bar updates, circular indicators, uploading status, etc.
Future<void> _handleFile(XFile? xfile, int orientation) {
if (xfile == null) {
return Future.value();
}
// store locally with Uploading Status
var imageService = locator<ImageService>();
setState(() { <-------- offending line (ui_partner_registration_id_photos.dart:103:5)
remoteImageStatus[xfile] = UploadStatus.Uploading;
images[orientation] = xfile;
});
// Upload and update result / error
return imageService.uploadIDPhoto(File(xfile.path), orientation).then((value) {
setState(() {
idPhotos[orientation] = value;
remoteImageStatus[xfile] = UploadStatus.Done;
});
print("Uploaded [${xfile.path}]");
}).onError((error, stackTrace) {
print("Error uploading image");
print(stackTrace);
setState(() {
remoteImageStatus[xfile] = UploadStatus.Error;
});
});
}
Why is this a problem?
setState() cannot be called on a stateful widget that is no longer visible/active/in-focus which is now the case for the showModalBottomSheet. That being said, after calling Navigator.pop() this should no longer be the case as the parent stateful widget is now in focus, this is causing my confusion.
(temporary) Solution
A temporary solution (which does not give exactly the desired result) is to add a mounted check as described here with an example here:
if (mounted) {
setState((){
// perform actions
})
}
StackTrace:
[VERBOSE-2:ui_dart_state.cc(199)] Unhandled Exception: setState() called after dispose(): _RegisterIDPhotosState#b75f9(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0 State.setState.<anonymous closure> (package:flutter/src/widgets/framework.dart:1052:9)
#1 State.setState (package:flutter/src/widgets/framework.dart:1087:6)
#2 _RegisterIDPhotosState._handleFile (my-awesome-app/viewcontrollers/register/partner/ui_partner_registration_id_photos.dart:103:5)
#3 _RegisterIDPhotosState.pickImageWithModalPopup.<anonymous closure> (package:my-awesome-app/viewcontrollers/register/partner/ui_partner_registration_id_photos.dart:188:23)
#4 _rootRunUnary (dart:async/zone.dart:1362:47)
#5 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
<asynchronous suspension>
Question:
After selecting a file and starting the upload process, how can I call setState() as in the example of _handleFile(XFile?, int) above?
Refactor that logic to a ChangeNotifier or ValueNotifier higher up in the widget tree and make your Widgets use it to share state between them see the official docs for a more in thorough description.
The setState approach won't work because you are handling 2 different widgets there. You state:
"That being said, after calling Navigator.pop() this should no longer be the case as the parent stateful widget is now in focus, this is causing my confusion."
Whats causing your confusion is that setState is not a global callback which is executed in the currently focused Sateful Widget, setState is nothing more than executing your callback and calling markNeedsBuild for the specific widget in which the setState call was made, which in your case is no longer mounted.
That being said the docs I pointed you to is a recommended way of sharing state in a Flutter app.

setState() or markNeedsBuild() called during build inside a StreamBuilder

I am using this StreamBuilder to get the current location:
StreamBuilder<UserLocation>(
stream: locationService.locationStream,
builder: (context, snapshot) {
if (snapshot.data != null) {
bool es_actual = ubicacionesProvider.ubicacionActualSeleccionada;
bool es_elegida = ubicacionesProvider.ubicacionElegidaSeleccionada;
if(es_actual){
latitudData = snapshot.data.latitude;
// ubicacionesProvider.setlatitudActual(latitudData);
longitudData = snapshot.data.longitude;
//ubicacionesProvider.setlongitudActual(longitudData);
Coordinates misCoordenadas =
new Coordinates(latitudData, longitudData);
// ubicacionesProvider.setubicacionActual(_miDireccionActual);
getAddress(misCoordenadas);
}
if(es_elegida){
_latitudElegida = ubicacionesProvider.latitudElegida;
_longitudElegida = ubicacionesProvider.longitudElegida;
_miDireccionActual = ubicacionesProvider.ubicacionElegida;
}
}
I want to update a provider called ubicacionesProvider with some changes:
ubicacionesProvider.setlatitudActual(latitudData)
ubicacionesProvider.setlongitudActual(longitudData)
ubicacionesProvider.setubicacionActual(_miDireccionActual)
But I am getting a warning using one or all of them, the app is not exiting but the warning is shown:
======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for UbicacionesProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<UbicacionesProvider> 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<UbicacionesProvider>
value: Instance of 'UbicacionesProvider'
listening to value
The widget which was currently being built when the offending call was made was: StreamBuilder<UserLocation>
dirty
dependencies: [MediaQuery]
state: _StreamBuilderBaseState<UserLocation, AsyncSnapshot<UserLocation>>#39568
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4292:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4307:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:496:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:226:25)
#4 UbicacionesProvider.setlatitudActual (package:flutter_qplan/providers/ubicaciones_provider.dart:50:5)
...
The UbicacionesProvider sending notification was: Instance of 'UbicacionesProvider'
====================================================================================================
I would like to update the provider without getting that warning.
Usually this happens when you setState or notifyListeners before the build has finished building all the widgets. Maybe you can add your update logic like this :
WidgetsBinding.instance!.addPostFrameCallback((_) {
// Add Your Update Code here.
});

Flutter Mobx - setState() or markNeedsBuild() called during build

When I try to navigate from a page to another, I'm getting the error bellow. After some research i found some solutions as calling the navigation inside a SchedulerBinding.instance.addPostFrameCallback, but even if that solve in some cases, for this specific case none of them are working.
I'm using an IndexedStack with three AnimatedOpacity wrapping one different page. Besides the error, could be the way on that the IndexedStack renders its content?
Seems to be cause it renders all the pages at the same time while shows only the current index. But how i can solve this if a i need switch between these pages with the same state? I tried with PageView but the state is miss when change from one page to another.
Anyone can help me on this?
════════ Exception caught by flutter_mobx ══════════════════════════════════════
The following MobXCaughtException was thrown:
setState() or markNeedsBuild() called during build.
This Observer 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:
Observer
The widget which was currently being built when the offending call was made was:
AnimatedOpacity
When the exception was thrown, this was the stack
#0 Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:4167
#1 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4182
#2 ObserverElementMixin.invalidate
package:flutter_mobx/src/observer_widget_mixin.dart:70
#3 ReactionImpl._run
package:mobx/…/core/reaction.dart:119
#4 ReactiveContext._runReactionsInternal
package:mobx/…/core/context.dart:345
...
The build method:
#override
Widget build(BuildContext context) {
return IndexedStack(
index: index,
children: <Widget>[
AnimatedOpacity(
key: Key('animatedOpacity_0'),
opacity: index == 0 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Page1(),
),
AnimatedOpacity(
key: Key('animatedOpacity_1'),
opacity: index == 1 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Page2(),
),
AnimatedOpacity(
key: Key('animatedOpacity_2'),
opacity: index == 2 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Page3(),
),
],
);
}
UPDATE:
The problem occurs on the third AnimatedOpacity, and if i remove the AnimatedOpacity widgets, the problems doesn't occurs but i can't figure it out why. Someone has some explanation about that?

Showing dialog with bottom navigationbar flutter

I want to show Dialog instead of function using bottom navigation bar in flutter.How can I implement that?
#override
Widget build(BuildContext context) {
Widget child = Container();
switch(_selectedIndex) {
case 0:
child = function_1();
print("done");
break;
case 1:
child = function_1();
break;
}
When I use ShowDialog method It says:
The following assertion was thrown building ServerGrid(dirty; state: _ServerGridState#59211289()):
I/flutter (14351): setState() or markNeedsBuild() called during build.
I/flutter (14351): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter (14351): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter (14351): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter (14351): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter (14351): Otherwise, the framework might not visit this widget during this build phase.