How to intercept Navigator.pop and block navigation back conditionally? - flutter

I'm looking for a way to intercept navigation back conditionally.
User/server can modify global state of the app like authentication, causing page to navigate to login form, but when user navigates to screen A then presses back button twice, user is still able to see widgets that are supposed to be available only for logged in session.
I've tried all I could thought of:
onGenerateRoute in MaterialApp but it's not triggered when back button is pressed
WillPop is a solution to that, but all top-level widgets would have to be wrapped in it and I need to remember to pass auth state to all the widgets, it's not really scalable solution, but could work
navigatorObservers is notified about Navigator.pop events seems not to be able to prevent navigation
Ideally, would like to avoid 3rd party dependencies with hooks on every widget like back_button_interceptor.
It should also work with statelesswidgets, where dispose() method is not available.
Is there any way to get something like single class/point of failure "navigator interceptor" which would return true/false and be able to modify route in flight based on a condition?

Looking into the implementation of WillPopScope, it makes use of the addScopedWillPopCallback() function of the ModalRoute.
https://api.flutter.dev/flutter/widgets/ModalRoute/addScopedWillPopCallback.html
If you implemented a router with onGenerateRoute it should be possible to add an universal check for every route you push. Maybe you could even make a customized Route class inherited from MaterialPageRoute that includes that logic.
If i understood your problem right,i feel like intercepting back navigation is the wrong approach. I would rather try to remove the unauthorized pages from the navigation stack when navigating back to the login screen with Navigator.pushNamedAndRemoveUntil()

Related

Flutter BLoC Cubit back button history

I have a project that I had initially setup with Navigator and have since converted over to use BLoC Cubit.
When I use Navigator.of(context).pop(); to go back from a screen that was navigated to using context.read<AppCubits>().pageOneScreen(); I get a black screen instead of being directed back.
Does BloC Cubit's not allow or keep any history like Navigator does?
**** MORE DETAILS ****
I have a back button that would simply call
Navigator.of(context).pop()
and bring the user back to their previous page.
Now with Cubit, using the above doesn't work and returns a blank screen as there is no context stored.
So I have been searching for a solution to this but every one of them seems very verbose, is this type of feature not a part of Cubit or BLoC?
To navigate from page to page currently I am using
context.read<AppCubits>().pageOneScreen();
context.read<AppCubits>().pageTwoScreen();
This works as expected but I am looking for a solution to adding a call that will navigate back to the previous screen/state that was loaded. So if I was on pageTwoScreen, I could click back if I had come from pageOneScreen and have the state changed or at least be directed back to pageOneScreen.
Do I have to create a list and store the state value as a form of rudimentary history and then pop off the previous value and call something akin to
context.read<AppCubits>().historylistvalue()

how to send a data from page to another without change the screen?

i have a page named displayData . On it there is a button if i click on it it shows DraggableScrollableSheet normally it has a button done when i click on it the icon of the page on the bakground (DisplayData) must change .i used a variable in a setState but it doesn't work it's true on the DraggableScrollableSheet but always false in display data
any help !
for this type of cases, there are several solutions, I recommend the use of providers, in this way with the use of 'extends ChangeNotifier' you will be able to detect the changes made without having to use the setState and you will be able to call from any class (or page ) the variables you need
you can use callback methods from on widget to another, if you have much complicated scenario then I suggest using state management libraries like Provider, RiverPod or Bloc.
also
Could you share your code here

How to simulate device back btn press/ AppBar back press? WillPopScope is not called upon Navigator.pop()

