Navigator.pushedName does not work. flutter - flutter

I'm currently developing an app on VScode. In my Sign up page I'm using createUserWithEmailAndPassword (firebase authentication), once the sign up button is clicked the email shows in Firebase Authentication, but it does not navigate to the next page where the user can enter their profile information. and if I hot restart or hot reload the project it the app freezes and I have to terminate it for it to work again.
SignupButton(
text: 'Sign Up',
color: kColor,
onTap: () async {
if (_formKey.currentState.validate()) {
try {
final user = (await _auth
.createUserWithEmailAndPassword(
email: _emailController.text,
password:
_passwordController.text))
;
if (user!=null){Navigator.pushNamed(
context, DoctorAccountSetup.id);}
} catch (e) {
print('Error Happened!!!: $e');
}
}
}),
main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: Registration.id,
routes: {
// id = Static String id = 'InitialPage'; for each dart file
InitialPage.id: (context) => InitialPage(),
Login.id: (context) => Login(),
Infopage.id: (context) => Infopage(),
Registration.id: (context) => Registration(),
DoctorAccountSetup.id: (context) => DoctorAccountSetup(),
},
);
}
}
How can I fix this?
Edit
SignupButton(
text: 'Sign Up',
color: kColor,
onTap: () {
Navigator.pushNamed(context, "/doctorAccountSetup");
// if (_formKey.currentState.validate()) {}
// when the if statement is commented the navigation works fine.
// Might the problem be in the try and catch?
}),

since DoctorAccountSetup.Id is the name of your page, try changing it to a string in your main.dart to test things, i.e:
make the route name look like this:
'/doctorAccountSetup': (context) => DoctorAccountSetup(),
and use this for naviagtion.
if (user!=null){
Navigator.pushNamed(context, "/doctorAccountSetup");
}
What happens?

Related

How can I access the fields in firebase firestore?

I am working on ride sharing app. I just want that whenever I close the application after the login then next time I will redirect to the home screen.
I have two options login as driver or pessenger ,but whenever I loggedin as pessenger and closed the application, it will redirect me to the Driver home screen. I just want to access status field so that I can make a conditional difference between driver and pessenger
I am pasting the code below, kindly check it and help me if you can!!
class SplashServices {
void isLogin(BuildContext context) {
final auth = FirebaseAuth.instance;
final user = auth.currentUser;
if (user != null) {
Timer(
const Duration(seconds: 3),
() => Navigator.push(context,
MaterialPageRoute(builder: (context) => const DriverPost())));
} else {
Timer(
const Duration(seconds: 3),
() => Navigator.push(context,
MaterialPageRoute(builder: (context) => const GettingStarted())));
}
}
}
I'm not sure I entirely understand the question, but with the way that your doc is set up with the status field, which I'm assuming will have two options, driver or passenger, you could access the value through a function like this or similar to it:
Future<void> checkUserStatus() {
final driver = FirebaseAuth.instance.currentUser;
final driverDoc = await FirebaseFirestore.instance.collection('driver').doc(driver!.uid).get();
if (userDoc.data()!['status'] == 'driver'){
Navigator.push(context,
MaterialPageRoute(builder: (context) => const DriverPost()))
} else{
Navigator.push(context,
MaterialPageRoute(builder: (context) => const GettingStarted()))
}
}
Let me know if that helps.

Go back to login when logged out from the drawer, no matter what

