Flutter: Dynamic Initial Route - flutter

Dears,
I am using provider dart package which allows listeners to get notified on changes to models per se.
I am able to detect the change inside my main app root tree, and also able to change the string value of initial route however my screen is not updating. Kindly see below the code snippet and the comments lines:
void main() => runApp(_MyAppMain());
class _MyAppMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<UserProvider>.value(
value: UserProvider(),
),
ChangeNotifierProvider<PhoneProvider>.value(
value: PhoneProvider(),
)
],
child: Consumer<UserProvider>(
builder: (BuildContext context, userProvider, _) {
return FutureBuilder(
future: userProvider.getUser(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final User user = snapshot.data;
String initialScreen = LoginScreen.path;
// (1) I am able to get into the condition
if (user.hasActiveLogin()) {
initialScreen = HomeOneScreen.path;
}
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.green,
accentColor: Colors.blueGrey,
),
initialRoute: initialScreen,
// (2) here the screen is not changing...?
routes: {
'/': (context) => null,
LoginScreen.path: (context) => LoginScreen(),
RegisterScreen.path: (context) => RegisterScreen(),
HomeOneScreen.path: (context) => HomeOneScreen(),
HomeTwoScreen.path: (context) => HomeTwoScreen(),
RegisterPhoneScreen.path: (context) => RegisterPhoneScreen(),
VerifyPhoneScreen.path: (context) => VerifyPhoneScreen(),
},
);
},
);
},
),
);
}
}
Kindly Note the Below:
These are are paths static const strings
LoginScreen.path = "login"
RegisterScreen.path = "/register-screen"
HomeOneScreen.path = "home-one-screen"
HomeTwoScreen.path = "home-two-screen"
RegisterPhoneScreen.path = "/register-phone-screen"
VerifyPhoneScreen.path = "/verify-phone-screen"
What I am missing for dynamic initialRoute to work?
Many Thanks

According to this issue described on github issues it is not permissible to have initial route changes. At least this is what I understood. However what I did is that I replaced the initialRoute attribute with home attr. Thus this change mandates that initialScreen becomes a widget var.
The changes is shown below:
void main() => runApp(_MyAppMain());
class _MyAppMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<UserProvider>.value(
value: UserProvider(),
),
ChangeNotifierProvider<PhoneProvider>.value(
value: PhoneProvider(),
)
],
child: Consumer<UserProvider>(
builder: (BuildContext context, userProvider, _) {
return FutureBuilder(
future: userProvider.getUser(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final User user = snapshot.data;
// (1) This becomes a widget
Widget initialScreen = LoginScreen();
if (user.hasActiveLogin()) {
initialScreen = HomeOneScreen();
}
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.green,
accentColor: Colors.blueGrey,
),
home: initialScreen,
// (2) here the initial route becomes home attr.
routes: {
'/': (context) => null,
LoginScreen.path: (context) => LoginScreen(),
RegisterScreen.path: (context) => RegisterScreen(),
HomeOneScreen.path: (context) => HomeOneScreen(),
HomeTwoScreen.path: (context) => HomeTwoScreen(),
RegisterPhoneScreen.path: (context) => RegisterPhoneScreen(),
VerifyPhoneScreen.path: (context) => VerifyPhoneScreen(),
},
);
},
);
},
),
);
}
}
Also note on my RegistrationScreen on success api response I did Navigator.of(context).pop()
Thanks

Related

pushAndRemoveUntil is not working with onGenerateRoute in flutter

