Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Webviews in Flutter

I have a very complex Cordova app on Android that I have developed over a period of over a year. The app uses one custom, i.e written by me, Cordova plugin. A I consider a port of the app to iOS I am contemplating switching to Flutter in order to have just the one codebase to maintain - failing that the whole of the plugin will have to be rewritten for iOS.

With clean HTML5 + ES6 + CSS3 it works remarkably well and I see no reason to throw it all out and start all over again as a pure Flutter app. Enter WebViews. I am planning to use all of my current Cordova Webview code - essentially the whole of the Cordova project www folder - in a WebView in flutter. Key to this all is being able to efficiently communicate bidirectionally between the WebView and Flutter. To that end I have written up a small skeleton project. The main.dart file for that project is shown below

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
import 'package:webview_flutter/webview_flutter.dart';

void main() => runApp(Webby());

class Webby extends StatelessWidget 
{
 @override Widget build(BuildContext context) 
 {
  return MaterialApp
  (
   title: 'Flutter Demo',
   theme: ThemeData(primarySwatch: Colors.blue,),
   home: Feuille(),
  );
 }
}

class Feuille extends StatefulWidget
{
 Feuille({Key key}):super(key: key);
 @override  _FeuilleState createState() => _FeuilleState();
}

class _FeuilleState extends State<Feuille>
{
 WebViewController wVC; 
 @override Widget build(BuildContext context) 
 {
  SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);  
  return Scaffold
  (
   body:SafeArea
   (
    child:Column
    (
     children:<Widget>
     [
      Expanded
      (
       child:Container
       (
        margin:const EdgeInsets.all(0.0),
        child:WebView
        (
         debuggingEnabled:true, 
         initialUrl:'',
         javascriptMode:JavascriptMode.unrestricted,
         onWebViewCreated:registerController,
         javascriptChannels:Set.from
         ([JavascriptChannel(name:'JSBridge',onMessageReceived:handleMessage)]),
        ),),)])));
 }

 Future<void> handleMessage(JavascriptMessage message) async
 {
  print(message.message);
  wVC.evaluateJavascript("addNums(10,21)");
 }

 void registerController (WebViewController controller) async
 {
  wVC = controller;
  String html = await rootBundle.loadString('assets/index.html');
  wVC.loadUrl(Uri.dataFromString(html,mimeType:'text/html',
  encoding:Encoding.getByName('utf-8')).toString());
 }
}

The local HTML that is loaded into this webview is shown below

<!DOCTYPE html>
<html>
 <head>
  <meta name='viewport'
   content='user-scalable=no, initial-scale=1, maximum-scale=1, 
   minimum-scale=1, width=device-width, height=device-height' />
   <meta charset='utf-8' />
 </head>  
 <body>
  <p>The result is <span id="spnResult">Not Available Yet</span></p> 
 </body>
 <script>
   window.addEventListener("load",flutterReady);

   function flutterReady()
   {
    alert("Sending message");
    setTimeout(function(){JSBridge.postMessage("This is London calling");},5000);
   } 

   function addNums(n1,n2)
   {
    document.getElementById("spnResult").innerText = n1 + n2;
   }
  </script>
</html>

Finally the pubspec.yaml file for the project is as follows

name: webby
description: Webby Project

version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
 flutter:
  sdk: flutter

 cupertino_icons: ^0.1.2
 path_provider: ^1.4.0
 permission_handler: ^3.3.0
 connectivity: ^0.4.5+6
 webview_flutter: ^0.3.19+9

dev_dependencies:
 flutter_test:
 sdk: flutter


flutter:
 uses-material-design: true
assets:
  - assets/

Now my questions

  • I am using this skeleton project to test if I can communicate bidirectionally: from Webview hosted JS to Flutter and from Flutter back to Webview hosted JS. This works - very well. However, there are things I have done here quite blindly - this is my second day with Flutter - which need clarification

  • I have used the window.load event in HTML5 to know when I start communicating with Flutter. This is my replacement for the Cordova.docReady event. Is it reasonable to assume that when this event occurs Flutter will be "ready", the WebViewController will be availbale etc?

  • In my effort to get the WebView to occupy the entire device screen I have ended up using Scaffold...SafeAera...Expanded... quite simply because I ran into this being used to show a full screen image and it worked for me. However, it is not clear to me that this is the best way/right way to do things

  • I note that pubspec.yaml specifies the use of Material Design and the "Widget" being created here is a MaterialApp. Given that the whole of my UI is created and managed through HTML5 + CSS3 + ES6 in the WebView is there not a simpler way to create the widget that might entail some performance/memory footprint benefits

  • I have come across comments to the effect that WebView is slow, does not render well on iOS etc. In my experience people who make such comments typically tend to rely on "frameworks" that impose an unnecessary burden on the system. With clean HTML + ES6 that relies only on the HTML5 DOM as its "framework" performance has never been an issue to me. Is there any known reason why the Flutter WebView (which is ultimately the OS supplied WebView) should underperform?

  • And finally: Once I have got this nailed down I need to start implementing GeoLocation, Wifi network watching, accelerometer monitoring, websocket driven I/O, local file storage, SQLite database storage etc. Are the APIs provided by Fluter up to these tasks - and are they adequately OS agnostic to truly generate apps for both Android and iOS?

I'd be most grateful to anyone who might be able to provide some tips and pointers here.


I am not sure I am ever going to get a response here. However, a footnote for anyone running into this thread and attempting to copy what I have done thus far.

Quite simply - this will not work. Using Uri.dataFromString(... will allow you to show a static HTML document with embedded styles and JS. It will not be able to access stylesheets and scripts stored in other files/folders inside your Flutter assets folder. In order to do that you need to run a local HTTP server. While you could try to spin your own it isn't worth doing. Consider using the excellent, and well documented, flutter_inappwebview plugin instead.

like image 322
DroidOS Avatar asked Mar 19 '20 14:03

DroidOS


People also ask

How do I use WebView to Flutter?

With the WebView Flutter plugin you can add a WebView widget to your Android or iOS Flutter app. On iOS the WebView widget is backed by a WKWebView, while on Android the WebView widget is backed by a WebView. The plugin can render Flutter widgets over the web view.

How do I open URL in WebView in Flutter?

Android-only settings: forceWebView – If set to null or false , the URL is opened in the device's default browser; otherwise, the URL is launched in a WebView. enableJavaScript – If set to true , JavaScript is enabled in WebView.

How do you debug a Flutter in WebView?

Debugging Android WebViews On Android, in order to enable/disable debugging WebViews using chrome://inspect on Chrome, you should use the AndroidInAppWebViewController. setWebContentsDebuggingEnabled(bool debuggingEnabled) static method. For example, you could call it inside the main function: Copy to clipboard.


1 Answers

From what I understand, it seems that you'd just like to display your webpage using WebView on Flutter. If you need access to geolocation support on a WebView, webview_flutter has still this issue open. As a workaround, you may want to consider using flutter_inappwebview in the meantime. As for handling WebView callbacks on Flutter, here's an interesting guide that you can use as a start.

like image 75
Omatt Avatar answered Oct 10 '22 10:10

Omatt