Flutter - How to handle Navigation without using MaterialApp? - flutter

In flutter creating named routes is easy and logical, but only when returning a MaterialApp.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
initialRoute: "/";
return MaterialApp( //gives errors when I return a Container
routes: {
"/" : (context) => FirstRoute(),
"/second route" : (context) => SecondRoute()
}
);
}
}
I am not that big a fan of Material Design and want to create a UI from my own design.
But when I apply the same pattern when returning a container I get an error.
So my qustion is how do I have named route setup with a vanilla Flutter App or am I being forced to use MaterialApp for my project?
Thanks in advance

MaterialApp is just a collection of commonly used components such as a navigator. You could also use a CupertinoApp. Material uses iOS navigation animations on iOS and Android animations on android. Though you’re not stuck to the UI design because you’re using a MaterialApp as a foundation. You are able to build whatever UI you want with a material app or even use Cupertino widgets. It’s all up to you.

Related

How to set the default Scroll Physics of an android flutter app

I am developing a flutter app, and simply want to change the default scrolling physics to BouncingScrollPhysics so my listviews are more modified.
I know that there is already an answered question but it doesn't work on the latest version of flutter.
I wouldn't want to go around my app changing ListView properties an doing bad practices, any help would be appreciated <3
Similar to this solution:
Default ScrollPhysics
class MyScrollBehavior extends ScrollBehavior {
const MyScrollBehavior();
#override
ScrollPhysics getScrollPhysics(BuildContext context) => const BouncingScrollPhysics();
}
MaterialApp(
scrollBehavior: MyScrollBehavior(),
// ...
);

Integrate a Flutter app in to another flutter app

I have a bigger app named X and I have another smaller one named Y.
they are right now separate from each other and they are working fine.
I want to integrate app Y in X. I want to put codes of Y in X project but they should have a different Main so I can set different themes and routes.
Is there anyway to do that?
////update////
I upvoted all answers because they are all correct.
but if you know this one please answer.
I am using GetMaterialApp from GetX instead of MaterialApp.
and it returns error
'package:flutter/src/widgets/framework.dart': Failed assertion: line 5334 pos 14: '_dependents.isEmpty': is not true.
how can I fix this?
You can use multiple MaterialApp() in your project, combine the code/file like that the app is on another screen, and you will result in apps that have different references to their InheritedWidgets like Theme, Navigator, MediaQuery...
/*...*/
MaterialApp(
/*...*/
home: AppXHome(),
),
/*...*/
class AppXHome extends StatelessWidegt {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(),
Container(),
MaterialApp(
home: AppYHome(),
),
],
),}}
You can use two MaterialApp widgets for defining separate routing ,
One for your APP X and other for Your APP Y,
Main function will remain the same no changes needed,
just create a route in APP X for app Y and wrap your App Y code in another MaterialApp Widget where you can define routes for APP Y.
Even if you integrate one or more apps into another app, there must be only one main after the whole merge.
So you need to move the other's main logic to the current one and then deal with the theme.
You can utilize this sample, which has three different apps in it with custom themes

How to scope providers on several widgets without putting the provider on a root widget in flutter

I'm working on a simple demo where the user logs in, sees a list of items, clicks on an item, and then sees its details. These are 3 screens but let's say two flows where one deals with login and the other with items.
I'm managing my state using provider package. I placed a LoginRepository provider on my root widget because that state is needed everywhere. This works great.
Now I need an ItemRepository which should be available on the ItemListScreen and the ItemDetailScreen. To implement that scoping, I decided to create a common ancestor widget called ItemScreens. This is where I placed my provider and my nested Navigator.
I navigate my screens using Navigator.of(context) to make sure the closes ancestor navigator is used. I fetch my provider using Provider.of<ItemRepository>(context) to make sure the closes ancestor state is used.
This apparently does not work because I get a Could not find the correct provider. I understand that the provider from the sibling route cannot be accessed by another sibling but here I'm nesting navigators I would expect the ItemRepository to be placed at ItemScreens which then handles subrouting so that these two routes can fetch providers from the parent route?
Sidenote:
Flutter documentation clearly states that providers should be lifted to achieve proper scoping. But I've never seen an example where the provider was lifted to a non-root widget. If all we can do is place providers into the root widget, that should be clearly stated.
EDIT: Re-creating providers on several widgets is kind of an alternative but that's not really scoping it's just pseudo passing by value.
EDIT: Putting all providers on the root widget with MultiProvider isn't a good idea because it creates a global state anti-pattern. Some states should also simply not be created unless needed i.e. the user is visiting that flow/screen.
You can wrap your MaterialApp Widget in main.dart with Multiprovider and give the list of providers you are using, That way App knows which class to treat as providers.
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => FirstProvider(),
),
ChangeNotifierProvider(
create: (_) => SecondProvider(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Auro',
home: _decideMainPage(context),
// routes: routes,
theme: AllCoustomTheme.getThemeData(),
),
)

Notification to adjacent widget

I have a widget that contains a "save" button. After pressing that button several other widgets (not only ancestor ones) must save its state to the file. I don't know how to inform them. What is the best way to achieve that in flutter? I was thinking about using Notification in the "save" widget, closest shared ancestor would contain a NotificationListener that triggers an event to which every widget will subscribe. For me, it doesn't look like a solution.
Provider is the recommended way to do State Management for apps of all sizes. -–Chris Sells – Product Manager, Flutter. June 19, 2019
It's pretty complicated at first, best to check out Simple app state management
The ChangeNotifier uses the notifyListeners function whenever there is any change in the class:
class ChangeNotifierModel extends ChangeNotifier {
String stringThatChanges = 'startValue';
void changeTheString(String value) {
stringThatChanges = value;
notifyListeners();
}
}
The only code that is specific to ChangeNotifier is the call to notifyListeners(). Call this method any time the model changes in a way that might change your app’s UI.
I'm pretty new to Flutter myself, but the way I understand it so, it kinda acts like a wrapper, e.g. wrapping the child of main.dart
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => SingleNotifier())
],
child: MyApp(),
));
don't forget the dependencies
dependencies:
provider: ^4.3.2+2

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