I am using flutter_inappwebview 5.7.2+3 to open Chrome custom tabs on Android like this:
class EnrollBrowser extends ChromeSafariBrowser {
#override
void onOpened() {
logDebug("ChromeSafari browser opened");
}
#override
void onCompletedInitialLoad() {
logDebug("ChromeSafari browser initial load completed");
}
#override
void onClosed() {
logDebug("ChromeSafari browser closed");
}
}
Future openELoanBrowser() async {
final theUrl = await buildUrl();
try {
await EnrollBrowser().open(
url: Uri.parse(theUrl),
options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions(
packageName: 'com.android.chrome',
shareState: CustomTabsShareState.SHARE_STATE_OFF,
keepAliveEnabled: true),
ios: IOSSafariOptions(barCollapsingEnabled: true)));
} catch (e) {
logError(StackTrace.current, 'Error opening browser: ${e.toString()}');
return false;
}
return true;
}
When ever I navigate, I use (it is a old project):
Navigator.of(context).push()
I have a strange problem.
If I have opened the Chrome custom tab and I then put the app in the background and bring it back to foreground, everything works as I expect, the complete navigation stack is preserved and my Chrome custom tab is on top.
I have a flow where the page opened in the custom tab opens a third party app than then in return opens my app again via Asset Links.
If I have "android:launchMode" set to "singleTop" or "singleTask" or "singleInstance" then my app, when opened with Asset Links, now has a very strange navigation stack. The custom tab that used to be and should be on top, now is on bottom and the rest of the navigation stack from before are now on top of my custom tab and I have to navigate back and back and back... until I get to it.
If I have "android:launchMode" set to "standard" then I am almost there. When my app is opened with Asset Links, the whole navigation stack is preserved, but one problem remains, my default "/" route is pushed on top of the custom tab.
I have tried calling:
Navigator.of(context, rootNavigator: true).pop();
when I detect that my "/" route has been pushed on top of my custom tab, but then I get a black screen. This is strange, because if acuallt manually navigate back on the device, it works and I get back to the custom tab.
So, with option 1. I do not get any routes added, but my custom tab that was at the top of the navigation stack now is at the bottom. Can I prevent this somehow? Then option 1. would probably be best.
With option 2. the correct navigation stack i preserved, but the "/" route is pushed on top of everything, and from code I can not pop this (even though I can manually).
How can this be? Any ideas to fix this?
Best regards and thank you
Søren
Related
According to this blog, if using Navigator 2.0 and/or (in my case) GoRouter you can no longer override the phone's back button using the "WillPopScope" and onWillPop function call. Navigator 2.0 now uses PopRoute to go back.
This causes an issue when using webview on a flutter page. If the user navigates to another web page within that webview and then clicks the back button on their phone they naturally expect the webview navigate back to the previous web page. But instead it takes the user off that page and back to their previous flutter page.
Is there any way around this? Can I have my back button first check whether there is a controller.canGoBack() like I used to be able to do with the old Navigator system?
I have found a solution. Convoluted, but functional:
I had to create a custom "backButtonDispatcher" and add it to the main.dart MaterialApp.router function
child: Builder(builder: (BuildContext context) {
final router = Provider.of<MainRouter>(context, listen: false).router;
backbuttondispatcher = backButtonDispatcher(router.routerDelegate, settings);
return MaterialApp.router(
routeInformationParser: router.routeInformationParser,
routeInformationProvider: router.routeInformationProvider,
routerDelegate: router.routerDelegate,
backButtonDispatcher: backbuttondispatcher,
.
.
.
I created the new dispatcher in the router folder and called it "backbuttondispatcher.dart.
import 'package:flutter/material.dart';
class backButtonDispatcher extends RootBackButtonDispatcher {
final RouterDelegate _routerDelegate;
final _settings;
backButtonDispatcher(this._routerDelegate,this._settings)
: super();
Future<bool> didPopRoute() async {
//Can user leave the page?
if (!_settings.canLeavePage) {
//no, as the webview widget has flagged canLeavePage as false
_settings.goBackToPreviousWebsite();
return true;
}else{
//yes, perform standard popRoute call
return _routerDelegate.popRoute();
}
}
}
Using a shared class reference (I used "_settings") I store a flag that says whether or not the user has traversed through more than one web page - if TRUE, the back button dispatcher won't go back to a previous route/page and instead call another function (pointer) that handles going back to a previous web page in the webview widget route. But if FALSE, the dispatcher performs it's standard didPopRoute function.
Additionally, on all other routes/pages with a webview, the pointer function and boolean need to reset to null and false. This is not ideal but fortunately there aren't that many pages in the app.
It annoys me that they changed the back button functionality for main route/page navigation but didn't take in to consideration the fact that the back button can also be used for going back to a previous webpage. I understand that we shouldn't really be showing web pages with apps anyway but we lowly developers don't always have the power to deny app requirements from higher up.
In my app I have multiple pages, but when I try and navigate backwards, instead of going to the previous page it always goes to the root page (MainPage). On Android, it's bad because you lose all context. On iOS it's worse, because it causes an exception in the Program class here:
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
The exception is System.NullReferenceException. Message=Object reference not set to an instance of an object.
I'm navigating like this - I'm starting out on my MainPage, and I navigate to the next page like this:
FriendsPage fp = new FriendsPage();
await Navigation.PushAsync(fp, true);
On that page, I'm going to the next page like this:
FriendRatingsPage frp = new FriendRatingsPage() { SearchResults = results, FriendRatingListType = FriendRatingsPage.RatingListType.FriendsItemRatings };
#if ANDROID
await Navigation.PushAsync(frp, true);
#else
await Navigation.PushModalAsync(frp, true);
#endif
Modal pages are broken it seems for Android right now, which is why the two different approaches.
Once I'm on the third page, I want to go back to the previous page. So I follow the directions explained here: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/shell/navigation. It says "Backwards navigation can be performed by specifying ".." as the argument to the GoToAsync method". So I have a click handler that does so like this:
await Shell.Current.GoToAsync("..");
And that's where things go sideways as I described above - going back to the MainPage instead of the previous page, which was the FriendsPage. I've gone in circles on this but clearly am missing something. Can anyone tell me what I'm doing wrong here?
In my Flutter application my '/' route is set to Wrapper(),
In that Wrapper, I have the following:
FirebaseUser user = context.watch<FirebaseUser>();
if (user == null) {
return Authentication();
} else {
return Home();
}
The problem I a having with this code is that if I update the Navigation stack with, for example, Navigator.pushNamed(context, "/login"), the return statement from above does not update the screen.
For instance, after the Authentication() page I redirect to either Login or Register page with Flutter's Navigator. And if the state of the FirebaseUser changes from one of those pages, I do not get redirected to Home() from my Wrapper(). I need to update the screen manually (press back on Android Emulator) and only then will I be redirected to Home().
I am also open to complete 'redesigns' of the structure of my app if needed.
EDIT: Also, do I even need the wrapper? I implemented it to make sure the user get to the right screen, since I did not know if there are any workarounds with the state of the app. Can I get by with just using the navigation without all of the Wrapper nonsense?
I am using ionic deeplinks. I have a service where I initialize the deeplink function like:
initDeepLink(nav: Nav) {
this.deeplinks.routeWithNavController(nav, {
'/page1/ PageComponent1,
})
}...
I call this function in my main app.component.ts , inside onAllReady() function, from the service with:
this.calldeepLinkServ.initDeepLink(this.nav);
where this.nav is of type:
#ViewChild(Nav) nav: Nav;
declared in app.component.ts
When I visit a deeplink on my device, it works fine and goes to the given page (PageComponent1), but there is always a blank page before it and then the transition sort of jumps and push the deeplink page.
It never push (slide from right) the new page in the top of the current one opened in my app. I was trying to put the #ViewChild(Nav) nav: Nav inside the deeplink service rather the app.component.ts, but it acts the same.
I also tried to see if there is a method to disable the native page push in this.deeplinks.routeWithNavController() but it does not have any properties like that (like animate: false for example which is available for NavController).
So I am really not sure how to prevent this bad navigation experience with deeplinks i am sure that this is not a typical transition for ionic deeplinks, it looks bad for UI.
So I've sorted this one out, instead:
this.deeplinks.routeWithNavController
I am using:
this.deeplinks.route
and than i check what is passed on inside the match.$link. So depending on the query send through like
if (match.$link['path'].indexOf('/page1/') !== -1) {...}
I call the nav push, with animate:false and it does not uses the default nav push animation.
Having some trouble opening a Dialog after navigating to a page. The intention is to always open the dialog when a user lands on this page.
Simplified, I have a controller that looks like this:
onInit: function() {}
this.myRouter.getRoute("orderscreate").attachPatternMatched(this._onObjectMatched, this);
},
_onObjectMatched: function() {
this.dialog = sap.ui.xmlfragment("myDialog", this);
this.dialog.open();
},
When I put a debugger in, this works great: I can see the dialog is open.
THEN, the navigation animation kicks in, does the slide animation, and upon completion the dialog is closed again. I'm not sure why it insist on navigating after the view has rendered.
This happens when using navTo as well. Dialog opens, animation starts, dialog is closed again. sap.m.Page does not have another way of executing code after showing, as far as I'm aware.
Any advice?
Unless told otherwise the TargetHandler will close all open dialogs.
TargetHandler, a class used for closing dialogs and showing transitions in NavContainers when targets are displayed.
Try adding the following code after your router has been initialized
this.myRouter.getTargetHandler().setCloseDialogs(false);