flutter: webview does not allow jumping to anchors in the same html page
Unfortunately this doesn't seem to be fixed by now. The best workaround I found was to create local HttpServer.
For simplicity - I used local_assets_server package, which basically wraps around HttpServer and simplifies serving bundled resources. But it is extremely easy to implement this functionality directly using HttpServer, especially if you use this solely for serving static HTML content. Here is my implementation using local_assets_server:
class HelpScreen extends StatefulWidget {
final String? tag;
HelpScreen({this.tag});
@override
_HelpScreenState createState() => _HelpScreenState();
}
class _HelpScreenState extends State<HelpScreen> {
late LocalAssetsServer _webServer;
late int _webServerPort;
bool _webServerListening = false;
@override
void initState() {
_startServer();
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: () {
if (_webServerListening) {
return WebView(
initialUrl: 'http://localhost:$_webServerPort/help.html#${this.widget.tag ?? ""}',
javascriptMode: JavascriptMode.unrestricted,
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
}
_startServer() async {
_webServer = new LocalAssetsServer(
address: InternetAddress.loopbackIPv4,
assetsBasePath: 'assets/html',
);
await _webServer.serve();
setState(() {
_webServerPort = _webServer.boundPort;
_webServerListening = true;
});
}
}
Make sure you allow local HTTP networking on Android by creating/updating android/app/src/main/res/xml/network_security_config.xml
:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">localhost</domain>
</domain-config>
</network-security-config>
... and then referencing it from android/app/src/main/AndroidManifest.xml
<manifest ...>
...
<application
...
android:networkSecurityConfig="@xml/network_security_config"
...>
...
</application>
</manifest>
... and on iOS in ios/Runner/Info.plist
:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
Comments
-
kakyo over 1 year
I've bumped into an annoying flutter issue where HTML anchors don't work in the
WebView
.The full issue is here: https://github.com/flutter/flutter/issues/65379
Steps to Reproduce
I'm using webview to show localized html files. In each HTML file there are
href
links to redirect to corresponding language files. I'm using this little class below to show the HTML as a page.with this little class class LocalLoader { Future<String> loadLocal(String filename) async { return await rootBundle.loadString('assets/$filename'); } } class HtmlPage extends StatelessWidget { final Completer<WebViewController> controller; final String htmlFile; HtmlPage({ @required this.htmlFile, @required this.controller, }); @override Widget build(BuildContext context) { return Container( child: FutureBuilder<String>( future: LocalLoader().loadLocal(htmlFile), builder: (context, snapshot) { if (snapshot.hasData) { return WebView( debuggingEnabled: true, initialUrl: Uri.dataFromString(snapshot.data, mimeType: 'text/html', // CAUTION // - required for non-ascii chars encoding: Encoding.getByName("UTF-8") ).toString(), javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController webViewController) { // CAUTION // - avoid exception "Bad state: Future already completed" if(!controller.isCompleted) { controller.complete(webViewController); } }, ); } else if (snapshot.hasError) { return Text("${snapshot.error}"); } else { print('undefined behaviour'); } return CircularProgressIndicator(); }, ),); } } class HelpPage extends HtmlPage { HelpPage(ctrl) : super(htmlFile: 'help_en.html', controller: ctrl); } class AboutPage extends HtmlPage { AboutPage(ctrl) : super(htmlFile: 'about_en.html', controller: ctrl); }
And the relevant html code looks like this in each language files
<p><span style="font-size: 20px;"><a href="help_en.html">English</a></span> <span class="math inline"> </span> <span style="font-size: 20px;"><a href="help_fr.html">Français</a></span></p>
Repro 1
Open my app
- Place
help_en.html
andhelp_fr.html
inassets
folder, and add the corresponding yaml section. - Load the initial page "help_en.html" and it shows correctly
- Click the
Français
link on the page
Expected results:
We should see the
help_fr.html
upon clicking its redirection link.Actual results:
We see a blank page
Repro 2
I suspected it may have been a relative path error. So I modified the HTML page so that the href looks like
<p><span style="font-size: 20px;"><a href="assets/help_en.html">English</a></span> <span class="math inline"> </span> <span style="font-size: 20px;"><a href="assets/help_fr.html">Français</a></span></p>
But the result is the same as in Repro 1.
LogsE/InputMethodManager(24228): b/117267690: Failed to get fallback IMM with expected displayId=215 actual IMM#displayId=0 view=io.flutter.plugins.webviewflutter.InputAwareWebView{f259a5f VFEDHVC.. .F...... 0,0-2075,706} E/InputMethodManager(24228): b/117267690: Failed to get fallback IMM with expected displayId=215 actual IMM#displayId=0 view=io.flutter.plugins.webviewflutter.InputAwareWebView{f259a5f VFEDHVC.. .F...... 0,0-2075,706}
No
flutter analyze
errors$ flutter doctor -v [✓] Flutter (Channel dev, 1.22.0-9.0.pre, on Mac OS X 10.15.6 19G2021, locale en-CN) • Flutter version 1.22.0-9.0.pre at /Applications/flutter • Framework revision 7a43175198 (10 days ago), 2020-08-28 23:18:04 -0400 • Engine revision 07e2520d5d • Dart version 2.10.0 (build 2.10.0-73.0.dev) • Pub download mirror https://pub.flutter-io.cn • Flutter download mirror https://storage.flutter-io.cn [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0) • Android SDK at /Applications/Android/sdk • Platform android-30, build-tools 30.0.0 • ANDROID_HOME = /Applications/Android/sdk • ANDROID_SDK_ROOT = /Applications/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 11.7) • Xcode at /Applications/Xcode.app/Contents/Developer • Xcode 11.7, Build version 11E801a • CocoaPods version 1.9.1 [✓] Android Studio (version 4.0) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin version 49.0.2 • Dart plugin version 193.7547 • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593) [✓] VS Code (version 1.48.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.13.2 [✓] Connected device (2 available) • ONEPLUS A6000 (mobile) • <my udid> • android-arm64 • Android 10 (API 29) • MyiPhone (mobile) •<my udid> • ios • iOS 13.6.1 ! Device emulator-5554 is offline. • No issues found!
UPDATE
I dug further and thought this might be related to #27086, but it turned out to be even worse, see below:
Attempt
To work around this issue, I thought about merging
help_en.html
andhelp_fr.html
into a single html file and turn the href's into anchors so that I can bypass the local assets reference issue by jumping within the same html file.In theory, this would work.
Trouble
However, to my surprise, this didn't work either! I still see blank pages when trying to go to the anchors. The same anchors work fine in an Electron test app and in the browsers.
This time I got an error in Android Studio's console
E/InputMethodManager(28987): b/117267690: Failed to get fallback IMM with expected displayId=259 actual IMM#displayId=0 view=io.flutter.plugins.webviewflutter.InputAwareWebView{b193667 VFEDHVC.. .F...... 0,0-2075,706} I/chromium(28987): [INFO:CONSOLE(0)] "Not allowed to navigate top frame to data URL: data:text/html;charset=utf-8,%3C!DOCTYPE%20html%3E%0A%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%20lang=%22%22%20xml:lang=%22%22%3E%0A%3Chead%3E%0A%20%20%3Cmeta%20charset=%22utf-8%22%20/%3E%0A%20%20%3Cmeta%20name=%22generator%22%20content=%22pandoc%22%20/%3E%0A%20%20%3Cmeta%20name=%22viewport%22%20content=%22width=device-width,%20initial-scale=1.0,%20user-scalable=yes%22%20/%3E%0A%20%20%3Ctitle%3E%20%3C/title%3E%0A%20%20%3Cstyle%3E%0A%20%20%20%20code%7Bwhite-space:%20pre-wrap;%7D%0A%20%20%20%20span....E4%BA%8E%3C/h1%3E%0A%3Ch3%20id=%22section%22%3E%3C/h3%3E%0A%3Ch2%20id=%22%E5%8A%A8%E5%90%AC-version-1.0%22%3E%E5%8A%A8%E5%90%AC%20version%201.0%3C/h2%3E%0A%3Cp%3ECopyright%C2%A9%202020%20NExT%20%E5%B7%A5%E4%BD%9C%E5%AE%A4%E7%BE%A4%3C/p%3E%0A%3Cp%3E%E7%89%88%E6%9D%83%E6%89%80%E6%9C%89%3C/p%3E%0A%3Cp%3E%E9%9A%90%E7%A7%81%E6%94%BF%E7%AD%96%3Cspan%20class=%22math%20inline%22%3E%C2%A0%C2%A0%C2%A0%C2%A0%3C/span%3E%E7%94%A8%E6%88%B7%E6%9C%8D%E5%8A%A1%E5%8D%8F%E8%AE%AE%3C/p%3E%0A%3C/body%3E%0A%3C/html%3E%0A#helpen", source: data:text/html;charset=utf-8,%3C!DOCTYPE%20html%3E%0A%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%20lang=%22%22%20xml:lang=%22%22%3E%0A%3Chead%3E%0A%20%20%3Cmeta%20charset=%22utf-8%22%20/%3E%0A%20%20%3Cmeta%20name=%22generator%22%20content=%22pandoc%22%20/%3E%0A%20%20%3Cmeta%20name=%22viewport%22%20content=%22width=device-width,%20initial-scale=1.0,%20user-scalable=yes%22%20/%3E%0A%20%20%3Ctitle%3E%20%3C/title%3E%0A%20%20%3Cstyle%3E%0A%20%20%20%20code%7Bwhite-space:%20pre-wrap;%7D%0A%20%20%20%20span.smallcaps%7Bfont-variant:%20small-caps;%7D%0A%20%20%20%20span.underline%7Btext-decoration:%20underline;%7D%0A%20%20%20%20div.column%7Bdisplay:%20inline-block;%20vertical-align:%20top;%20width:%2050%25;%7D%0A%20%20%20%20div.hanging-indent%7Bmargin-left:%201.5em;%20text-indent:%20-1.5em;%7D%0A%20%20%3C/style%3E%0A%20%20%3C!--%5Bif%20lt%20IE%209%5D%3E%0A%20%20%20%20%3Cscript%20src=%22//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js%22%3E%3C/script%3E%0A%20%20%3C!%5Bendif%5D--%3E%0A%20%20%3Cscript%20type=%22text/javascript%22%20src=%22help_lang.js%22%20type=%22module%22%3E%3C/script%3E%0A%20%20%3Cstyle%3E%0A%20%20/*%0A%20%20%20%20CAUTION:%20must%20use%20embedded%20style%20for%20standalone%20html%20with%20-s%0A%20%20*/%0A%20%20/***************%0A%20%20Global%0A%20%20***************/%0A%20%20/*%0A%20%20%20*%20I%20add%20this%20to%20html%20files%20generated%20with%20pandoc.%0A%20%20%20*/%0A%20%20html%20%7B%0A%20%20font-size:%20100%25;%0A%20%20overflow-y:%20scroll;%0A%20%20-webkit-text-size-adjust:%20100%25;%0A%20%20-ms-text-size-adjust:%20100%25;%0A%0A%20%20/*--scrollbarBG:%20%23CFD8DC;*/%0A%20%20--scrollbarBG:%20%232a2f39;%0A%20%20--thumbBG:%20%2390a4ae;%0A%20%20/*CAUTION:%20required%20for%20background%20parallax%20*/%0A%20%20height:%20100%25;%0A%20%20/*CAUTION%20end:%20required%20for%20background%20parallax%20*/%0A%20%20%7D%0A%20%20body%20%7B%0A%20%20color:%20%23e5e5e5;%0A%20%20font-family:%20Arial,%20Helvetica,%20sans-serif;%0A%20%20font-size:%2012px;%0A%20%20line-height:%201.7;%0A%20%20padding:%201em;%0A%20%20margin:%20auto;%0A%20%20max-width:%2042em;%0A%20%20/*CAUTION:%20required%20for%20background%20parallax%20*/%0A%20%20background:%20url('images/bg-logo.png')%20no-repeat%20top%20right,%20%232a2f39;%0A%20%20height:%20100%25;%0A%20%20background-attachment:%20fixed;%0A%20%20background-position:%20center;%0A%20%20background-repeat:%20no-repeat;%0A%20%20background-size:%20cover;%0A%20%20/*CAUTION%20end:%20required%20for%20background%20parallax*/%0A%20%20%7D%0A%20%20a%20%7B%0A%20%20color:%20%237c8694;%0A%20%20text-decoration:%20none;%0A%20%20%7D%0A%20%20a:visited%20%7B%0A%20%20color:%20%230072ae;%0A%20%20%7D%0A%20%20a:hover%20%7B%0A%20%20color:%20%2306e;%0A%20%20%7D%0A%20%20a:active%20%7B%0A%20%20color:%20%23faa700;%0A%20%20%7D%0A%20%20a:focus%20%7B%0A%20%20outline:%20thin%20dotted;%0A%20%20%7D%0A%20%20*::-moz-selection%20%7B%0A%20%20background:%20rgba(255,%20255,%200,%200.3);% E/InputMethodManager(28987): b/117267690: Failed to get fallback IMM with expected displayId=259 actual IMM#displayId=0 view=io.flutter.plugins.webviewflutter.InputAwareWebView{b193667 VFEDHVC.. .F...... 0,0-2075,706}
Verdict
So to me this is beyond the fuss of running an http server to work around a very simple webpage doc. It is actually a much more serious bug to me.
-
Alessandro over 3 yearsAny workaround? :(
-
kakyo over 3 yearsSorry, haven't found one. Looks like the web part of flutter has a long way to go.
- Place