pushAndRemoveUntil is not proper working with "onGenerateRoute" in MaterialApp but if I have used pushAndRemoveUntil the "routes" in MaterialApp So it's working fine.
current screen name is FiveScreen. and here is the one button and below code in for onclick.
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context)=>ThirdScreen()), ModalRoute.withName(MUtils.second));
if I have use the "routes" in MaterialApp So it's navigate to "SecondScreen" after onbackpress to navigate to "FirstScreen" but If I have Use the the "onGenerateRoute" in MaterialApp so it's redirect to "secondScreen" but after click on the backpress so App is closed.
Thank you in advance.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
/*routes: {
MUtils.home: (context) => HomeScreen(),
MUtils.second: (context) => SecondScreen(),
MUtils.thirdScreen: (context) => ThirdScreen(),
MUtils.fourth: (context) => FourthScreen(),
MUtils.settings: (context) => SettingsScreen(),
},*/
onGenerateRoute:MyRoutes().onGenerateRoute,
//home: const MyHomePage(title: 'Flutter Demo Home Page'),
);}
Below is onGenerate Route class
class MyRoutes{
Route? onGenerateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => MyHomePage(
title: "Home Screen",
),
);
case MUtils.home:
return MaterialPageRoute(
builder: (_) => HomeScreen(content: settings.arguments,),
);
case MUtils.second:
return MaterialPageRoute(
builder: (_) => SecondScreen(
),
);case MUtils.thirdScreen:
return MaterialPageRoute(
builder: (_) => ThirdScreen(
),
);
case MUtils.fourth:
return MaterialPageRoute(
builder: (_) => FourthScreen(
),
);
case MUtils.settings:
return MaterialPageRoute(
builder: (_) => SettingsScreen(),
);
default:
return null;
}
}
}
I have draw design to better understand.
Route:- HomeScreen=> 1_Screen=> 2_Screen=> 3_Screen=> 4_Screen=> 5_Screen (Now Navigate to) 2_Screen (onBackPress)=> 1_Screen (onBackPress)=> HomeScreen.
onGenerateRoute:- HomeScreen=> 1_Screen=> 2_Screen=> 3_Screen=> 4_Screen=> 5_Screen (Now Navigate to) 2_Screen (onBackPress)=> App Closed.
So here issue the issue in "onGenerateRoute" when backpress in 2_screen to app is closed but it's working in "Routes".
Mycode
onGenerateRoute: (setting){
Widget myroute = Container(
);
if(setting.name == '/'){
myroute = MyHomePage(
title: "Home Screen",
);
}else if(setting.name == MUtils.home){
myroute = HomeScreen();
}else if(setting.name == MUtils.second){
myroute = SecondScreen();
}else if(setting.name == MUtils.thirdScreen){
myroute = ThirdScreen();
}else if(setting.name == MUtils.fourth){
myroute = FourthScreen();
}else if(setting.name == MUtils.settings){
myroute = SettingsScreen();
}
return MaterialPageRoute(
builder: (_) => myroute,
);
},
Your Code:-
onGenerateRoute: (settings) {
final map = {'/': 1, '1': 2, '2': 3, '3': 4, '4': 5,'5':6};
return MaterialPageRoute(
builder: (ctx) => FooPage(map[settings.name]!),
settings: settings, // <<< this is the difference
);
},
Only the deference is here but I couldn't find the issue in my code.

Providing bloc to a new page using named route

I am trying to provide a local bloc to a new page, I found some way to do this by using an anonymous route but it doesn't look elegant
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return BlocProvider.value(
value: context.bloc<MyBloc>(),
child: NewPage());
}),
);
What I want is to do the same thing but using a named route and without creating a global bloc as I simply can't
Create app_router.dart file.
app_router.dart
class AppRouter {
final LocalBloc _localBloc = LocalBloc();
Route onGenerateRoute(RouteSettings routeSettings) {
switch (routeSettings.name) {
case '/':
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _localBloc,
child: Home(),
),
);
case '/page1':
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _localBloc,
child: Page1(),
),
);
case '/page2':
return MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: _localBloc,
child: Pag2(),
),
);
}
}
}
Then in your main.dart file
Add onGenerateRoute
final AppRouter _appRouter = AppRouter();
MaterialApp(
title: 'My App',
onGenerateRoute: _appRouter.onGenerateRoute,
),),
In your navigation, you can do this:
Navigator.of(context).pushNamed('/page1');

Provider is not working when navigate to new screen

I implemented Authentication by provider
The problem is when is the first time myHomeCalss is notified that the user is Authenticated by dont return the correctPage (MainGui)
SplashPages is page with a button continue, and push the login page ,
The Login page is pushed outside of costumer
but when I dont pass in the SplashPages is worked perfectyl
any adea please
//splash page
ContinueButton(
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
ListenableProvider.value(
value: yourModel,
child: LoginPage(),
),
),
);
}
)
//main
void main() async {
setupLocator();
WidgetsFlutterBinding.ensureInitialized();
await firebase_core.Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthenticationService()),
],
child: MyApp(),
),
);
}
//My app
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHome(),
builder: (context, child) => Navigator(
key: locator<DialogService>().dialogNavigationKey,
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => DialogManager(child: child)),
));
}
}
MyHome
Class MyHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: FutureBuilder<bool>(
future: startTime(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot2) {
if (snapshot2.hasData) {
if (snapshot2.data) {
return SplashPages();
} else {
return Consumer<AuthenticationService>(builder: (_, auth, __) {
if (auth.currentUserr == null) {
return LoginPage();
} else {
return FutureBuilder(
future: auth.populateCurrentUser(auth.currentUserr),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (auth.currentUserr.emailVerified) {
return MainGui();
} else {
return ValidationMailPage(
email: auth.currentUserr.email,
);
}
} else
return Container(
// child: Center(
// child: SpinKitRotatingCircle(
// color: Colors.white,
// size: 50.0,
// ))
);
});
}
});
}
}
You may consider using SharedPreferences, in which you will store the user (or maybe just the token), and then check in main if there is a token/user stored there before rendering the app; if there is a token you log in and then push to the homepage, if not you navigate directly to the login page.
SharedPrefenreces is persisted data storage that persists even if you restart the app, but Provider is a state management solution that doesn't persist between app restarts.
Here is the SharedPreferences plugin you may use.

MultiRepositoryProvider doesn't instantiate Bloc

