Get overlay without context - flutter

I need a reference to the current OverlayState from a service where I don't have access to the current context. I've tried to use the "trick" of getting my BuildContext from a navigator key, but while this works for dialogs and navigation, does not seem to be valid for this use case. I've tried following approaches:
Overlay.of(navigatorKey.currentContext, rootOverlay: false);
Overlay.of(navigatorKey.currentState!.context, rootOverlay: false);
Overlay.of(navigatorKey.currentState!.overlay!.context, rootOverlay: false);
And all of them return null. Same piece of code, called from a widget (Overlay.of(context)) returns a valid OverlayState. Is there any way of achieving this?

Related

Is there a way to solve the BuildContext problem?

I'm getting The argument type BuildContext Function() can't be assigned to the parameter type BuildContext.
You can't send context to a function when it isn't even defined in the initstate function. Here you are trying to use provider outside the Build Function which is a no no. What you can do as an alternative is to wrap the function with a Future to give it a valid context :
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
myString = Provider.of<AppData>(context, listen: false).testString;
});
I solved this by removing the flutter whole folder where it is saved and than remove the Environmental Values and remove path and add new download new Flutter SDK and than again update the path in Environmental Values

How to delete an item from a list in Flutter using provider?

I have a list that I am trying to delete a specific item from.
I first tried:
Provider.of<WeekList>(context, listen: false).listOfWeeks.remove(widget.index);
That did not work.
After thinking about it I realized that the Provider is retrieving the list but not actually updating the provider.
So then I decided to use the provider to save retrieve the list and save it to a new list:
List<Week> myList =Provider.of<WeekList>(context, listen: false).listOfWeeks;
I then tried to delete an item at the current position:
myList.remove(widget.index);
I expected the myList to be shortened by one after that last line of code. But I put a print before and after it and they both still say the length is 6...
not sure why its not removign anything from myList. If it worked it should shorten it by one and I planned on then trying to update the provider.... But maybe I am not goign about this correctly.
in your last approach you are changing a "copy" of your list, what you need to do to achieve your goal is to make function inside your state that updates your list and notifies the listeners,
this function to be put inside your ChangeNotifier class:
void updateList(int index)
{
listOfWeeks.removeAt(widget.index);
notifyListeners();
}
and you can call it like:
Provider.of<WeekList>(context, listen: false).updateList(index);
If you're removing by index, you need to use removeAt.

Why I should use named routes?

I searched a lot about "what are benefits of using named route to navigate between screens". And I can't find any actual benefits, actually it has many disadvantages and annoying.
1. In flutter document, it said that named routes use to avoid code duplication.
For example, if I want to navigate to SecondRoute with String one argument, it change from this
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute('Some text')),
);
to this
Navigator.pushNamed(context, SecondRoute.routeName, arguments: 'Some text');
and I need to register it in main file
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == SecondRoute.routeName) {
final String text = settings.arguments as String;
return MaterialPageRoute(
builder: (context) => SecondRoute(text),
);
}
},
);
And if I have more routes, I need to handle and assign arguments for every routes. Isn't that more duplication and complexity?
Also setting.arguments has no-type, isn't that very bad?
2. You can't select which constructor to use when using pushNamed.
For example, I have two constructor default and otherConstructor
class SecondRoute extends StatelessWidget {
static const String routeName = '/second';
String? text;
SecondRoute(this.text);
SecondRoute.otherConstructor(String text) {
this.text = 'Other Constructor: ' + text;
}
}
How can I tell pushNamed which one I want to use.
I have an idea to pass constructor name as an argument and check it like this
Navigator.pushNamed(context, SecondRoute.routeName, arguments: ['default' or 'otherConstructor','Some text']);
In main file
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == SecondRoute.routeName) {
final args = settings.arguments as List<String>;
if (args[0] == 'otherConstructor') {
return MaterialPageRoute(
builder: (context) => SecondRoute.otherConstructor(text),
);
} else if (args[0] == 'default') {
return MaterialPageRoute(
builder: (context) => SecondRoute(text),
);
}
}
},
);
But this is very very complicate and obviously not a good way to do.
3. Some anwsers in reddit and stackoverflow said that named route make code more centralize by keep every route in main file.
Surely centralization is good, but why not do it others way?
For example, keep all dart files that contain routes in new folder
Can someone enlighten me why most people use named route? Thanks for any help.
From my point of view, personally I think the root cause of your concern is you keep using pass the data around your screens using constructor or injection.
Reference from Official flutter docs: https://docs.flutter.dev/development/data-and-backend/state-mgmt/declarative
I am an iOS developer with UI kit so I think you has the same problem with me, that I resolved by changing my thinking flow.
Important thing : From the declaratively UI picture: UI = f(state).
For your problem (1):
I think we should not to pass the data around screens, you can use rxdart, provider, mobx, redux... or any state management. It will keep the data of for UI rendering and you no need to check it again in very long switch/if in main file. If you keep passing arguments so you can't build UI from state
For your problem (2):
I think it's the same problem with (1). Instead of using many constructors, you can create your state to reflecting it. So your screen(I mean the Widget reflecting your screen, not their sub widgets) can take data from state and no need to get it from arguments. Again, it about UI = f(state).
For your problem (3):
I definitely agree with you, keep it in another folder is a best choice.
Note: Personally right now I split widgets to 2 types: screen and Presentational widget.
Screen: Never pass arguments to it, it use the data from state(UI = f(state))
Presentational widget: can receive arguments in constructor.
The original idea is from the founder of redux (https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)
=> With this idea, It clearly resolved your problem (1) and (2). With (3) by split to another folder and import it should end with some lines in main file like this(without any process arguments on screen changing):
For the benefit:
Personally I think most advantage is:
1 - You can change the screen later without modify working code(open-closed principle): Assuming your client want to change the screen A to screen A', which has alot of navigate in your code.
Example: your app can go to login screen from 3 different logic: from splash, from press logout, from admin banned message.
If you use: MaterialPageRoute(builder: (context) => SecondRoute('Some text')), so you need to replace it many times(3 times in example) inside the code that work perfectly. The most dangerous thing is: maybe the logic "from admin banned message" is # # from other dev
If you usse: Navigator.pushNamed(context, SecondRoute.routeName, arguments: 'Some text'), you change the code in splited folder(that you mention in (3)) by redirect it to another screen(the new login scrren)
Things below I think it less important:
2- (just a note) If you build your project with web version, you will the route name in the url address, so it turn your page to get url.
3- Prevents alot of unnecessary import(example you can see in login, if you use routes, just import 1 file. but you use navigator directly you need to import many screens related: register, forgotpass....). Anyway, it can resolve by create something like screen_index.dart.

