Description
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
When facebook url finish loading, canGoBack return "true" but when tap on goBack, it doesn't do anything. Should return "false" if it's not able to go back to previous page. This doesn't happen to other url like instragram/youtube/other websites.
Expected Behavior
canGoBack return "false" like other redirected url eg: instagram. And let the navigation router pop the page.
Steps with code example to reproduce
Code Sample
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as iwebv;
import 'package:url_launcher/url_launcher_string.dart';
import 'package:webview_flutter/webview_flutter.dart' as webv;
import 'package:url_launcher/url_launcher.dart';
const url = 'https://facebook.com/facebook';
// TODO: change to instagram link, on goBack able to work.
// const url = 'https://www.instagram.com/facebook/';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FB Navigation Test',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
initialRoute: '/',
routes: {
'/': (context) => const MyHome(),
'/inappwebview': (context) => const TestInAppWebView(),
'/webview': (context) => const TestWebView(),
},
);
}
}
class MyHome extends StatelessWidget {
const MyHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
child: const Text('InAppWebView'),
onPressed: () {
Navigator.of(context).pushNamed('/inappwebview');
},
),
const SizedBox(height: 10.0),
TextButton(
child: const Text('WebView'),
onPressed: () {
Navigator.of(context).pushNamed('/webview');
},
),
],
),
),
);
}
}
Future<bool> handleAndroidIntent(String url, Function(String fallbackUrl) reloadUrl) async {
try {
String? deepLink;
RegExp schemeRegex = RegExp(r'scheme=([\w\.]+)');
Match? match = schemeRegex.firstMatch(url);
if (match != null) deepLink = match.group(1);
// Extract fallback URL
String? fallbackUrl;
RegExp fallbackRegex = RegExp(r'S\.browser_fallback_url=([^;]+)');
Match? fallbackMatch = fallbackRegex.firstMatch(url);
if (fallbackMatch != null) {
fallbackUrl = Uri.decodeFull(fallbackMatch.group(1)!);
}
// Convert intent:// to fb:// or appropriate deep link
if (deepLink != null && deepLink == "fb") {
String fbDeepLink = url.replaceAll("intent://", "fb://");
if (await canLaunchUrlString(fbDeepLink)) {
await launchUrlString(fbDeepLink);
return false;
} else if (fallbackUrl != null) {
reloadUrl(fallbackUrl);
return false;
}
} else if (fallbackUrl != null) {
// If not Facebook but there's a fallback URL, open it
reloadUrl(fallbackUrl);
return false;
}
return true;
} catch (e) {
print("Error handling intent:// URL: $e");
return true;
}
}
class TestInAppWebView extends StatefulWidget {
const TestInAppWebView({super.key});
@override
State<TestInAppWebView> createState() => _TestInAppWebViewState();
}
class _TestInAppWebViewState extends State<TestInAppWebView> {
iwebv.InAppWebViewController? _inAppWebViewController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('InAppWebViewTest')),
floatingActionButton: Builder(builder: (context) {
return FloatingActionButton(
child: const Icon(Icons.arrow_back),
onPressed: () async {
// Back on webview or pop page.
final canBack = await _inAppWebViewController?.canGoBack() ?? false;
print('canBack InAppwebview: $canBack');
openSnackBar(context, canBack);
canBack ? await _inAppWebViewController?.goBack() : Navigator.pop(context);
},
);
}),
body: iwebv.InAppWebView(
onWebViewCreated: (ctl) => _inAppWebViewController = ctl,
initialSettings: iwebv.InAppWebViewSettings(transparentBackground: true),
initialUrlRequest: iwebv.URLRequest(url: iwebv.WebUri(url)),
shouldOverrideUrlLoading: (webviewController, navigationAction) async {
var uri = navigationAction.request.url!;
/// Handle on intent://
if (!["http", "https", "file", "chrome", "data", "javascript", "about"].contains(uri.scheme)) {
if (uri.scheme == 'intent' && Platform.isAndroid) {
final result = await handleAndroidIntent(
uri.toString(),
(fallbackUrl) =>
webviewController.loadUrl(urlRequest: iwebv.URLRequest(url: iwebv.WebUri(fallbackUrl))),
);
return result ? iwebv.NavigationActionPolicy.ALLOW : iwebv.NavigationActionPolicy.CANCEL;
}
if (await canLaunchUrl(uri)) await launchUrl(uri);
return iwebv.NavigationActionPolicy.CANCEL;
}
return iwebv.NavigationActionPolicy.ALLOW;
},
),
);
}
}
class TestWebView extends StatefulWidget {
const TestWebView({super.key});
@override
State<TestWebView> createState() => _TestWebViewState();
}
class _TestWebViewState extends State<TestWebView> {
late final webv.WebViewController _webViewWidgetController = webv.WebViewController()
..setJavaScriptMode(webv.JavaScriptMode.unrestricted)
..loadRequest(Uri.parse(url))
..setNavigationDelegate(
webv.NavigationDelegate(
onNavigationRequest: (request) async {
Uri uri = Uri.parse(request.url);
/// Handle on intent://
if (!["http", "https", "file", "chrome", "data", "javascript", "about"].contains(uri.scheme)) {
if (uri.scheme == 'intent' && Platform.isAndroid) {
final result = await handleAndroidIntent(
uri.toString(),
(fallbackUrl) => _webViewWidgetController.loadRequest(Uri()),
);
return result ? webv.NavigationDecision.navigate : webv.NavigationDecision.prevent;
}
if (await canLaunchUrl(uri)) await launchUrl(uri);
return webv.NavigationDecision.prevent;
}
return webv.NavigationDecision.navigate;
},
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('WebViewTest')),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.arrow_back),
onPressed: () async {
// Back on webview or pop page.
final canBack = await _webViewWidgetController.canGoBack();
print('canBack WebView: $canBack');
openSnackBar(context, canBack);
canBack ? await _webViewWidgetController.goBack() : Navigator.pop(context);
},
),
body: webv.WebViewWidget(controller: _webViewWidgetController),
);
}
}
void openSnackBar(BuildContext context, bool canBack) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Can go back -> $canBack')));
}
Flutter version
v3.24.5
Operating System, Device-specific and/or Tool
iOS.
Android need to spam goBack few times only able to work.
Plugin version
v6.1.5
Additional information
I've attached the sample code using webview_flutter
plugin also, it's behaviour is same as flutter_inappwebview
, might the the way Facebook handle the redirection.
I've logged out the getCopyBackForwardList()
, saw that when redirection finish. I'm in the index 1 instead of 0. Not sure if this helps.
Is it possible that I'll have to apply workaround specifically just for facebook navigation?