I want to use my CustomPageRoute in Navigator.pushNamed() but I can't find a way to use it.
However, I can use it in following way
Navigator.push(context, CustomPageRoute(builder: (context) => MyPage()));
This is my route:
"/page": (context) => MyPage(), // Can I use CustomPageRoute here, if yes then how?
Recently I ran into the same problem "fixed" it with the onGeneratePageRoute hook.
I.e. create a method to dynamically generate the route:
static Route<dynamic> _onGenerateRoute(RouteSettings settings) {
switch (settings.name) {
case '/onboarding':
return MyCustomPageRoute(builder: (context) => OnboardingScreen(), settings: settings);
}
throw UnsupportedError('Unknown route: ${settings.name}');
}
and set it to onGenerateRoute in your MaterialApp
onGenerateRoute: _onGenerateRoute,
Looks like you probably already figured this out based on a previous comments but figured I'd make it easier for future people like me who stumble upon this post!
note: make sure the route name (in this case /onboarding) is not also defined and set to routes on your MaterialApp as it will only trigger your onGenerateRoute if it can't be resolved from your normal routes.
You can use this package
page_transition
and use onGenerateRoute in MaterialApp and then
onGenerateRoute: (settings) {
switch (settings.name) {
case '/second':
return PageTransition(
child: SecondPage(),
type: PageTransitionType.scale,
settings: settings,
);
break;
default:
return null;
}
},
make sure to not include the routes whenyou have delcared it in route: because it will bypass the onGenerateRoute animation that you want.
For usage, just use like usual Navigator.pushNamed(context, '/second');
Related
I'm relatively new to Dart/Flutter,
Just struggling to understand some code/syntax and wondered if someone can help explain.
Im looking at the example of setting up multiple providers and I cant get my head round the code for setting up the update..
providers: [
// In this sample app, CatalogModel never changes, so a simple Provider
// is sufficient.
Provider(create: (context) => CatalogModel()),
// CartModel is implemented as a ChangeNotifier, which calls for the use
// of ChangeNotifierProvider. Moreover, CartModel depends
// on CatalogModel, so a ProxyProvider is needed.
ChangeNotifierProxyProvider<CatalogModel, CartModel>(
create: (context) => CartModel(),
update: (context, catalog, cart) {
cart.catalog = catalog;
return cart;
},
),
],
Specifically...
update: (context, catalog, cart) {
cart.catalog = catalog;
return cart;
}
I thought it was a function that takes in 3 parameters context, catelog, cart
But I dont see anywhere where they are first instantiated
Can anyone explain what is going on here?
Thanks
update: denotes a parameter to the ChangeNotifierProxyProvider<CatalogModel, CartModel> constructor, passing it an anonymous function that takes three parameters. The code in (or near) the ChangeNotifierProxyProvider will be invoking this function as necessary.
Is there an analyzer option or linter rule in Dart that requires the return in anonymous functions when they have a return type specified? For example, in the MaterialApp widget constructor there is a parameter called onGenerateRoute. This parameter requires a function or anonymous function that takes in a RouteSettings object and returns a Route object.
So it should look something like this when it is made:
onGenerateRoute: (RouteSettings settings) {
return Route();
},
However, I can leave off the return statement without the IDE notifying me that I should be returning a Route object:
onGenerateRoute: (RouteSettings settings) {
//no return statement
},
Is there an analyzer option or linter rule that will make the IDE notify me?
I'm using a BLoC to keep state between two nested FullScreenDialogs.
I'm initializing the bloc when I push the first screen, like so
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => ProductBlocProvider(child: ProductEntryScreen()),
fullscreenDialog: true
));
},
);
ProductEntryScreen has a bunch of TextFields and a button than opens a new FullScreenDialog. This new Screen also has TextFields.
The problem I'm having is that every time I write on a TextField on the second FullScreenDialog, the onPressed function where I start the ProductBlocProvider runs again.
And that re-run is causing the Bloc to create a new instance, so I end up loosing the state.
What I want to do?
Maybe I'm doing it wrong so I'll explain what I'm trying to achieve.
I want to keep state between the two FullScreenDialogs while I fill all the fields, and when I'm done I want to press a button that send all of the data (both screens) to a database.
The problem is that I was creating the instance of the bloc inside the provider in the builder function of the MaterialPageRoute.
That builder function was being called repeatedly, and creating a new instance of the bloc every time. The solution was to take out from the builde function the creation of the bloc instance, like this:
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
//Here I create the instance
var _bloc = ProductBloc();
Navigator.of(context).push(MaterialPageRoute(
//And I pass the bloc instance to the provider
builder: (BuildContext context) => ProductBlocProvider(bloc: _bloc, child: ProductEntryScreen()),
fullscreenDialog: true
));
},
);
The package get_it may be of help to you. get_it is a service locator library, and uses a Map to store the registered objects; therefore, it provides access at a complexity of O(1), which means it's incredibly fast. The package comes with a singleton GetIt which you can use like so,
// Create a global variable (traditionally called sl or locator)
final sl = GetIt.instance; // There is also a shorthand GetIt.i
// ...
// Then, maybe in a global function called initDi(),
// you could register your dependencies.
sl.registerLazySingleton(() => ProductBloc());
registerLazySingleton() or registerSingleton() will always
return the same instance; lazily (i.e., when first called)
or at app start-up respectively.
If you want to create a new instance every time, use registerFactory() instead (I put this here even though it's not exactly what you want).
For example,
sl.registerFactory(() => ValidatorCubit());
And it could be accessed like this,
MultiBlocProvider(
providers: [
// The type is inferred here
BlocProvider<AuthenticationBloc>(create: (_) => sl()),
// The type is explicitly given here
BlocProvider(create: (_) => sl<ProductsBloc>()),
],
child: ProductsScreen(),
),
This example primarily shows you how it can be done with the flutter_bloc library, but get_it works anywhere, even in non-flutter dart projects.
If you need more functionality, do make sure to read the docs for this package. It is well documented, and contains (almost) every feature you might need, including scoping.
Also, this approach allows you to use the interface pattern, making the code much more maintainable and testable, as you will have to change just one place to use a different implementation.
Im looking through flutter docs and examples and im stuck in the routing.
Here is documentation https://docs.flutter.io/flutter/widgets/Navigator-class.html
It tells I can do a button with function to navigate with Navigator
onPressed: (context) {
Navigator.pop(context);
}
but my ide (vs code) and debugger doesn't think it is possible. It shows me error when im trying to do it.
[dart] The argument type '(dynamic) → Null' can't be assigned to the parameter type '() → void'.
Build also fails with exceptions
setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framewo…
not quite sure what it means...
Any suggestions?
You don't need to pass context to the onPressed callback, context is available inside the callback anyway if the callback is created in a scope where context is available:
build(BuildContext context) {
...
onPressed: () {
Navigator.pop(context);
}
}
Do not pass the context to onPressed as it is a void callback, so it does not return anything. Use it only to execute logic.
Is there a way to listen to route changes of the Navigator in Flutter? Basically, I'd like to be notified when a route is pushed or popped, and have the current route on display on screen be returned from the notification
Building on navigator observers, you can also use RouteObserver and RouteAware.
Navigator has observers. You can implement NavigatorObserver and receive notifications with details.
I was also struggling with that and for my purpose the RouteObservers felt overkill and where too much for my needs. The way I handled it lately was to use the onGenerateRoute property inside my MaterialApp. So this solution is more applicable if you are using onGenerateRoute with your app (might especially be useful if you are navigating with arguments). You can read more about it here.
My MaterialApp looks like the following:
runApp(MaterialApp(
title: 'bla',
home: BuilderPage(LoginArguments(false)),
onGenerateRoute: generateRoute
));
The generateRoute method looks like the following:
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case 'main':
print('Navigated to main')
return MaterialPageRoute(builder: (_) => MainScreen());
case 'register':
print('Navigated to register')
return MaterialPageRoute(builder: (_) => RegisterScreen());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}')),
));
}
}
So everytime I now do for example:
Navigator.pushNamed(
context,
'register',
);
It will print Navigated to register. I think this way is especially helpful if you don't necessarily need to know whether you pushed or popped. With some additional implementation it would also be possible to observe that via injected arguments.