FlutterMap LateInitializationError State Management

I'm trying to make a Flutter app, using Riverpod for state management and FlutterMaps. I have set up notifiers for pages, and am also using a (State)NotifierProvider for the map page. I am building a search function, which goes to a search page which displays a list with search results. When one of these results (in a list) is clicked, I update the notifier of the map page. However, here comes the tricky part: I want to update the map according to the LatLng of the search result (center it).
My implementation of the Notifier contains:
late MapController? _mapController;
My map page is loaded in and gets the mapController from the Notifier: mapController: notifier.getMapController()
Using Riverpod state management, this all should work, however I get the following Error:
LateError (LateInitializationError: Field '_state#1225051772' has already been initialized.)
This Error is within the MapController itself (which is part of the FlutterMaps package), so I do not want to touch that. Does anyone have any idea how to tackle this?
lateerror is caused by nulling a variable that will fill before the application starts.
late MapController? _mapController;
this definition is wrong. Here, you are saying that this value will fill in soon. You also say that this value can be null.
late MapController _mapController;
if you do so this variable should not be null at runtime. if it is null you will get a lateerror error.
MapController? _mapController;
if you do it this way. This means that the value can be null. You should check when using.
like this
void sameFunction(){
if(_mapController!=null){
... your code
// here make sure this variable is not null. For this reason ! i put this
MapController anotherController=_mapController!;
}
}
or
void sameFunction(){
// Your code will not throw an error if this variable is null. Because specify that
//it can be null
mapController?.moveTo(...)
}
for more detailed information
you should remove the late keyword or you should intialise it in the initState
method.

How to make Provider and Navigator work together?

I use Provider for state management and for separate business logic from UI.
I have put some Provider above MaterialApp so can access everywhere in app (for example user info).
But You don’t want to place ChangeNotifierProvider higher than necessary (because you don’t want to pollute the scope).
So I try put some Provider which are only use on some pages lower in widget tree.
For example in my purchase item flow I can have: SelectItemPage => ConfirmItemPage => CheckOutPage.
But my issue is Navigator.push() create separate widget tree every time. So if I initialise a Provider in SelectItemPage this cannot be accessed in ConfirmItemPage or CheckOutPage.
I want Provider be scope to this flow, but I cannot see how.
How I can solve this?
Or my approach is wrong?
Edit: I know I can use prop drilling for pass data from SelectItemPage => ConfirmItemPage => CheckOutPage but this hard to maintain. I want use Provider instead of prop drilling.
I think it's no other way, you mush initialize the provider on each class
try use like this on you app
on you main app
MultiProvider(
providers: ChangeNotifierProvider<yourstate>(
builder: (context) => yourstate,
),
child: MaterialApp...
on each page, you can initialize using below code
final state = Provider.of<yourstate>(context);
and use like this if you don't want to listen any change of the state
final state = Provider.of<yourstate>(context, listen: false);