I was able to override the back button onPressed behavior using WillPopScope. Now, whenever I press the device back button or the default AppBar back button, the code inside onWillPop runs as expected.
The problem now is that I want to have my own custom buttons to navigate backward, but Navigator.of(context).pop() doesn't check the WillPopScope as if it doesn't exist.
Is there a way to simulate the system's back behavior or call it somehow?
I know I can just call the same function that is called by
onWillPop() manually upon pressing my custom back button, but I was eager to find a cleaner method, as in the future I could easily forget doing that and cause bugs.
In simple cases, when you need to intercept the Android back-button, you usually add WillPopScope to your widget tree. However, when developing stateful widgets that interact with the back button, it's more convenient to use the BackButtonInterceptor.
For more details check this link: https://pub.dev/packages/back_button_interceptor
While using StateFullWidget, It is more convenient to use BackButtonInterceptor.
Hope this will Helpful.
For more Details Please check this:
[1]: https://pub.dev/packages/back_button_interceptor
The key was to use Navigator.of(context).maybePop()instead. Or more specifically in my case, was to use Navigator.of(context, rootNavigator:true).maybePop() as I was using nested Navigation.

How to navigate to a specific Route at app start and show another one when popping in Flutter?

The question is, how to achieve this with a clean navigation approach in Flutter.
So to better understand the question, here's an example:
The user taps on a messenger notification and the app opens. The first screen to show is a setup screen, which verifies if the user authenticated and does some app related stuff. The next screen should be the chat with the corresponding message. After closing this chat screen, the user lands on the screen containing a list with all chats. After closing this one, the user lands on the home screen of the app.
What is the best and cleanest approach to handle such a scenario? Should I use a separate navigation package like auto_route or what do u suggest?
Or is this a deep link scenario? How to implement such a scenario?
Define static String id for all screen,after define them under MaterialApp as
routes (map<Sting, Function>) after you have to give parameter to initial route still under MaterialApp, now you can use these routes by using Navigator.pushNaned and Navigator.pop function.

Determining if widget is in the foreground/background

Say I have a Flutter app with two screens - screen A and screen B. Screen A is just a ListView that displays a list of items but needs to perform a network call whenever the list changes and then update the list. Screen B can modify the list of items but I want screen A to only perform the network call when screen A is in the foreground and being displayed. As far as I can tell, I cannot do this easily. Screen A is not disposed and reinitialized when navigating to and from screen B. It would very helpful if StatefulWidget had onForeground and onBackground methods to override, but they do not. This problem is not exclusive to navigation either, but this same problem presents itself when using PageView with full-screen pages. Is there some proper/standard way of implementing this?
My current setup is as follows: I let the parent widget to both screen A and B hold the list in a ValueNotifier and pass it to both screens when constructing them. Then, screen A listens for changes on the ValueNotifier and performs a network call whenever it does. So, in order to determine whether screen A is in the foreground/background, I will have to start/stop listening for changes before/after navigating. But I haven't started implementing this, as I think it will get complicated when widgets far down the widget tree trigger the navigation or other widgets need to know whether they're in the foreground/background.
Another option I've thought of is instead of observing for changes in the list, I could rather just return a result from screen B saying whether or not the list changed and react then. But I can think of many ways this can complicate my code as well since my real app involves more than just one dependency. I would have to create a custom result class for each screen containing a record of all the data that changed then it would be tedious if I want to add more data in the future. And how would I handle navigation to screen B then screen C? The result would have to be retained and passed down so screen A can react to changes made by screen C. I would also have to ensure all calls to Navigator.pop contained the result, and override back button presses to pop with the result. And I'd also have to ensure that the result makes it to the proper widgets that need to react to changes.
Am I just missing something simple to accomplish this? I am not the most experienced with Flutter and I wouldn't be surprised if there's some easy solution I haven't learned yet.
Edit: After some more testing, it appears AnimationController does something similar to what I need with the vsync parameter, in that it does not update when the State is in the background or when it is not being drawn. So, I could use SingleTickerProviderStateMixin on screen A's state, then use createTicker to create a Ticker, and then check if the Ticker.isTicking whenever the list changes. If it is ticking, screen A is in the foreground, otherwise it is in the background. Although I'm not sure if this is a good approach, since it appears Ticker's are really only used for Animations and there's nothing documented for a use case like mine.