Navigator.removeRoute & Navigator.removeRouteBelow - flutter

There are two functions in Navigator.dart, removeRoute and removeRouteBelow. I don't know how to use these two methods. This is the page route: / -> A -> B -> C -> D. And I perform this code in page D:
Navigator.removeRoute(context, MaterialPageRoute(builder: (context) => PushPageB()));
But there goes something wrong:
══╡ EXCEPTION CAUGHT BY GESTURE ╞═════════════════════════════════
The following assertion was thrown while handling a gesture:
'package:flutter/src/widgets/navigator.dart': Failed assertion: line 1832 pos 12: 'route._navigator
== this': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new
When the exception was thrown, this was the stack:
2 NavigatorState.removeRoute (package:flutter/src/widgets/navigator.dart:1832:12)
3 Navigator.removeRoute (package:flutter/src/widgets/navigator.dart:1221:34)
4 PushPageE.build.<anonymous closure> (package:flutter_navigation/push_page_1.dart:223:29)
5 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:507:14)
6 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:562:30)
7 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
8 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9)
9 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:175:7)
10 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9)
11 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
12 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
13 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:143:19)
14 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22)
15 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7)
16 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7)
17 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7)
18 _invoke1 (dart:ui/hooks.dart:153:13)
19 _dispatchPointerDataPacket (dart:ui/hooks.dart:107:5)
(elided 2 frames from class _AssertionError)
Handler: onTap
Recognizer:
TapGestureRecognizer#2a91c(debugOwner: GestureDetector, state: possible, won arena, finalPosition:
Offset(211.1, 411.1), sent tap down)
══════════════════════════════════════════════════════════════════
I don't know why would this happened, and how use these two methods correctly.

The MaterialPageRoute you have created inside Navigator.removeRoute call has no connection to the Navigator because it wasn't pushed yet. Therefore you've got an error.
In order to use removeRoute method correctly you need to remeber a route you pushed to the Navigator.
I am using following code to keep just one instance of a page in the Navigator.
var filterRoutes = <String, MaterialPageRoute<dynamic>>{'page1': null, 'page2': null, 'page3': null, 'page4': null};
MaterialPageRoute getNextRoute(BuildContext context, String nextPage) {
var route;
switch (nextPage) {
case 'page1':
route = MaterialPageRoute(builder: (context) => Page1());
break;
case 'page2':
route = MaterialPageRoute(builder: (context) => Page2());
break;
case 'page3':
route = MaterialPageRoute(builder: (context) => Page3());
break;
case 'page4':
route = MaterialPageRoute(builder: (context) => Page4());
break;
default:
route = MaterialPageRoute(builder: (context) => SomeOtherPage());
}
if (nextPage != null) {
if (filterRoutes[nextPage] != null) {
Navigator.removeRoute(context, filterRoutes[nextPage]);
}
filterRoutes[nextPage] = route;
}
return route;
}

If you want to remove blow router and go to new route like logout from setting and go to login page, you can do this:
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) =>
LoginScreen()), (Route<dynamic> route) => false);

Related

Unhandled Exception: Null check operator used on a null value in flutter

I hope you are well, I will give you a bit of context about my problem...
I really don't know why this error happens and I've seen many similar publications but they haven't worked for me.
I'm new to flutter and I'm working on an app, but when the user wants to log out, the app crashes and shows the following error:
E/flutter (12659): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value
E/flutter (12659): #0 StatefulElement.state
package:flutter/…/widgets/framework.dart:4999
E/flutter (12659): #1 Navigator.of
package:flutter/…/widgets/navigator.dart:2543
E/flutter (12659): #2 Navigator.pushReplacement
package:flutter/…/widgets/navigator.dart:2105
E/flutter (12659): #3 MainCoordinator.logoutNavigation
package:domicilios/…/MainCoordinator/MainCoordinator.dart:62
E/flutter (12659): #4 UserActions.signOut.<anonymous closure>.<anonymous closure>
package:domicilios/…/View/ProfileTab.dart:222
E/flutter (12659): <asynchronous suspension>
This is the code that generates this error, I hope you can help me, thank you very much for your attention.
this is the class:
extension UserActions on _ProfileTabState {
void signOut(BuildContext context) {
AlertView.showAlertDialog(
model: AlertViewModel(
context,
const AssetImage('assets/logout.png'),
'Cierre de sesión en curso',
"¿Desear salir de la sesión actual?",
'Cerrar sesión',
"Cancelar", () {
_profileTabViewModel
.signOut()
.then((value) => coordinator.logoutNavigation(context: context));
}, () {
Navigator.pop(context);
}));
}
}
That's how I call it in the code:
onTap: () => signOut(context),
This is most likely due to the widget being unmounted before navigating.
You can check if the widget is mounted before navigating.
if (mounted) {
_profileTabViewModel.signOut().then((value) {
if (mounted) {
coordinator.logoutNavigation(context: context);
}
});
}
Also,
if(mounted){
Navigator.pop(context);
}

How can I do contextless navigation in order to move to a maintenance screen in Flutter?

