8

I have a Flutter project and I am using the webview_flutter package to create a Webview widget.

This is my main.dart file:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Communication Bridge',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Native - JS Communication Bridge'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  WebViewController _controller;
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(title: Text('Webview')),
      body: WebView(
        initialUrl: 'about:blank',
        javascriptMode: JavascriptMode.unrestricted,
        javascriptChannels: Set.from([
          JavascriptChannel(
              name: 'messageHandler',
              onMessageReceived: (JavascriptMessage message) {
                _scaffoldKey.currentState.showSnackBar(
                    SnackBar(
                        content: Text(
                            message.message)
                    )
                );
              })
        ]),
        onWebViewCreated: (WebViewController webviewController) {
          _controller = webviewController;
          _loadHtmlFromAssets();
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.arrow_upward),
        onPressed: () {
          _controller.evaluateJavascript('foo("message")');
        },
      ),
    );

  }

  _loadHtmlFromAssets() async {
    String file = await rootBundle.loadString('assets/index.html');
    _controller.loadUrl(Uri.dataFromString(
        file,
        mimeType: 'text/html',
        encoding: Encoding.getByName('utf-8')).toString());
  }

}

As you can see, I am loading a local html file into the webview (_loadHtmlFromAssets).

The html file has the following markup:

   <html>

    <head>
        <title>My Local HTML File</title>
    </head>

    <body>
        <script type="text/javascript">
            function foo(msg) {
               //Code
            }
        </script>
    </body>
</html>

When the html contains the javascript like the code above, I have no problems and am able to communicate from the application to the webview.

I want to have the javascript code in a separate file, and not inside the html file.

 <html>
    
        <head>
            <title>My Local HTML File</title>
        </head>
    
        <body>
            <script src="script.js"></script> // <-------
        </body>
    </html>

When I tried doing this, I get the following error (where it's trying to reference methods defined in the javascript file):

I/chromium(15188): [INFO:CONSOLE(1)] "Uncaught ReferenceError: foo is not defined

I have added the file in my pubspec.yaml under assets, so that is not the issue.

I have seen this question on SO, but the solution there does not help.

1 Answer 1

4

You can copy paste run 4 full code main.dart, index.html, script.js, style.css below
And add android:usesCleartextTraffic="true" in AndroidManifest.xml
You can use package https://pub.dev/packages/webview_flutter_plus
webview_flutter_plus is a powerful extension of webview_flutter. This package helps to load Local HTML, CSS and Javascript content from Assets or Strings. This inherits all features of webview_flutter with minor API changes.

You can directly download full example code from https://github.com/shah-xad/webview_flutter_plus and test it

code snippet

WebViewPlus(
      initialUrl: 'assets/index.html',
      onWebViewCreated: (controller) {
        this._controller = controller;
      },
      onPageFinished: (url) {
        _controller.getHeight().then((double height) {
          print("Height: " + height.toString());
          setState(() {
            _height = height;
          });
        });
      },
      javascriptMode: JavascriptMode.unrestricted,
    ),

setting of assets

enter image description here

working demo of example code

enter image description here

full code of main.dart

import 'package:flutter/material.dart';
import 'package:webview_flutter_plus/webview_flutter_plus.dart';

void main() {
  runApp(WebViewPlusExample());
}

class WebViewPlusExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: WebViewPlusExampleMainPage(),
    );
  }
}

class WebViewPlusExampleMainPage extends StatefulWidget {
  @override
  _WebViewPlusExampleMainPageState createState() =>
      _WebViewPlusExampleMainPageState();
}

class _WebViewPlusExampleMainPageState
    extends State<WebViewPlusExampleMainPage> {
  WebViewPlusController _controller;
  double _height = 1000;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('webview_flutter_plus Example'),
      ),
      body: ListView(
        children: [
          SizedBox(
            height: 500,
            child: WebViewPlus(
              initialUrl: 'assets/index.html',
              onWebViewCreated: (controller) {
                this._controller = controller;
              },
              onPageFinished: (url) {
                _controller.getHeight().then((double height) {
                  print("Height: " + height.toString());
                  setState(() {
                    _height = height;
                  });
                });
              },
              javascriptMode: JavascriptMode.unrestricted,
            ),
          )
        ],
      ),
    );
  }
}

full code of index.html

<!DOCTYPE HTML>
<html>
<head>
    <title>webview_flutter_plus</title>
    <link crossorigin="anonymous" href="style.css" rel="stylesheet">
</head>
<body>
<div id="testDiv">
    webview_flutter_plus is an extension of webview_flutter to load HTML, CSS and Javascript even from Assets or String.
    <br>
    <br>
    <br>
    Please tap the text to see Javascript execution.
</div>
<script src="script.js"></script>
</body>

full code of script.js

var testDiv = document.getElementById("testDiv");
testDiv.addEventListener('click', function f(e) {
    testDiv.setAttribute('style', 'background:red;')
    console.log("style changed");
})

full code of style.css

#testDiv {
    background: green;
    color: white;
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer. Are you saying the scenario I asked s not possible using the webview_flutter package?
There is actually a pull request github.com/flutter/plugins/pull/1247 , long time ago, still not merged.
I suggest you can use this extension if you have deadline. because it's inherits all features of webview_flutter with minor API changes

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.