I need to redirect user to login page when he clicks on logout button from drawer (wherever he is). The problem is that when I click on the logout button, the screen remains the same.
According to this post: Flutter provider state management, logout concept
I have:
void main() async {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<Profile>(
create: (final BuildContext context) {
return Profile();
},
)
],
child: MyApp(),
),
);
}
MyApp:
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
initPlatformState();
}
/// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
if (!mounted) return;
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
initialRoute: '/',
navigatorKey: navigatorKey,
// ...
home: Consumer<Profile>(
builder: (context, profile, child){
return profile.isAuthenticated ? SplashScreen() : AuthScreen();
}
)
);
}
}
The part of the drawer where there is the logout button:
ListTile(
leading: Icon(Icons.logout),
title: Text(AppLocalizations.of(context)!.logout),
onTap: () async {
SharedPreferences preferences =
await SharedPreferences.getInstance();
await preferences.clear();
final Profile profile =
Provider.of<Profile>(context, listen: false);
profile.isAuthenticated = false;
}),
As I said, when I click on the logout button from the drawer, the user is correctly logged out, but the screen remains the same.
UPDATE
This is the profile class:
class Profile with ChangeNotifier {
bool _isAuthenticated = false;
bool get isAuthenticated {
return this._isAuthenticated;
}
set isAuthenticated(bool newVal) {
this._isAuthenticated = newVal;
this.notifyListeners();
}
}
I think you are using provider class incorrectly.
use your profile class like this.
class Profile with ChangeNotifier {
bool _isAuthenticated = true;
bool get getIsAuthenticated => _isAuthenticated;
set setIsAuthenticated(bool isAuthenticated) {
_isAuthenticated = isAuthenticated;
notifyListeners();//you must call this method to inform lisners
}
}
in set method call notifyListners();
in your listTile
replace profile.isAuthenticated = false to profile.isAuthenticated = false;
Always use getters and setters for best practice.
I hope this is what you were looking for.
Add Navigator.of(context).pushReplacementNamed("/routeName") in LogOut onTap() Section.
For more information : https://api.flutter.dev/flutter/widgets/Navigator/pushReplacementNamed.html
Make sure to have logout route set in MyApp file, and i'd edit logout button file as such:
ListTile(
leading: Icon(Icons.logout),
title: Text(AppLocalizations.of(context)!.logout),
onTap: () async {
SharedPreferences preferences =
await SharedPreferences.getInstance();
await preferences.clear();
final Profile profile =
Provider.of<Profile>(context, listen: false);
profile.isAuthenticated = false;
// add login file route here using Navigator.pushReplacementNamed() ;
}),
Navigator push named -> logout route?

App widget is not generating the route | firebase signup

Upon successful signup, I am trying to send users to the homepage (home) explaining how to use the app. I am doing so through this code block on my signup.dart
onPressed: () async {
try {
User user =
(await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
))
.user;
if (user != null) {
user.updateProfile(displayName: _nameController.text);
Navigator.of(context).pushNamed(AppRoutes.home);
}
}
Which is pointing to the home route
class AppRoutes {
AppRoutes._();
static const String authLogin = '/auth-login';
static const String authSignUp = '/auth-signup';
static const String home = '/home';
static Map<String, WidgetBuilder> define() {
return {
authLogin: (context) => Login(),
authSignUp: (context) => SignUp(),
home: (context) => Home(),
};
}
}
However, when I sign up, the data is rendering in firebase, but the user is not being sent to the home page, and throws this error in my console
Make sure your root app widget has provided a way to generate
this route.
Generators for routes are searched for in the following order:
1. For the "/" route, the "home" property, if non-null, is used.
2. Otherwise, the "routes" table is used, if it has an entry for the route.
3. Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "home" and "routes".
4. Finally if all else fails onUnknownRoute is called.
Unfortunately, onUnknownRoute was not set.
Any thoughts on how to rectify?
Have you added onGenerateRoute in your MaterialApp? Like this:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: Router.generateRoute,
initialRoute: yourRoute,
child: YouApp(),
);
}
}
class Router {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case AppRoutes.home:
return MaterialPageRoute(builder: (_) => Home());
case AppRoutes.authLogin:
return MaterialPageRoute(builder: (_) => Login());
case AppRoutes.authSignUp:
return MaterialPageRoute(builder: (_) => SignUp());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}')),
));
}
}
}
}
}

Flutter Provider rebuilt widget before parent's Consumer

