flutter: Navigation routes not working on authentication - flutter

I am new to flutter and trying to get this to work. I have a Welcome() page, which has two buttons (SignUp and SignIn), which take you to SignUp() and SignIn() pages. After successfully authenticating, the app should navigate to Home(), but it does not. Neither does it go back to Welcome() page if user logs out. Also, if the app is started and user was already logged in, it doesn't automatically go to Home(), it stays at Welcome(). What am I doing wrong here?
I am using a StreamProvider like so
if (snapshot.connectionState == ConnectionState.done) {
return StreamProvider<User>.value(
value: AuthService().user, child: Wrapper());
}
And my wrapper looks like this:
class Wrapper extends StatelessWidget {
const Wrapper({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user != null)
print(user.id);
else
print("User is null");
return MaterialApp(initialRoute: '/', routes: {
'/': (context) => user == null ? Welcome() : Home(),
'/signup': (context) => SignUp(),
'/signin': (context) => SignIn()
'/profile': (context) => Profile(),
'/edit': (context) => Edit()
});
}
This prints the user ID, which means the user is not null, but it does not navigate to Home().

I'm not dictating you what to do but i will just show you how i usually do.
The concept is simple. When the user successfull login or signup, you must save this information in a shared_preference. It may be like auth : true.
You must also wait on the app launching process to see if the user already login or not and based on that, you can navigate to another screen.
To check at startup if user is authenticated, you must read the auth property you previously added in shared_preference. Look at this code :
void main() async {
var mapp;
var routes = <String, WidgetBuilder>{
'/initialize': (BuildContext context) => Initialize(),
'/register': (BuildContext context) => Register(),
'/home': (BuildContext context) => Home(),
};
print("Initializing.");
WidgetsFlutterBinding.ensureInitialized();
await SharedPreferencesClass.restore("initialized").then((value) {
if (value) {
mapp = MaterialApp(
debugShowCheckedModeBanner: false,
title: 'AppName',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: routes,
home: Home(),
);
} else {
mapp = MaterialApp(
debugShowCheckedModeBanner: false,
title: 'AppName',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: routes,
home: Initialize(),
);
}
});
print("Done.");
runApp(mapp);
}
This code is so self explanatory. You can adapt it to your edge.
To navigate to a new screen it is simple, just use this code :
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);

Related

Can't land directly on subpage url on flutter web

This is my web site: https://patrickferreira.com.br
It has these routes:
/calculadoracelular
/calculadorapc
/robotrader
I will use the route /robotrader as an example but this happens with any route.
When I run the webapp on localhost I can access a subpage by typing on the browser: localhost:70721/robotrader which is what I expect. But when the site hosted on the web, it shows an error when I try to access the subpage in patrickferreira.com.br/robotrader.
Something weird is that if you access the home page in patrickferreira.com.br, and then access the subpages through there, the browser shows the correct url as patrickferreira.com.br/robotrader
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/calculadoracelular':
return MaterialPageRoute(builder: (_) => ProductPage(work: tradeSizeCalculatorForMobile));
case '/calculadorapc':
return MaterialPageRoute(builder: (_) => ProductPage(work: tradeSizeCalculatorForDesktop));
case '/robotrader':
return MaterialPageRoute(builder: (_) => ProductPage(work: tradingBot));
default:
return MaterialPageRoute(builder: (_) => const MyHomePage(title: 'Portfólio Patrick Ferreira'));
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Portfólio Patrick Ferreira',
debugShowCheckedModeBanner: false,
theme: ThemeData(
fontFamily: "Ubuntu",
primarySwatch: Colors.deepOrange,
),
onGenerateRoute: (settings) => generateRoute(settings),
initialRoute: '/#',
routes: {
'/': (context) => const MyHomePage(title: 'Portfólio Patrick Ferreira'),
'/calculadorapc': (context) => ProductPage(work: tradeSizeCalculatorForDesktop),
'/calculadoracelular': (context) => ProductPage(work: tradeSizeCalculatorForMobile),
'/robotrader': (context) => ProductPage(work: tradingBot),
},
);
}
}

flutter navigator links / routes doesn't work from external

