'Navigation in flutter without context
I created a service folder and made a file in it called request. dart, here I intend to place all requests I make into a class called AuthService, with the login request below I want to be able to navigate to the home screen once response.statusCode == 200 or 201 but I am unable to do that because navigation requires a context and my class is neither a Stateful nor Stateless widget, is there any way I can navigate without the context??
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';
class AuthService {
login(email, password) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
if (email == "" && password == "") {
return;
}
try {
Map data = {'email': email, 'password': password};
var jsonResponse;
var response = await http
.post('https://imyLink.com/authenticate', body: data);
if (response.statusCode == 200 || response.statusCode == 201) {
//I want to navigate to my home screen once the request made is successful
jsonResponse = json.decode(response.body);
if (jsonResponse != null) {
await sharedPreferences.setString("userToken", jsonResponse["token"]);
var token = sharedPreferences.getString("userToken");
print('Token: $token');
print(jsonResponse);
print("Login successful");
}
} else {
print(response.statusCode);
print('Login Unsuccessful');
print(response.body);
}
} catch (e) {
print(e);
}
}
Solution 1:[1]
OPTION 1
If you will be calling the login
method in either a Stateful
or Stateless
widget. You can pass context
as a parameter to the login
method of your AuthService
class.
I added a demo using your code as an example:
class AuthService {
// pass context as a parameter
login(email, password, context) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
if (email == "" && password == "") {
return;
}
try {
Map data = {'email': email, 'password': password};
var jsonResponse;
var response = await http
.post('https://imyLink.com/authenticate', body: data);
if (response.statusCode == 200 || response.statusCode == 201) {
//I want to navigate to my home screen once the request made is successful
Navigator.of(context).push(YOUR_ROUTE); // new line
jsonResponse = json.decode(response.body);
if (jsonResponse != null) {
await sharedPreferences.setString("userToken", jsonResponse["token"]);
var token = sharedPreferences.getString("userToken");
print('Token: $token');
print(jsonResponse);
print("Login successful");
}
} else {
print(response.statusCode);
print('Login Unsuccessful');
print(response.body);
}
} catch (e) {
print(e);
}
}
OPTION 2
You can access your app's Navigator without a context by setting the navigatorKey property of your MaterialApp:
/// A key to use when building the [Navigator].
///
/// If a [navigatorKey] is specified, the [Navigator] can be directly
/// manipulated without first obtaining it from a [BuildContext] via
/// [Navigator.of]: from the [navigatorKey], use the [GlobalKey.currentState]
/// getter.
///
/// If this is changed, a new [Navigator] will be created, losing all the
/// application state in the process; in that case, the [navigatorObservers]
/// must also be changed, since the previous observers will be attached to the
/// previous navigator.
final GlobalKey<NavigatorState> navigatorKey;
Create the key:
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
Pass it to MaterialApp:
new MaterialApp(
title: 'MyApp',
navigatorKey: key,
);
Push routes (both named and non-named routes work):
navigatorKey.currentState.pushNamed('/someRoute');
Find more details about option 2 by following the github issue below: https://github.com/brianegan/flutter_redux/issues/5#issuecomment-361215074
Solution 2:[2]
First, create a class
import 'package:flutter/material.dart';
class NavigationService{
GlobalKey<NavigatorState> navigationKey;
static NavigationService instance = NavigationService();
NavigationService(){
navigationKey = GlobalKey<NavigatorState>();
}
Future<dynamic> navigateToReplacement(String _rn){
return navigationKey.currentState.pushReplacementNamed(_rn);
}
Future<dynamic> navigateTo(String _rn){
return navigationKey.currentState.pushNamed(_rn);
}
Future<dynamic> navigateToRoute(MaterialPageRoute _rn){
return navigationKey.currentState.push(_rn);
}
goback(){
return navigationKey.currentState.pop();
}
}
In your main.dart file.
MaterialApp(
navigatorKey: NavigationService.instance.navigationKey,
initialRoute: "login",
routes: {
"login":(BuildContext context) =>Login(),
"register":(BuildContext context) =>Register(),
"home":(BuildContext context) => Home(),
},
);
Then you can call the function from anywhere in your project like...
NavigationService.instance.navigateToReplacement("home");
NavigationService.instance.navigateTo("home");
Solution 3:[3]
You can use flutter Get
package.
Solution 4:[4]
you can use this plugin to skip the required context
https://pub.dev/packages/one_context
// go to second page using named route
OneContext().pushNamed('/second');
// go to second page using MaterialPageRoute
OneContext().push(MaterialPageRoute(builder: (_) => SecondPage()));
// go back from second page
OneContext().pop();
Solution 5:[5]
Is there a way to use S.R Keshav method to access pages and giving them an argument ?
routes: {
"sce": (BuildContext context, {args}) => MatchConversation(args as int),
"passport": (BuildContext context, {dynamic args}) => Passport(),
},
It looks that the arg is lost when Navigator goes in _pushEntry method. The navigated Page is accessed, but no initial arguments are loaded.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Gazihan Alankus |
Solution 2 | S.R Keshav |
Solution 3 | BeHappy |
Solution 4 | ALEXANDER LOZANO |
Solution 5 | Arnaud Feige |