I need to pass an argument for my initialRoute. I found this issue and tried it like this:
initialRoute: AuthService.isLoggedIn() ? Views.home : Views.firstStart,
onGenerateInitialRoutes: (String initialRouteName) {
return [
AppRouter.generateRoute(
RouteSettings(
name: AuthService.isLoggedIn() ? Views.home : Views.firstStart,
arguments: notificationPayloadThatLaunchedApp,
),
),
];
},
onGenerateRoute: AppRouter.generateRoute,
This almost works. The problem I have is that somehow after calling this...
Navigator.pushReplacementNamed(
context,
Views.loading,
);
... my Multiprovider which is a parent of my GetMaterialApp is being called again which crashed my app because I call a function when initializing my providers:
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) {
var dataProvider = DataProvider();
dataProvider.init( // <- this is called again which should not happen
context,
);
return dataProvider;
},
),
],
child: GetMaterialApp(
title: 'Flutter Boilerplate',
navigatorKey: Get.key,
initialRoute: AuthService.isLoggedIn() ? Views.home : Views.firstStart,
onGenerateInitialRoutes: (String initialRouteName) {
return [
AppRouter.generateRoute(
RouteSettings(
name: AuthService.isLoggedIn() ? Views.home : Views.firstStart,
arguments: notificationPayloadThatLaunchedApp,
),
),
];
},
onGenerateRoute: AppRouter.generateRoute,
),
);
}
I feel like the way how I pass the initial argument is wrong. Is there any other/better way to get this done? Let me know if you need any more info!
I think you misunderstand the usage of new API onGenerateInitialRoutes cause by the name it should load without the calling of
Navigator.pushReplacementNamed(
context,
Views.loading,
);
API at all. if you call this route from another widget, it means this is already the second route already. it should be the default route for your application. so it makes no sense to give a parameter to an initial route at all.
since you have used Provider Package, it is better to just get whatever argument(parameter) that you want to send via Provider API itself.
if you want to give hardcoded data, then just treat it as a normal Widget class.
home: MyHomePage(name:"parameter name",data:DataHome()), :
GetMaterialApp(
title: 'Flutter Boilerplate',
navigatorKey: Get.key,
home: MyHomePage(name:"parameter name",data:DataHome()),
onGenerateRoute: AppRouter.generateRoute,
);
take note that if you calling Navigator.pushReplacementNamed API . your provider will be gone cause of the Flutter Widget Tree most Navigator API will create a new Widget tree level from the root. since the provider only provides for all its child only. so you cant use any provider data since you have a different ancestor.
so if you only have one page to go I suggest you use the home attribute in MaterialApp API cause it will treat your initial page as a child and Provider API can receive the data cause up in the Widget tree, it has an Ancestor Provider API:
MultiProvider(
providers: [
ChangeNotifierProvider(
if you want to move between pages via navigator after the main page.
consider using NestedNavigator so flutter will not create a new route from the root of the widget tree. so you still can access Provider API's data.
guessing from your variable name. I assume that you want to handle a notification
routing, check this deeplink
Related
Currently we're building an app to learn Flutter and Bloc pattern at my company. We use a MultiRepositoryProvider as the main widget and GoRouter for routing. My route looks like this:
GoRoute(
path: '/game/:id',
builder: (context, state) => GameDetailScreen(),
),
In the MultiRepositoryProvider the child is a MultiBlocProvider and the provider for this screen is:
BlocProvider(
create: (BuildContext context) {
return GameDetailBloc(context.read<FirestoreRepo>());
},
),
The BlocProvider's create function returns the BuildContext but it's not clear to me how I get the GoRoute state to pass the url param id to the GameDetailBloc.
We managed to get this to work by setting the game's id in GoRoute's build function when creating the GameDetailScreen. Then we removed that BlocProvider in the MultiBlocProvider and then accessed the bloc from the BuildContext when building the widget but it doesn't seem correct and we're trying to find the "correct solution" to this problem. Any help is greatly appreciated. Thanks!
go_router has it's params in its state.
Hence pass the state to the page
Router
GoRoute(
name: "test",
path: "/test/:id",
builder: (context, state) {
return SampleWidget(
goRouterState: state, 👈 Pass state here
);
},
),
Usage
context.goNamed("test", params: {"id": "123"}),
Accesing in the page
class SampleWidget extends StatelessWidget {
GoRouterState? goRouterState;
SampleWidget({super.key, this.goRouterState});
#override
Widget build(BuildContext context) {
print(goRouterState?.params.toString()); 👈 access anywhere like so
return const Scaffold(
body: ...
);
}
}
I couln't completely understand the question. I have attempted to answer based on my interpretation. Let me know the specifics if you have any other requrirements.
Uri.base.toString().replaceAll(Uri.base.origin, '')
I am doing an experiment, where I want to make two similar apps with single source code.
I am trying to make an "adaptive" State on top of the widget tree, and this state (ChangeNotifier) depends on the application (isApp1() determines which app is this ).
here's the code that I'm using:
ChangeNotifierProvider(
create: (context) {
return isApp1() ? AppState1() : AppState2();
},
child: MaterialApp(
onGenerateRoute: isAdminApp()
? App1Router.generateRoute
: App2Router.generateRoute,
initialRoute: "/",
),
);
when I try to read the state using Provider.of in the low levels of the tree I am facing the error:
Error: Could not find the correct Provider<AppState1> above this Widget
Note: when I put AppState1() or AppState2() directly instead of isApp1() ? AppState1() : AppState2() I don't face this error.
my goal is to provide value of user location with a Provider in the whole app. It's important to me that is't on top of the app as I want to use the value in the routes also.
However, in my app user first needs to login. Only afterwards he gets to the map. Here is the thing. According to bussiness requirements I can't call for permissions before the user gets to the map widget.
So the provider needs to be on top o the app but the future function has to be called once the user logs in.
How can I achieve that?
Here is a sample of my MyApp widget.
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<UserModel?>.value(
initialData: null,
value: AuthService().user,
),
FutureProvider<LatLng?>.value(
value: GeolocationService().getUserLocation(),
initialData: null,
)
],
child: MaterialApp(
title: '',
theme: themeData(),
onGenerateRoute: onGenerateRoute(),
builder: EasyLoading.init(),
home: AuthWrapper(),
),
);
}
}
SchedulerBinding.instances from scheduler. You can use it inside the initState of StatefulWidget
{
//inside initState method
SchedulerBinding.instances?.addPostframecallback((_){
//anything run within this function will be called just after the very first build method
// how it works?
// before the build method ran, initState will be called first synchronously
// after that, build method will be called
// then right after that build method finished the first render task,
// the post frame callback will be called, in this place we can use context
// since the UI has been built
});
}
I have a MainBloc that resides inside a main route, this route has a bottom app bar with multiple sub-routes, I want the same BLoC to run on all five sub-routes so that when one of them changes the state of the block the others will see the effect.
I tried this SO question but its really far from what I'm looking for, also I tried following what the error advised me to, but didn't work, here is the message that I got:
This can happen if:
1. The context you used comes from a widget above the BlocProvider.
2. You used MultiBlocProvider and didn't explicity provide the BlocProvider types.
Good: BlocProvider<MainBloc>(builder: (context) => MainBloc())
Bad: BlocProvider(builder: (context) => MainBloc()).
Main route:
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<MainBloc>(
builder: (BuildContext context) => MainBloc(),
),
BlocProvider<OtherBloc>(
builder: (BuildContext context) => OtherBloc(),
),
],
child: /..., //here I have the bottom app bar with 5 buttons to navigate between sub-routes
);
one of the sub-routes:
#override
Widget build(BuildContext context) {
final MainBloc bloc = BlocProvider.of<MainBloc>(context);
return /...; //here I have the context of this sub-route.
}
from what I've seen from tutorials and articles this code should work, but I can't seem to find why not.
The problem is you cannot access InheritedWidgets across routes unless you provide the InheritedWidget above MaterialApp. I would recommend wrapping your new route in BlocProvider.value to provide the existing bloc to the new route like:
Navigator.of(context).push(
MaterialPageRoute<MyPage>(
builder: (_) {
return BlocProvider.value(
value: BlocProvider.of<MyBloc>(context),
child: MyPage(),
);
},
),
);
You can find more detailed information about this in the bloc documentation
As this child has the bottom app bar:
child: /..., //here I have the bottom app bar
then I assume that the MultiBlocProvider(..) is not wrapping the whole part of app which is using this Bloc, my suggestion here is to wrap the "MaterialApp" with "MultiBlocProvider".
return MultiBlocProvider(
providers: [..],
child: MaterialApp(..) // Set MaterialApp as the child of the MultiBlocProvider
//..
)
I am not able to set localization in my app.
I am trying to add language settings and associated localization in my app. I am able to get-set the language option. I am using 'intl' plug-in for internationalization. My code looks like below in pretty much all the UI .dart files.
AppTranslations.of(context).accountNumber +
" ${accountDetails.accountNumber}",
The getters is set as :
String get accountNumber => _text("account_number");
String _text(String key) {
return _localisedValues[key] ?? _defaultLocaleValues[key];
}
I've also placed json files containing localized labels in 3 different languages. However, it seems there is some instantiation problem of the locazation plug-in. The code doesn't go the getter line.
Any help would be highly appreciated.
AppTranslations.of(context) is a standard way of accessing the localised labels. You are right about the instantiation. If the program doesn't go to the getter line them it means, there's a problem in somewhere in the initial part of the code. It could be in the main.dart.
Check where you are initialising LocalStorageProvider(). In case it is not initialised then that's the problem. Assuming you are using a MaterialApp, try the below suggestion then :
Wrap the MaterialApp with LocalStorageProvider(). I mean, in the main widget build, return LocalStorageProvider() and pass your existing code of MaterialApp() as a child to it. Sample below (Please ignore the theme etc since I just copied the code from one of my app) :
#override
Widget build(BuildContext context) {
LocalStorage localStorage = LocalStorage();
return LocalStorageProvider(
localStorage: localStorage,
child: LocaleProvider(
localStorage: localStorage,
localeWrapper: LocaleWrapper(),
child: Builder(
builder: (context) {
return AnimatedBuilder(
animation: LocaleProvider.of(context).localeWrapper,
builder: (context, _) {
return MaterialApp(
onGenerateTitle: (context) =>
AppTranslations.of(context).appName,
locale: LocaleProvider.of(context).locale,
title: "App Title",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MapsDemo(),
localizationsDelegates: [
AppTranslationsDelegate(
LocaleProvider.of(context).supportedLanguagesCodes,
),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: LocaleProvider.of(context).supportedLocales,
);
},
);
},
),
),
);
}