I have a splash screen and a StreamBuilder
that emits a state that contains information about authentication status. When the authentication status is known, I want to navigate either to sign in page or home page. But when I write something like Navigator.of(context).pushReplacement(...)
I get
I/flutter ( 2058): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ I/flutter ( 2058): The following assertion was thrown building StreamBuilder(dirty, state: I/flutter ( 2058): _StreamBuilderBaseState>#f4346): I/flutter ( 2058): setState() or markNeedsBuild() called during build. I/flutter ( 2058): This Overlay widget cannot be marked as needing to build because the framework is already in the I/flutter ( 2058): process of building widgets. A widget can be marked as needing to be built during the build phase I/flutter ( 2058): only if one of its ancestors is currently building. This exception is allowed because the framework I/flutter ( 2058): builds parent widgets before children, which means a dirty descendant will always be built. I/flutter ( 2058): Otherwise, the framework might not visit this widget during this build phase. I/flutter ( 2058): The widget on which setState() or markNeedsBuild() was called was: I/flutter ( 2058): Overlay-[LabeledGlobalKey#e0460](state: OverlayState#ab1a5(entries: I/flutter ( 2058): [OverlayEntry#4e962(opaque: false; maintainState: false), OverlayEntry#7656a(opaque: false; I/flutter ( 2058): maintainState: true), OverlayEntry#1f86e(opaque: false; maintainState: false), I/flutter ( 2058): OverlayEntry#05a15(opaque: false; maintainState: true)])) I/flutter ( 2058): The widget which was currently being built when the offending call was made was: I/flutter ( 2058): StreamBuilder(dirty, state: _StreamBuilderBaseState>#f4346) I/flutter ( 2058): I/flutter ( 2058): When the exception was thrown, this was the stack: I/flutter ( 2058): #0 Element.markNeedsBuild. (package:flutter/src/widgets/framework.dart:3503:11) I/flutter ( 2058): #1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3529:6) I/flutter ( 2058): #2 State.setState (package:flutter/src/widgets/framework.dart:1133:14) I/flutter ( 2058): #3 OverlayState.insertAll (package:flutter/src/widgets/overlay.dart:346:5) I/flutter ( 2058): #4 OverlayRoute.install (package:flutter/src/widgets/routes.dart:43:24) I/flutter ( 2058): #5 TransitionRoute.install (package:flutter/src/widgets/routes.dart:180:11) I/flutter ( 2058): #6 ModalRoute.install (package:flutter/src/widgets/routes.dart:895:11) I/flutter ( 2058): #7 NavigatorState.pushReplacement (package:flutter/src/widgets/navigator.dart:1799:14) I/flutter ( 2058): #8 _replace (package:map_chat/application/navigation.dart:75:27) I/flutter ( 2058): #9 _SignInPage.replace (package:map_chat/application/navigation.dart:67:5) I/flutter ( 2058): #10 Roadmap.replace (package:map_chat/application/navigation.dart:25:18) I/flutter ( 2058): #11 _SplashPageState._buildPageBasedOnAuthenticationState (package:map_chat/feature/splash.dart:52:19) I/flutter ( 2058): #12 _SplashPageState._buildSplashScreen (package:map_chat/feature/splash.dart:40:11) I/flutter ( 2058): #13 _SplashPageState._buildPage. (package:map_chat/feature/splash.dart:27:18) I/flutter ( 2058): #14 StreamBuilder.build (package:flutter/src/widgets/async.dart:425:74) I/flutter ( 2058): #15 _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:125:48) I/flutter ( 2058): #16 StatefulElement.build (package:flutter/src/widgets/framework.dart:3825:27) I/flutter ( 2058): #17 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3739:15) I/flutter ( 2058): #18 Element.rebuild (package:flutter/src/widgets/framework.dart:3565:5) I/flutter ( 2058): #19 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2278:33) I/flutter ( 2058): #20 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:700:20) I/flutter ( 2058): #21 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:286:5) I/flutter ( 2058): #22 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1012:15) I/flutter ( 2058): #23 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:952:9) I/flutter ( 2058): #24 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.scheduleWarmUpFrame. (package:flutter/src/scheduler/binding.dart:773:7) I/flutter ( 2058): #33 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:382:19) I/flutter ( 2058): #34 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:416:5) I/flutter ( 2058): #35 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:171:12) I/flutter ( 2058): (elided 8 frames from package dart:async and package dart:async-patch)
The only workaround I found is to schedule the navigation to the end of event queue using Future(...).then(navigate)
but that's sick. Is here an adequate solution for this?
You can listen your stream outside of build method and redirect to another view from there.
---- EDITED ----
This is an example of how you can do that:
@override
void initState() {
super.initState();
Future.delayed(Duration.zero, _verify);
}
void _verify() {
final _myBloc = BlocProvider.getBloc<MyBloc>();
_myBloc.myStream.listen((data) {
// Redirect to another view, given your condition
if (data) {
Navigator.of(context).pushNamed("my-new-route");
}
});
}
Just remember to save the StreamSubscription
object returned from listen
method, so you can cancel the subscription on dispose()
.