I recently started developing an app in Flutter, so I'm fairly new to the area. So I've been looking into using Blocs. However when I Instantiate my Bloc and my services everything works fine. That is, until I use MultiRepositoryProvider. I have 2 code snippets. The first one:
return RepositoryProvider<AuthenticationService>(
create: (context) {
return FakeAuthenticationService();
},
// Injects the Authentication BLoC
child: BlocProvider<AuthenticationBloc>(
create: (context) {
final authService = RepositoryProvider.of<AuthenticationService>(context);
return AuthenticationBloc(authService)..add(AppLoaded());
},
child: MaterialApp(
title: 'Authentication Demo',
theme: appTheme(),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
// show home page
return HomePage(
user: state.user,
);
}
// otherwise show login page
return StartupPage();
},
),
)
),
);
This code works fine, but the second snippet which is exactly the same, except it utilized MultiRepositoryProvider doesn't work. Second code:
return MultiRepositoryProvider(
providers: [
RepositoryProvider<AuthenticationService>(
create: (context) => FakeAuthenticationService(),
child: BlocProvider<AuthenticationBloc>(
create: (context) {
final authService = RepositoryProvider.of<AuthenticationService>(context);
return AuthenticationBloc(authService)..add(AppLoaded());
},
),
)
],
child: MaterialApp(
title: 'Authentication Demo',
theme: appTheme(),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
// show home page
return HomePage(
user: state.user,
);
}
// otherwise show login page
return StartupPage();
},
),
),
);
Now this second code gives me the error BlocProvider.of() called with a context that does not contain a Cubit of type AuthenticationBloc.
Does anyone know why this second code doesn't work?
I'm working on the same thing and I got an error but now resolved
return MultiRepositoryProvider(
providers: [
RepositoryProvider<TranslationRepository>(
create: (context) => TranslationRepository(),
),
RepositoryProvider<WeatherRepository>(
create: (context) => WeatherRepository(),
),
],
child: MultiBlocProvider(
providers: [
BlocProvider<WeatherBloc>(
create: (context) =>
WeatherBloc(context.read<WeatherRepository>()),
),
BlocProvider<ConnectivityBloc>(
create: (context) => ConnectivityBloc(),
),
BlocProvider<TranslationBloc>(
create: (context) =>
TranslationBloc(context.read<TranslationRepository>()),
),
],
child: MaterialApp(
title: 'Material App',
onGenerateRoute: router.generateRoute,
initialRoute: '/',
)));
First, in my create function I overrided the context with "_" but I got the same error.
Now with this snippet it works perfectly, just put the same context name as my providers before

Multiple ScopedModels in flutter

I followed a tutorial from a flutter app with login system using a scope_model. Then, I added a new scope_model called Group to use in a new "route" called opportunities.
But in my new route I can't call the scope_model Group and I allways see the same error:
Error: Could not find the correct ScopedModel.
I think that my mistake is in main.dart. I don't know how to "invoque" my new scope_model.
Here is my code.
file opportuinity.dart
import 'package:scoped_model/scoped_model.dart';
import 'package:business_maker/data/models/group_api.dart';
(...)
#override
Widget build(BuildContext context) {
final _group = ScopedModel.of<GroupModel>(context, rebuildOnChange: true);
file main.dart
#override
Widget build(BuildContext context) {
return ScopedModel<ThemeModel>(
model: _model,
child: new ScopedModelDescendant<ThemeModel>(
builder: (context, child, theme) => ScopedModel<AuthModel>(
model: _auth,
child: MaterialApp(
theme: theme.theme,
home: new ScopedModelDescendant<AuthModel>(
builder: (context, child, model) {
if (model?.user != null) return Home();
return LoginPage();
}),
routes: <String, WidgetBuilder>{
"/login": (BuildContext context) => LoginPage(),
"/menu": (BuildContext context) => Home(),
"/home": (BuildContext context) => Home(),
"/settings": (BuildContext context) => SettingsPage(),
"/opportunities": (BuildContext context) => OpportunityPage()
},
),
),
));
}
thank you
If you want to use models in different routes then you need to place the model above the Navigator which is usually created in a WidgetsApp/MaterialApp/CupertinoApp
In your code I do not see a ScopedModel<Group> that's placed above the navigator. Or anywhere, actually. You need to add the group model above the navigator (something the materialapp creates for you).
Widget build(BuildContext context) {
return ScopedModel<ThemeModel>(
model: _model,
child: ScopedModel<Group>(
model: _yourGroupModel,
child: new ScopedModelDescendant<ThemeModel>(
builder: (context, child, theme) => ScopedModel<AuthModel>(
model: _auth,
child: MaterialApp(
theme: theme.theme,
home: new ScopedModelDescendant<AuthModel>(
builder: (context, child, model) {
if (model?.user != null) return Home();
return LoginPage();
}),
routes: <String, WidgetBuilder>{
"/login": (BuildContext context) => LoginPage(),
"/menu": (BuildContext context) => Home(),
"/home": (BuildContext context) => Home(),
"/settings": (BuildContext context) => SettingsPage(),
"/opportunities": (BuildContext context) => OpportunityPage()
},
),
),
)
)
);
}