I have a Maintenance controller, that initializez an observable variable, and whenever that variable is true, it navigates to a maintenance screen, and when the variable gets set to false it navigates back as follows:
class MaintenanceController extends GetxController {
RxBool isMaintenance = false.obs;
#override
onInit() {
super.onInit();
ever(
isMaintenance,
(_) => {
if (isMaintenance.value) {Get.offNamed(Routes.maintenance)} else {Get.offNamed(Routes.splash)}
});
}
}
Everything works fine, but I get the following exception in the logs:
flutter: You are trying to use contextless navigation without
a GetMaterialApp or Get.key.
If you are testing your app, you can use:
[Get.testMode = true], or if you are running your app on
a physical device or emulator, you must exchange your [MaterialApp]
for a [GetMaterialApp].
flutter:
#0 GetNavigation.global (package:get/get_navigation/src/extension_navigation.dart:1094:7)
#1 GetNavigation.offNamed (package:get/get_navigation/src/extension_navigation.dart:629:12)
#2 MaintenanceController.onInit.<anonymous closure> (package:n_app/src/screens/maintenance/maintenance_controller.dart:17:85)
#3 ever.<anonymous closure> (package:get/get_rx/src/rx_workers/rx_workers.dart:69:44)
#4 GetStream._notifyData (package:get/get_rx/src/rx_stream/get_stream.dart:47:21)
#5 GetStream.add (package:get/get_rx/src/rx_stream/get_stream.dart:97:5)
#6 RxObjectMixin.value= (package:get/get_rx/src/rx_types/rx_core/rx_impl.dart:105:13)
#7 MaintenanceController.subscribe.<anonymous closure> (package:n_app/src/screens/maintenance/maintenance_controller.dart:29:21)
#8 _rootRunUnary (dart:async/zone.dart:1434:47)
#9 _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#10 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#11 _Bufferi<…>
flutter: ----------------------------------------------------
app.dart:
GetMaterialApp(
debugShowCheckedModeBanner: false,
locale: Get.find<LocalizationController>().deviceLocale,
getPages: Routes.routes,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
unknownRoute: Routes.getNotFound,
initialRoute: GetPlatform.isWeb ? Routes.initial : Routes.splash,
theme: Get.find<ThemeController>().themeData,
navigatorKey: NavigationService.navigatorKey,
);
I tried to add the navigatorKey: Get.key and use navigatorKey.currentState!.pushNamed(routeName); to navigate, but the currentState is always null. Any advice?
You have to use GetMaterialApp instead of MaterialApp for the contextless navigation to work in GetX.
Replace
MaterialApp(home: somePage());
with
GetMaterialApp(home: somePage());
Adding navigatorKey: Get.key to the GetMaterialApp and continuing to use Get.offNamed(...) instead of navigatorKey.currentState!.pushNamed(routeName); worked.

flutter scheduler callback errors?

