3

Background

Im using webview_flutter 3.0.0 in order to render a complex HTML string that I'm creating in real time. So its not content loaded from the web. I'm doing this as follows:

  final Completer<WebViewController> _controller =
  Completer<WebViewController>();
  late WebViewController _con;

  var finalThreadHTML;

  _loadHTML() async {

   finalThreadHTML = createComplexHTMLStringInRealTime();

    }

    _con.loadUrl(Uri.dataFromString(
        setThreadHTML(
            finalThreadHTML
        ),
        mimeType: 'text/html',
        encoding: Encoding.getByName('utf-8')
    ).toString());
  }

and then in my UI

     WebView(
      initialUrl: '',
      javascriptMode: JavascriptMode.unrestricted,
        javascriptChannels: <JavascriptChannel>[
          JavascriptChannel(
              name: 'MessageInvoker',
              onMessageReceived: (s) {
              }),
        ].toSet(),
      onWebViewCreated: (WebViewController webViewController) {
                      _con = webViewController;
                      _loadHTML();
                      },
      onProgress: (int progress) {
                            print("WebView is loading (progress : $progress%)");
                            },
      navigationDelegate: (NavigationRequest request) {
                            if (request.url.startsWith('https://www.youtube.com/')) {
                            print('blocking navigation to $request}');
                            return NavigationDecision.prevent;
                            }
                            print('allowing navigation to $request');
                            return NavigationDecision.navigate;
                            },
      onPageStarted: (String url) {
                            print('Page started loading: $url');
                            },
      onPageFinished: (String url) {
                            print('Page finished loading: $url');
                            },
      gestureNavigationEnabled: true,
    )

My realtime HTML string (createComplexHTMLStringInRealTime() )looks something like this:

String createComplexHTMLStringInRealTime(){
  return ('''
    <html>
      <head>
    
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      </head>
      
        <body style="height:100vh;">
              Testing some stuff
              <img src="/images/blankAvatar.jpg" />
              Testing more stuff
        </body>
      </html>
      
    ''');
}

My question

In my formed HTML string I want to include some images, which are local asset files as defined in pubspec.yaml

So I tried doing this:

<img src="/images/blankAvatar.jpg" />

but that did not load anything, then I tried

<img src="file:///images/blankAvatar.jpg" />

Still nothing.

Is there anyway to load local files inline with my generated text to load a local file into the webview?

Mark
  • 3,653
  • 10
  • 30
  • 62
  • did you try `flutter_inappwebview` package ? – GNassro Jan 01 '22 at 21:57
  • @GNassro No, I'd rather do this with the webview_flutter package alone, if its possible – Mark Jan 01 '22 at 22:06
  • did you solve this problem? – Karanveer Singh Apr 29 '22 at 05:48
  • @KaranveerSingh No sorry, I totally gave up on it because at the time I wrote this question, webview was still a work in progress and they were still building up that functionality. From reading through the GitHub comments, they were actively working on that. Im not sure its current status – Mark Apr 30 '22 at 14:07
  • @Mark Thanks for replying. I don't think it works. We ended up converting the web page into single html file with internal css and base64 images. – Karanveer Singh May 02 '22 at 05:39

4 Answers4

2