I'm building a webapp with flutter and Navigator 2.0.
The in app navigation work fine. But as soon as I publish my app on a webserver and try to got to a url that points to a rout in my app the routes aren't loaded and the app starts at the homepage.
For example:
Link I click from outside: https://flutter-project.com/#/contacts/ID6Nx2wTxgFcNW3gvMb2
Location the app opens in the browser: https://flutter-project.com/#/
Is there a way to make this possible so that users can share links to other people and they can open them?
Currenty the only way to open links is to open the webapp and then paste the link in the browserwindow where the app already was started but that is not the behaviour I expect from a webapp. I want to click the link and be right where it points at.
I also have this problem.
This is my web site: patrickferreira.com.br
It has these routes:
/calculadoracelular
/calculadorapc
/robotrader
if you try patrickferreira.com.br and click in one of the items in the fourth section it navigates to the routes as expected
But if you try to land directly on a subpage it throws a broken link error.
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/calculadoracelular':
return MaterialPageRoute(builder: (_) => ProductPage(work: tradeSizeCalculatorForMobile));
case '/calculadorapc':
return MaterialPageRoute(builder: (_) => ProductPage(work: tradeSizeCalculatorForDesktop));
case '/robotrader':
return MaterialPageRoute(builder: (_) => ProductPage(work: tradingBot));
default:
return MaterialPageRoute(builder: (_) => const MyHomePage(title: 'Portfólio Patrick Ferreira'));
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Portfólio Patrick Ferreira',
debugShowCheckedModeBanner: false,
theme: ThemeData(
fontFamily: "Ubuntu",
primarySwatch: Colors.deepOrange,
),
onGenerateRoute: (settings) => generateRoute(settings),
initialRoute: '/#',
routes: {
'/': (context) => const MyHomePage(title: 'Portfólio Patrick Ferreira'),
'/calculadorapc': (context) => ProductPage(work: tradeSizeCalculatorForDesktop),
'/calculadoracelular': (context) => ProductPage(work: tradeSizeCalculatorForMobile),
'/robotrader': (context) => ProductPage(work: tradingBot),
},
);
}
}

How do I manage navigation routes table with Flutter connected to Firebase Firestore

I wonder how to manage screen navigation when your app is connected to Firebase.
When my app was offline I used a routes table, but Im not sure how to do now. Could I do as I show with my code below; use a streambuilder that switches between the AuthScreen when logged out and HomeScreen when logged in, and a routes table to switch with the following screens also when signed in.
I tried this approach but when im signing out from another screen than the HomeScreen the user stays signed in.
How can I set up my routes so that the user always signs out independent from which screen the user's currently on.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterApp',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (
ctx,
userSnapshot,
) {
if (userSnapshot.hasData) {
return HomeScreen();
}
return AuthScreen();
}),
routes: {
Screen1.routeName: (ctx) => Screen1(),
Screen2.routeName: (ctx) => Screen2(),
Screen3.routeName: (ctx) => Screen3(),
Screen4.routeName: (ctx) => Screen4(),
});
}
}
I don't think you're going about this the right way.
They are various ways of doing this. Here's mine.
Firstly, I think you should define an initial route that checks if the user is signed in.
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterApp',
theme: ThemeData(
primarySwatch: Colors.orange,
),
initialRoute:
FirebaseAuth.instance.currentUser == null ? "/login" : "/home",
routes: {
"/home": (ctx) => HomeScreen(),
"/login": (ctx) => AuthScreen(),
Screen1.routeName: (ctx) => Screen1(),
Screen2.routeName: (ctx) => Screen2(),
Screen3.routeName: (ctx) => Screen3(),
Screen4.routeName: (ctx) => Screen4(),
});
}
Note: "/home" & "/login" could be whatever you want as long as it's a string.
For logout, you need to replace the current screen with the login page, something like this should do.
Navigator.pushNamedAndRemoveUntil(context, '/login', (route) => false);

Can you have different conditional MaterialApp s returned?