I have got a problem with the provider package.
I want to be able to clean an attribute (_user = null) of a provider ChangeNotifier class (it is a logout feature).
The problem is when I am doing that from a Widget that use info from this Provider.
My main app is like :
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => AuthProvider(),
builder: (context, _) => App(),
),
);
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<AuthProvider>(builder: (_, auth, __) {
Widget displayedWidget;
switch (auth.loginState) {
case ApplicationLoginState.initializing:
displayedWidget = LoadingAppScreen();
break;
case ApplicationLoginState.loggedIn:
displayedWidget = HomeScreen();
break;
case ApplicationLoginState.loggedOut:
default:
displayedWidget = AuthenticationScreen(
signInWithEmailAndPassword: auth.signInWithEmailAndPassword,
registerAccount: auth.registerAccount,
);
}
return MaterialApp(
title: 'My App',
home: displayedWidget,
routes: {
ProfileScreen.routeName: (_) => ProfileScreen(),
},
);
});
}
}
My Provider class (simplified) :
class AuthProvider extends ChangeNotifier {
ApplicationLoginState _loginState;
ApplicationLoginState get loginState => _loginState;
bool get loggedIn => _loginState == ApplicationLoginState.loggedIn;
User _user;
User get user => _user;
void signOut() async {
// Cleaning the user which lead to the error later
_user = null;
_loginState = ApplicationLoginState.loggedOut;
notifyListeners();
}
}
My Profile screen which is accessible via named Route
class ProfileScreen extends StatelessWidget {
static const routeName = '/profile';
#override
Widget build(BuildContext context) {
final User user = Provider.of<AuthProvider>(context).user;
return Scaffold(
// drawer: AppDrawer(),
appBar: AppBar(
title: Text('Profile'),
),
body: Column(
children: [
Text(user.displayName),
FlatButton(
child: Text('logout'),
onPressed: () {
// Navigator.pushAndRemoveUntil(
// context,
// MaterialPageRoute(builder: (BuildContext context) => App()),
// ModalRoute.withName('/'),
// );
Provider.of<AuthProvider>(context, listen: false).signOut();
},
)
],
),
);
}
}
When I click the logout button from the profile screen, I don't understand why i get the error :
As I am using a Consumer<AuthProvider> at the top level of my app (this one includes my route (ProfileScreen), I thought it would redirect to the AuthenticationScreen due to the displayedWidget computed from the switch.
But it seems to rebuild the ProfileScreen first leading to the error. the change of displayedWidget do not seems to have any effect.
I'm pretty new to Provider. I don't understand what I am missing in the Provider pattern here ? Is my App / Consumer wrongly used ?
I hope you can help me understand what I've done wrong here ! Thank you.
Note : the commented Navigator.pushAndRemoveUntil redirect correctly to the login screen but I can see the error screen within a few milliseconds.
Your user is null, and you tried to get the name of him. You need to check it before using it. It will look like this:
user == null ?
Text("User Not Found!"),
Text(user.displayName),
From the provider API reference of Provider.of :
Obtains the nearest Provider up its widget tree and returns its
value.
If listen is true, later value changes will trigger a new State.build
to widgets, and State.didChangeDependencies for StatefulWidget.
So I think the line final User user = Provider.of<AuthProvider>(context).user; in your profile screen calls a rebuild when the _user variable is modified, and then the _user can be null in your ProfileScreen.
Have you tried to Navigator.pop the profile screen before clearing the _user variable?

Flutter exception: 'Context is not a subtype of BuildContext' error using Navigator

I am working on a flutter app and I am running into the following error: "The argument type 'Context' can't be assigned to the parameter type 'BuildContext'." However, when I try to pass in context to my functions as it says to do on the internet, I am unable to get it to work. Can someone please help me with this? The app is a camera app, and I know the video is successfully being recorded. here is the code:
This is the stop button that is pressed.
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: () {controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? onStopButtonPressed. //this is the function that is being called
: null;},
) //icons.stop
This is where the app isn't working with my Navigator.push call. It works fine when I take it out.
void onStopButtonPressed() {
stopVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recorded to: $videoPath');
print('Navigator is hit');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
});
}
And here is my stopVideoRecording function
Future<void> stopVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}
try {
await controller.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
//await _startVideoPlayer();
}
Thanks!
change it to
Navigator.push(
this.context, //add this so it uses the context of the class
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
Maybe you're importing a library or something that has a class named context and is interfering with the name context