So I'm running through some tutorials for Flutter Animation, and I had to use something called a Post Frame callback to ensure that function didn't run until after the build function, like so (with the function in question displayed):
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_addTrips();
});
}
void _addTrips() {
// get data from db
List<Trip> _trips = [
Trip(title: 'Beach Paradise', price: '350', nights: '3', img: 'beach.png'),
Trip(title: 'City Break', price: '400', nights: '5', img: 'city.png'),
Trip(title: 'Ski Adventure', price: '750', nights: '2', img: 'ski.png'),
Trip(title: 'Space Blast', price: '600', nights: '4', img: 'space.png'),
];
The build function (within the same initState) looks like this:
#override
Widget build(BuildContext context) {
return AnimatedList(
key: _listKey,
initialItemCount: _tripTiles.length,
itemBuilder: (context, index, animation) {
return SlideTransition(
child: _tripTiles[index],
position: animation.drive(_offset)
);
}
);
}
The items on the list fail to populate when I run the code. The error that I'm getting in the run panel is the following with the stack included:
Exception caught by scheduler library
The following assertion was thrown during a scheduler callback:
'package:flutter/src/widgets/animated_list.dart': Failed assertion: line 963 pos 12:
'itemIndex >= 0 && itemIndex <= _itemsCount': is not true.
Either the assertion indicates an error in the framework itself, or we should provide
substantially more information in this error message to help you determine and fix the
underlying
cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=2_bug.md
When the exception was thrown, this was the stack:
#2 SliverAnimatedListState.insertItem
(package:flutter/src/widgets/animated_list.dart:963:12)
#3 AnimatedListState.insertItem
(package:flutter/src/widgets/animated_list.dart:484:42)
#4 _TripListState._addTrips.<anonymous closure>
(package:ninja_trips/shared/tripList.dart:33:29)
#5 List.forEach (dart:core-patch/growable_array.dart:403:8)
#6 _TripListState._addTrips (package:ninja_trips/shared/tripList.dart:31:12)
#7 _TripListState.initState.<anonymous closure>
(package:ninja_trips/shared/tripList.dart:18:7)
#8 SchedulerBinding._invokeFrameCallback
(package:flutter/src/scheduler/binding.dart:1143:15)
#9 SchedulerBinding.handleDrawFrame
(package:flutter/src/scheduler/binding.dart:1088:9)
#10 SchedulerBinding.scheduleWarmUpFrame.<anonymous closure>
(package:flutter/src/scheduler/binding.dart:863:7)
(elided 13 frames from class _AssertionError, class _RawReceivePortImpl, class _Timer,
dart:async, and dart:async-patch)
The tutorial in question didn't seem to anticipate this as a possibility and I'm pretty stumped so far. Any ideas?
Thanks,
ATD

Dio Interceptor not working on first request in same callback, only subsequent calls

I am testing out dio and tried to add a interceptor to simply add a token to future requests but am getting a weird results I can't seem to fix. All I have as a test is a 2 buttons. One when clicked should log me in and add the token to the interceptor, and the second button requests the auth profile data. For some reason, clicking the log in button I log in fine but get a 403 Forbidden when clicking the second button to access the auth profile data (even though i request the profile data after adding the interceptor). The weird part is that when i click the second button again (without changing any code or even hot reloading) everything works fine and the auth profile data is printed out. Every time I hot restart I get back to this same problem where my first request of the auth profile data has a 403 but subsequent requests work fine. I've been trying to figure out what is going on for a couple hours and cannot understand whats wrong. Please help. Thank you. (The backend is handled by django but the problem cannot be there as the api works with other frameworks and even in dio works fine on subsequent button presses, just not the first)
Code
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Dio session = Dio(
BaseOptions(
connectTimeout: 30000,
baseUrl: 'http://127.0.0.1:8000',
responseType: ResponseType.json,
contentType: ContentType.json.toString(),
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RaisedButton(
child: Text('Log In'),
onPressed: () async {
print('starting');
var res = await session.post('/auth/login/',
data: {'username': 'tester', 'password': 'tester'});
print(res.data);
session.interceptors.clear();
session.interceptors.addAll([
InterceptorsWrapper(
onRequest: (RequestOptions requestOptions) {
session.interceptors.requestLock.lock();
String token = res.data['key'];
if (token != null) {
session.options.headers[HttpHeaders.authorizationHeader] =
‘token $token’;
}
session.interceptors.requestLock.unlock();
return requestOptions;
},
onError: (e) => print(e.message),
),
]);
print(session.interceptors);
}),
RaisedButton(
child: Text('Get Profile'),
onPressed: () async {
session.get('/api/auth/').then((res) => print(res.data));
}),
],
),
),
);
}
}
Console on clicking the log in button
Restarted application in 11,586ms.
flutter: starting
flutter: {key: 745c0a53112e61d54bea5ea725f7fa92e3a2cdbb}
flutter: [Instance of 'InterceptorsWrapper']
Console on the first time clicking the get profile button
flutter: Http status error [403]
[VERBOSE-2:ui_dart_state.cc(177)] Unhandled Exception: DioError [DioErrorType.RESPONSE]: Http status error [403]
#0 DioMixin._request._errorInterceptorWrapper.<anonymous closure>.<anonymous closure>.<anonymous closure>
package:dio/src/dio.dart:870
#1 _rootRunUnary (dart:async/zone.dart:1198:47)
#2 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
#3 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
#4 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
#5 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
#6 Future._completeWithValue (dart:async/future_impl.dart:529:5)
#7 Future._asyncCompleteWithValue.<anonymous closure> (dart:async/future_impl.dart:567:7)
#8 _rootRun (dart:async/zone.dart:1190:13)
#9 _CustomZone.run (dart:async/zone.dart:1093:19)
#10 _CustomZone.runGuarded (dart:async/zone.dart:997:7)
#11 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zon<…>
Console on subsequent times clicking the get profile button
flutter: {id: 3, username: tester, first_name: , last_name: , email: tester#tester.com}
5 more hours and finally figured out the problem.
session.options.headers[HttpHeaders.authorizationHeader] = 'token ' + token;
should be
requestOptions.headers[HttpHeaders.authorizationHeader] = 'token ' + token;

Check if popUntil possible / Check if named route in navigation stack

How can I check if there is a route with name workout in my navigation stack? Whenever I call popUntil and the route is not in my stack, I get:
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Navigator-[GlobalObjectKey<NavigatorState> _WidgetsAppState#9d229](dirty, state: NavigatorState#86712(tickers: tracking 2 tickers)):
'package:flutter/src/widgets/navigator.dart': Failed assertion: line 2330 pos 12: '!_debugLocked': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=BUG.md
The relevant error-causing widget was
MaterialApp
lib/main.dart:58
When the exception was thrown, this was the stack
#2 NavigatorState.build
package:flutter/…/widgets/navigator.dart:2330
#3 StatefulElement.build
package:flutter/…/widgets/framework.dart:4334
I have tried this but popUntil still throws the exception
try {
Navigator.popUntil(context, ModalRoute.withName('workout'));
} catch (e) {
Future.delayed(Duration(milliseconds: 1000), () {
Navigator.push(context, MaterialPageRoute(
builder: (_context) => WorkoutPage(),
settings: RouteSettings(name: 'workout'),
fullscreenDialog: true
));
});
}
If you are working on clearing the entire stack and showing a specific screen, you should try the below code:
Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route route) => false);
It should do the work.