How to implement both device_preview and auto_route packages in Flutter - flutter

I would like to use both device_preview and auto_route. To do so, I need to add the respective codes to the builder property of MaterialApp. However, since there is only one builder property in MaterialApp widget, I can only use one of the packages. What can I do to use both?

The builder property in Material App will give you context and a Native navigator in case you want to wrap it with some theme data or something, so instead of passing the native navigator pass your ExtendedNavigator.
MaterialApp(
builder: (context, nativeNavigator) => DevicePreview.appBuilder(
context,
ExtendedNavigator(router: Router()),
),

If anyone is using auto_route in version 0.6.2 or above, you might be struggling with the solution, as the answer above doesn't work anymore, since auto_route is using ExtendedNavigator.builder now. This is how you can solve the issue:
MaterialApp(
locale: DevicePreview.of(context).locale,
builder: (context, nativeNavigator) => DevicePreview.appBuilder(
context,
ExtendedNavigator.builder<Router>(
router: Router(),
)(context, nativeNavigator),
),
);

Related

Keep Navigator state data (provider/InheritedWidget) when pushing routes

In my flutter app I have a list of instances, when user clicks one I open the instance page and provide the InstanceData model to it using
Navigator.push(context, MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider(
create: (_) => InstanceData(...),
child: Instance()
)
));
This does work, but when the Instance widget does Navigator.push, the provider data is lost because my provider is below the app's Navigator.
I googled for a solution and found that I can do:
Navigator.push(context, MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider(
create: (_) => InstanceData(),
child: Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => Instance()
);
},
)
)
));
Here's the dartpad to show it: https://dartpad.dev/?id=51fcbabb1ad401afb193558ccde3b5e1
Now, all (even deeply nested) widgets that are opened from Instance() will have access to the InstanceData. Also, I actually need multiple nesting. E.g. we click Instance and create Provider for it, then inside Instance we click Area and I want all widgets from Area() and below to have access to both InstanceData and AreaData, so I will need to create another Navigator to hold AreaData. Think of it as in this example:
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
which shows that we keep separate history inside each Tab. Now, if each my Instance is in its own Tab, and Instance also has tabs panel with each tab being Area that also has to keep its own history - that's what I need.
However I am new to flutter and I am not sure if that's the correct approach. Creating own Navigator on each route push just so that my Inherited/Provider data is accessible? Looks like a hack to me.
E.g. what if I want to show two Instance() widgets side by side - I would wrap them with their own Navigator just to pass the data but isn't navigator supposed to track pages/screens instead? How will it behave with the Back button?
So can somehow confirm the solution and if not, them suggest a state management for flutter that will allow to create do something like
Navigator.push(context, MaterialPageRoute(builder: (context) =>
CoolProvider(
create: (_) => InstanceData(...),
child: Instance()
)
));
where CoolProvider does work for any Navigator.push() inside any child widget of the Instance(), even if that's some "CoolNavigator.push" instead. Maybe it's some kind of sub-routes? E.g. Navigator.pushSubRoute() so that the main route is still there?
Maybe it's not even about Navigator? E.g. in the side-by-side Instances having Navigator is almost meaningless - user can't navigate to both instances at once, but I do need each Instance() to be clickable and to go to nested Areas with the InstanceData() available to those widgets.
Update: In fact I tried it side by side here:
https://dartpad.dev/?id=3c299f3972ac18a1b70b841380eaffef
and it seems to be working fine, because without the Navigator the push just replaces the whole page, while with navigator it correctly replaces just the half-side widget.
So is it the way I suppose to do? Any drawbacks - need to use navigatorKey, etc?
P.S. There are of course alternatives like passing InstanceData to each child ctor but that's a lot of manual properties passing around... having the Provider is surely a better way.

How to use drawer in flutter_platform_widgets: ^0.72.0

I'm trying to use a drawer with PlatformScaffold but it seems MaterialAppBarData does not have a named parameter for drawer. What is the workflow for adding a drawer using flutter platform widgets for the newer versions. For example in ^0.72.0. I can't seem to find any information on this in the docs or on forums.
Found a tutorial using the PlatformAppBar and saw that I should use the material argument in the scaffold other than the one in the PlatformAppBar. Closing this. Below is the workflow.
PlatformApp(
home: PlatformScaffold(
appBar: PlatformAppBar(
title: Text("Title"),
),
body: AppContent(),
material: (context, platform) => MaterialScaffoldData(drawer: Drawer()),
),
);

how to navigate to new screen without setting routes in flutter

I'm building a shopping app and I can create a category from the database! however, I'm having a problem on how to navigate to that new category without defining routes in main.dart
Do you try this?
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);
Or, alternatively, you can use onGenerateRoute to define how your routing should work, and do whatever black magic you desire in this function. :-)

Provider across routes but after MaterialApp

I have a class which needs access to MaterialApp context, but it also needs global access from all routes.
To create a global provider, I can wrap MaterialApp with Provider, but such provider has no access to the context. Therefore, I have to provide the class after MaterialApp.
I realize I could wrap every single route with the provider because it is stateless, but I would like to know if there is a better way of doing this.
Thanks in advance!
You can use builder or onGenerateRoute of MaterialApp
MaterialApp(
builder: (context, child) {
return Provider.value(
value: // TO-DO use context
child: child,
);
}
)
You can wrap the material app in a Builder to gain acess do a brand new context.

Logout from non-Widget class

My app makes multiple requests to the server. Sometimes the server may ask the user to re-login, similar to this question:
Flutter: how to force an application restart (in production mode)?
I could do something like this,
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
(Route<dynamic> route) => false);
but I need to have a BuildContext for this.
Is there a way to get a current (most recently used) context while in a non-Widget class? I know I can pass the context as an argument every time I'm making a server call, but I hope to find a less intrusive way to do this.
The reason you are looking for context is because you'd like to get hold of the Navigator. (All the Navigator.pushXXX calls do a Navigator.of(context) under the hood to find the (typically) one and only navigator up near the top of the widget tree.)
If what you really want is just access to the navigator, there's another way to do this. Make your app stateful and put a global key in its state.
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
In the app's build (or top inherited widget's build), when you create the material app, pass it the key.
#override
Widget build(BuildContext context) {
return _SomeInherited(
data: this,
child: MaterialApp(
navigatorKey: navigatorKey,
title: 'Some Title',
theme: someTheme,
home: FrontPage(),
),
);
}
Now, from the level of the app, you can use navigatorKey.currentState.pushXXX