Basically, I want to return an OTP (One Time Pin) input screen if the app opens and there isn't a valid user file, otherwise the app should open with a menu (Using Provider).
What I tried was:
Main calls runApp(const MyApp()); which is stateless.
MyApp's build returns MultiProvider with its child const AuthenticatedMaterialApp(), which is a stateful widget.
Here I tried various things , including setting different routes based on the provided values, but keep getting unexpected (for me) results; e.g. in _AuthenticatedMaterialAppState:
class _AuthenticatedMaterialAppState extends State<AuthenticatedMaterialApp> {
late ConnectionNotifier connectionNotifier;
late Authenticate auth;
late bool isUserOkay;
#override
void didChangeDependencies() {
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
connectionNotifier = context.read<ConnectionNotifier>();
auth = context.watch<Authenticate>();
isUserOkay = (auth.isActive && auth.isKnown);
developer.log('build isUserOkay:$isUserOkay isKnown: ${auth.isKnown}',
name: 'AuthenticatedMaterialApp');
String initRoute = (!isUserOkay) ? '/otp' : '/menu';
developer.log('initRoute $initRoute', name: 'AuthenticatedMaterialApp');
if (!isUserOkay) {
developer.log('isUserOkay: $isUserOkay return OTP app ', name: 'AuthenticatedMaterialApp');
return MaterialApp(
navigatorObservers: [routeObserver],
title: 'Named Routes',
theme: ThemeData(scaffoldBackgroundColor: Colors.white),
initialRoute: '/otp',
routes: {
'/otp': (context) => const OTPScreen(),
},
);
} else {
developer.log('isUserOkay: $isUserOkay return menu app ', name: 'AuthenticatedMaterialApp');
return MaterialApp(
navigatorObservers: [routeObserver],
title: 'Named Routes',
theme: ThemeData(scaffoldBackgroundColor: Colors.white),
initialRoute: '/menu',
routes: {
'/menu': (context) => const MenuScreen(),
'/stocks': (context) => const StocksRoute(),
'/stocks/stock': (context) => const StockPage(),
'/clients': (context) => const ClientsRoute(),
'/clients/client': (context) => const ClientPage(),
'/viewings': (context) => const ViewingsRoute(),
'/viewings/viewing': (context) => const ViewingPage(),
'/offers': (context) => const OffersRoute(),
'/offers/offer': (context) => const OfferPage(),
'/checkin': (context) => const CheckinScreen(),
'/calendar': (context) => const CalendarScreen(),
'/reports': (context) => const ReportsScreen(),
'/calculators': (context) => const CalculatorsScreen(),
'/test': (context) => const TestRoute(),
},
);
}
}
}
... eventually generates output that reads [AuthenticatedMaterialApp] isUserOkay: true return menu app , but what is shown on the screen is the OTP screen.
Why? - has it something to do with AuthenticatedMaterialApp being a const, or?
I also tried registering all the routes but having initialRoute: be a variable string but get basically the same result.
I can get it working by creating a separate 'home` route, and checking the user there and ~redirecting like:
class _HomeState extends State<Home> {
late Authenticate auth;
#override
Widget build(BuildContext context) {
auth = context.watch<Authenticate>();
developer.log('build', name: '_HomeScreenState');
if (auth.isKnown) {
if (auth.isActive == true) {
return const MenuScreen();
} else if (auth.isActive == false) {
return const OTPScreen();
}
}
return Container();
}
}
while I have all the routes including OTP, menu etc all registered in the AuthenticatedMaterialApp, which I suppose is fine, but I would like to understand why my initial approach doesn't/can't work?

How to stay on same state even after restarting app in flutter

I want to stay on the same page(such as otpPage)until and unless a condition is verified.
Condition networkutil.verify == "Y".
This is my main.dart
final routes = {
'/':(context)=> SignIn() ,
'/SignIn': (context)=> SignIn(),
'/Home': (context)=> Home(),
'/Register': (context) => Register(),
'/OtpPage': (context) => OtpPage()
};
class A extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: './',
routes: routes,
);
}
}
You can use BLoC Pattern, where you will put a condition :
if the user is registered, then Welcome page else, SignIn Page.
Hope you can get some idea from this repo:
https://github.com/samrat19/login_bloc