I use package asset_webview for my HTML5 game. It`s very simple code, but it is enough for my project. All images are loading without problems.

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

final URL = 'assets/html/index.html';

main() {
  runApp(MaterialApp(
      home: Padding(
          padding: EdgeInsets.only(top: 48.0),
          child: AssetWebview(
            initialUrl: "asset://local/${URL}",
          )
      )
  ));
}
Sergey Tokarev
  • 153
  • 1
  • 6
1

The local image in the img tag can be showed in webview_flutter, you need adjust the controller to load the (HTML) uri in scheme instead of uri in data.

_con.loadUrl(Uri(scheme: 'file', path: file.path).toString());

enter image description here

HaiN
  • 917
  • 11
  • 31
0

You can copy paste run full code and index.html below
You can use package https://pub.dev/packages/flutter_inappwebview
In working demo you can see flutter icon <img src="images/flutter-logo.svg" alt="flutter logo"> display correctly

Step 1: add android:usesCleartextTraffic="true" in AndroidManifest.xml
Step 2: pubspec.yaml setting

assets:
    - assets/images/
    - assets/

Step 3: Add files and image to assets folder

enter image description here

working demo

enter image description here

full code

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

InAppLocalhostServer localhostServer = new InAppLocalhostServer();

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await localhostServer.start();
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('InAppWebView Example'),
        ),
        body: Container(
            child: Column(children: <Widget>[
          Expanded(
            child: Container(
              child: InAppWebView(
                initialUrl: "http://localhost:8080/assets/index.html",
                initialHeaders: {},
                initialOptions: InAppWebViewGroupOptions(),
                onWebViewCreated: (InAppWebViewController controller) {},
                onLoadStart: (InAppWebViewController controller, String url) {},
                onLoadStop: (InAppWebViewController controller, String url) {},
              ),
            ),
          )
        ])),
      ),
    );
  }
}

index.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Flutter InAppWebView</title>
    <link rel="stylesheet" href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="css/style.css">
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <link rel="shortcut icon" href="favicon.ico">
</head>
<body class="text-center">
    <div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
        <header class="masthead mb-auto">
            <div class="inner">
                <h3 class="masthead-brand">Flutter InAppWebView</h3>
                <nav class="nav nav-masthead justify-content-center">
                    <a class="nav-link active" href="index.html">Home</a>
                    <a class="nav-link" href="page-1.html">Page 1</a>
                    <a class="nav-link" href="page-2.html">Page 2</a>
                </nav>
            </div>
        </header>

        <main role="main" class="inner cover">
            <h1 class="cover-heading">Inline WebView</h1>
            <img src="images/flutter-logo.svg" alt="flutter logo">
            <a href="index.html"><img src="images/flutter-logo.svg" alt="flutter logo"></a>
            <p class="lead">Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.</p>
        </main>

        <footer class="mastfoot mt-auto">
            <div class="inner">
                <p>Cover template for <a target="_blank" href="https://getbootstrap.com/">Bootstrap</a>, by <a href="https://twitter.com/mdo">@mdo</a>.</p>
                <p>Phone link example <a href="tel:1-408-555-5555">1-408-555-5555</a></p>
                <p>Email link example <a href="mailto:example@gmail.com">example@gmail.com</a></p>
            </div>
        </footer>
    </div>
    <script src="js/main.js"></script>
</body>
</html>

Got this Solution from a answer hope it will help you

0

With flutter_inappwebview it's easy to read HTML assets :



        import 'dart:async';
    import 'dart:io';
    import 'package:flutter/material.dart';
    import 'package:flutter_inappwebview/flutter_inappwebview.dart';
    import 'package:url_launcher/url_launcher.dart';
    
    Future main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      if (Platform.isAndroid) {
        await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
      }
    
      runApp(new LastTest());
    }
    
    class LastTest extends StatefulWidget {
      @override
      _LastTest createState() => new _LastTest();
    }
    
    class _LastTest extends State {
      final GlobalKey webViewKey = GlobalKey();
    
      InAppWebViewController? webViewController;
      InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
          crossPlatform: InAppWebViewOptions(
            useShouldOverrideUrlLoading: true,
            mediaPlaybackRequiresUserGesture: false,
          ),
          android: AndroidInAppWebViewOptions(
            useHybridComposition: true,
          ),
          ios: IOSInAppWebViewOptions(
            allowsInlineMediaPlayback: true,
          ));
    
      late PullToRefreshController pullToRefreshController;
      String url = "";
      double progress = 0;
      final urlController = TextEditingController();
    
      @override
      void initState() {
        super.initState();
    
        pullToRefreshController = PullToRefreshController(
          options: PullToRefreshOptions(
            color: Colors.blue,
          ),
          onRefresh: () async {
            if (Platform.isAndroid) {
              webViewController?.reload();
            } else if (Platform.isIOS) {
              webViewController?.loadUrl(
                  urlRequest: URLRequest(url: await webViewController?.getUrl()));
            }
          },
        );
      }
    
      @override
      void dispose() {
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
              appBar: AppBar(title: Text("Loading HTML Assets")),
              body: SafeArea(
                  child: Column(children: [
                TextField(
                  decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
                  controller: urlController,
                  keyboardType: TextInputType.url,
                  onSubmitted: (value) {
                    var url = Uri.parse(value);
                    if (url.scheme.isEmpty) {
                      url = Uri.parse("https://www.google.com/search?q=" + value);
                    }
                    webViewController?.loadUrl(urlRequest: URLRequest(url: url));
                  },
                ),
                Expanded(
                  child: Stack(
                    children: [
                      InAppWebView(
                        //
                        initialFile: "assets/www/index.html",
                        //
                        key: webViewKey,
                        initialOptions: options,
                        pullToRefreshController: pullToRefreshController,
                        onWebViewCreated: (controller) {
                          webViewController = controller;
                        },
                        onLoadStart: (controller, url) {
                          setState(() {
                            this.url = url.toString();
                            urlController.text = this.url;
                          });
                        },
                        androidOnPermissionRequest:
                            (controller, origin, resources) async {
                          return PermissionRequestResponse(
                              resources: resources,
                              action: PermissionRequestResponseAction.GRANT);
                        },
    
                        shouldOverrideUrlLoading:
                            (controller, navigationAction) async {
                          var uri = navigationAction.request.url!;
    
                          if (![
                            "http",
                            "https",
                            "file",
                            "chrome",
                            "data",
                            "javascript",
                            "about"
                          ].contains(uri.scheme)) {
                            if (await canLaunchUrl(uri)) {
                              // Launch the App
                              await launchUrl(uri);
                              // and cancel the request
                              return NavigationActionPolicy.CANCEL;
                            }
                          }
                          return NavigationActionPolicy.ALLOW;
                        },
    
                        onLoadStop: (controller, url) async {
                          pullToRefreshController.endRefreshing();
                          setState(() {
                            this.url = url.toString();
                            urlController.text = this.url;
                          });
                        },
                        onLoadError: (controller, url, code, message) {
                          pullToRefreshController.endRefreshing();
                        },
                        onProgressChanged: (controller, progress) {
                          if (progress == 100) {
                            pullToRefreshController.endRefreshing();
                          }
                          setState(() {
                            this.progress = progress / 100;
                            urlController.text = this.url;
                          });
                        },
                        onUpdateVisitedHistory: (controller, url, androidIsReload) {
                          setState(() {
                            this.url = url.toString();
                            urlController.text = this.url;
                          });
                        },
                        onConsoleMessage: (controller, consoleMessage) {
                          print(consoleMessage);
                        },
                      ),
                      progress [
                    ElevatedButton(
                      child: Icon(Icons.arrow_back),
                      onPressed: () {
                        webViewController?.goBack();
                      },
                    ),
                    ElevatedButton(
                      child: Icon(Icons.arrow_forward),
                      onPressed: () {
                        webViewController?.goForward();
                      },
                    ),
                    ElevatedButton(
                      child: Icon(Icons.refresh),
                      onPressed: () {
                        webViewController?.reload();
                      },
                    ),
                  ],
                ),
              ]))),
        );
      }
    }