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

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.
});

Related

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.

issues with consumers and providers

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.

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.

Flutter Navigation from Firestore Flag

Diagram of what I want to do]1
I want to navigate to Screen2 from screen1 when the value of screen2_flag changes to true (on firebase cloud firestore ) ,
I have screen 1 as shown in the diagram ,which is working perfectly fine until the value of screen2_flag is false , when I change the value of flag from false to true .
void initState() {
super.initState();
DocumentReference reference =
Firestore.instance.collection('myColection').document('myDoc');
reference.snapshots().listen((querySnapshot) {
print('got sanpshot' + querySnapshot.data['screen2_flag'].toString());
if (querySnapshot.data['screen2_flag'].toString() == 'true') {
Navigator.pushNamed(context, screen2.id);
}
});
Widget build(BuildContext context) {
// my code
...
}
}
I am able to see screen2 on my emulator. But Getting below error on Console
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: setState() callback argument returned a Future.
E/flutter (24551): The setState() method on _WaitingRoomState#25dfa was called with a closure or method that returned a Future. Maybe it is marked as "async".
E/flutter (24551): Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
E/flutter (24551): #0 State.setState. (package:flutter/src/widgets/framework.dart:1151:9)
E/flutter (24551): #1 State.setState (package:flutter/src/widgets/framework.dart:1167:6)
The error told you the problem:
setState() callback argument returned a Future
The setState() method on _WaitingRoomState#25dfa was called with a
closure or method that returned a Future
and told you also the solution:
Instead of performing asynchronous work inside a call to setState(),
first execute the work (without updating the widget state), and then
synchronously update the state inside a call to setState()
So you do the async call,await for it until you get the flag value(as I suppose because you didn't show that part of code), then call setState() with the new values updated.

Set a provide after Future returns results

I'm trying to modify data using a provider after a Future returns with data.
class FirstPart extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getAll(),
initialData: Text('Loading Bro'),
builder: (context, snapshot) {
if (snapshot.hasError)
return Text('error here bro');
else if (snapshot.hasData) {
final toDoListManager = Provider.of<ListManager>(context);
toDoListManager.list = snapshot.data;
return Text('loaded');
}
return Text('load');
},
);
}
}
Future<void> getAll() async {... API Request}
I get an error when I set the value of the provider. (I removed a lot of the error because it was too long for Stack Overflow.)
How can I modify a provider after a Future has results?
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building FutureBuilder<void>(dirty, dependencies:
flutter: [InheritedProvider<ListManager>], state: _FutureBuilderState<void>#fe02e):
flutter: type 'Text' is not a subtype of type 'List<ListItem>'
flutter:
flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
flutter: more information in this error message to help you determine and fix the underlying cause.
flutter: In either case, please report this assertion by filing a bug on GitHub:
flutter: https://github.com/flutter/flutter/issues/new?template=BUG.md
flutter:
flutter: User-created ancestor of the error-causing widget was:
flutter: FirstPart
package:crud_todo/home.dart:16
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0 FirstPart.build.<anonymous closure>
package:crud_todo/home.dart:32
flutter: #1 _FutureBuilderState.build (package:flutter/src/widgets/async.dart)
setState() or markNeedsBuild() called during build.
There are some strange things going on there:
Your toDoListManager is a ListManager, toDoListManager.list returns a List and you are trying to assing a Text to thise list item since snapshot.data returns a Text Widget as initialData toDoListManager.list = snapshot.data;
Another strange thing is that your getAll returns a future of void when in my opinion should return a future of List