fluttercommunity / flutter_webview_plugin

Community WebView Plugin - Allows Flutter to communicate with a native WebView.
https://pub.dev/packages/flutter_webview_plugin
Other
1.48k stars 930 forks source link

How to display custom html instead of URL #23

Open oexza opened 6 years ago

oexza commented 6 years ago

I want to display some custom html string instead of loading from a url. Is this possible today?

lejard-h commented 6 years ago

not possible yet, but a hack would be to create a server with dart:io in the app on localhost, like here, https://medium.com/@segaud.kevin/facebook-oauth-login-flow-with-flutter-9adb717c9f2e

zoechi commented 6 years ago
new WebviewScaffold(
              url: new Uri.dataFromString('<html><body>hello world</body></html>', mimeType: 'text/html').toString()

worked for me as well

zoechi commented 6 years ago

Loading data URIs seems quite limiting. I found it difficult to load other data on click

Any plans to support

https://developer.android.com/reference/android/webkit/WebView.html#loadDataWithBaseURL(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)

and the other similar methods?

lejard-h commented 6 years ago

yes I planned to do it,

I can't find time to work on the plugin at the moment and I also need to find an equivalent on iOS which is not my domain of expertise :/

lejard-h commented 6 years ago

I guess this is the equivalent function on iOS

https://developer.apple.com/documentation/uikit/uiwebview/1617941-loaddata?language=objc

zoechi commented 6 years ago

I understand. I haven't done native mobile development and not the time to dive into it right now, therefore depend on plugins of others ;-) I guess I'll figure out some workaround. Thanks a lot for your work on the plugin ๐Ÿ‘

Kleak commented 6 years ago

To create a server easily you have an example in the readme of this repo

vascoV commented 6 years ago

Hello guys, new Uri.dataFromString('<html><body><h1>hello world</h1></body></html>', mimeType: 'text/html').toString(); this example also worked for me. But I have another question how can I render a whole .html file in flutter? Fot example insted using this string <html><body><h1>hello world</h1></body></html> just load the whole file new Uri.dataFromString('assets/test.html', mimeType: 'text/html').toString();

rodydavis commented 6 years ago

@zoechi Your approach works!

new WebviewScaffold(
              url: new Uri.dataFromString('<html><body>hello world</body></html>', mimeType: 'text/html').toString()

Although it doesn't load any images from img=src from the network. Any ideas how to force load them?

zoechi commented 6 years ago

@AppleEducate Images work for me

I added parameters: { 'charset': 'utf-8' }, don't remember why or if it is related (or pass encoding like shown in https://github.com/dart-flitter/flutter_webview_plugin/issues/68#issuecomment-384696379)

rodydavis commented 6 years ago

Thanks!

MaskyS commented 6 years ago

@lejard-h , how to load images/data that are stored locally when launching a webview scaffold that as above?

zoechi commented 6 years ago

@MaskyS see the link in the 2nd comment

MaskyS commented 6 years ago

@zoechi I think I'm probably missing something, but that link leads to android documentation? I checked the flutter_webview_plugin documentation and code but there seem to be no implementation of loadData() or loadDataWithBaseUrl() Could you help me understand with an example or clarification?

zoechi commented 6 years ago

@MaskyS the 2nd comment, not my 2nd comment https://github.com/dart-flitter/flutter_webview_plugin/issues/23#issuecomment-354431595

MaskyS commented 6 years ago

@zoechi I had a look at that article; I tried it out and couldn't serve multiple files (I have html1.html, html2.html, etc along with their data folders.). Is it possible? am I missing something?

zoechi commented 6 years ago

@MaskyS just because it doesn't demonstrate how you can serve multiple files, doesn't mean it's not possible. Just serve different files based on the URL

MaskyS commented 6 years ago

Thank you will try that.

Sun3 commented 6 years ago

I would suggest on the iOS side to use Swift and not Objective C. Most new projects in iOS are written in Swift. Just my thoughts.

Chris1234567899 commented 6 years ago

Any news on this? I tried with creating a path and simple html file like this:

project
|
+--assets/index.html
|
+--lib
|
...

and referencing it in the pubspec.yaml under assets. I can see it in the build, so the file is there. But how do I call it correctly in the dart method? Tried all possibilities I could think of but none worked. Is this supposed to work already? Additionally I saw there is an option "withLocalUrl". Does this do anything already?

zoechi commented 6 years ago

@Chris1234567899 see the medium article linked in the 2nd comment or my comment just below the 2nd one.

ghost commented 6 years ago

You can also use golang as your web server running locally. This is essentially a plugin that exposes itself to the flutter dart code over a http based RPC.

I am honestly not sure of the workflow needed here for this Facebook workflow though . But I just wanted to add this comment in case it helps anyone

zoechi commented 6 years ago

@gedw99 serving HTTP from Dart is quite easy though. All you need is shipped with Flutter already (dart:io)

ghost commented 6 years ago

true.. I just wanted to rock the boat.. No i am just saying it can be useful if you need to also package a ton of other logic and database stuff.

On Fri, 15 Jun 2018 at 18:11 Gรผnter Zรถchbauer notifications@github.com wrote:

@gedw99 https://github.com/gedw99 serving HTTP from Dart is quite easy though. All you need is shipped with Flutter already (dart:io)

โ€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dart-flitter/flutter_webview_plugin/issues/23#issuecomment-397669312, or mute the thread https://github.com/notifications/unsubscribe-auth/ATuCwlb42QUW85c_91raX5HG_hf_LKeoks5t89ycgaJpZM4RO-SO .

mdanics commented 6 years ago

@zoechi your solution worked for me, but i am trying to get the webview a rectangle as to the documentation

final flutterWebviewPlugin = new FlutterWebviewPlugin();  

flutterWebviewPlugin.launch(url,
                  fullScreen: false,
                  rect: new Rect.fromLTWH(
                      0.0, 
                      0.0, 
                      MediaQuery.of(context).size.width, 
                      300.0));

But it's not working - just a white screen on my device. Any ideas?

zoechi commented 6 years ago

@mdanics sorry, no idea. Haven't tried that yet myself. It seems unrelated to this issue and perhaps better to create a new issue.

devxpy commented 6 years ago

You can now do this on android, using my fork.

Example-

(place test.html at android/app/src/main/assets)

WebviewScaffold(
  url: 'file:///android_asset/test.html',
  allowFileURLs: true,
),

Flutter doesn't give the path to it's assets, so this won't work with flutter's assets, only android's.

pull request - #122

devxpy commented 6 years ago

So I think I can actually make this work by using this feature of flutter.

I have tried it out for flutter_pdf_viewer and it works great. So might issue a pull request if I get some time.

lucidappstudio commented 6 years ago

got error java.lang.ClassCastException: android.webkit.WebView cannot be cast to com.flutter_webview_plugin.ObservableWebView for the following code

new WebviewScaffold(
              url: new Uri.dataFromString('<html><body>hello world</body></html>', mimeType: 'text/html').toString()
krmao commented 5 years ago

need optimized for the local html data string

I/flutter (23463): [response]
I/flutter (23463): -------------------------------------------------------->
I/flutter (23463): [data]={code: 1, msg: ่Žทๅ–ๆˆๅŠŸ, time: 1536571706, data: {id: 15, contentlist_id: 4, title: ๅปบ็ซ‹็™พไธ‡ไบบๅŸบๅ› ๆ•ฐๆฎๅบ“ๅ’Œๅฅๅบท็ฎก็†็ณป็ปŸ๏ผŒโ€œๅŒ็™พโ€ๅทฅ็จ‹ๅŠฉๅŠ›ๅฅๅบทไธญๅ›ฝ, desc: ๅฅๅบทๅทฅ็จ‹, image: http://ym.51xixi.me/uploads/20180831/d38192d4b4956f939aeacb85dbfaf1f2.jpg, weigh: 12, auth: admin, recommend: 0, top: 0, index: 0, on: 0, content: <p style="margin-top:12.8pt;text-align:justify;text-justify:inter-ideograph;
I/flutter (23463): line-height:18.0pt;mso-pagination:widow-orphan"><span lang="EN-US" style="font-family: Arial, sans-serif; color: rgb(51, 51, 51); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;">&nbsp; &nbsp; &nbsp; &nbsp;7</span><span style="font-family: ๅฎ‹ไฝ“; color: rgb(51, 51, 51); background-image: initial; background-position: initial; background-size: initial; background
I/flutter (23463): โ•โ•โ•ก EXCEPTION CAUGHT BY WIDGETS LIBRARY โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
I/flutter (23463): The following ArgumentError was thrown building Builder(dirty):
I/flutter (23463): Invalid argument(s): String contains invalid characters.
I/flutter (23463): 
I/flutter (23463): When the exception was thrown, this was the stack:
I/flutter (23463): #0      _UnicodeSubsetEncoder.convert (dart:convert/ascii.dart:95:9)
I/flutter (23463): #1      AsciiCodec.encode (dart:convert/ascii.dart:45:46)
I/flutter (23463): #2      new UriData.fromString (dart:core/uri.dart:3214:44)
I/flutter (23463): #3      new Uri.dataFromString (dart:core/uri.dart:304:24)
I/flutter (23463): #4      DetailPageState._body (file:///Users/maokangren/workspace/flutter_template/lib/detail/Detail.dart:101:142)
I/flutter (23463): #5      DetailPageState.build.<anonymous closure> (file:///Users/maokangren/workspace/flutter_template/lib/detail/Detail.dart:49:20)
I/flutter (23463): #6      Builder.build (package:flutter/src/widgets/basic.dart:5590:41)
I/flutter (23463): #7      StatelessElement.build (package:flutter/src/widgets/framework.dart:3741:28)
I/flutter (23463): #8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3688:15)
I/flutter (23463): #9      Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #10     StatelessElement.update (package:flutter/src/widgets/framework.dart:3748:5)
I/flutter (23463): #11     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #13     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #14     ProxyElement.update (package:flutter/src/widgets/framework.dart:3957:5)
I/flutter (23463): #15     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #16     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #17     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #18     ProxyElement.update (package:flutter/src/widgets/framework.dart:3957:5)
I/flutter (23463): #19     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #20     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4531:32)
I/flutter (23463): #21     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4921:17)
I/flutter (23463): #22     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #23     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #24     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #25     StatefulElement.update (package:flutter/src/widgets/framework.dart:3845:5)
I/flutter (23463): #26     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #27     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #28     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #29     ProxyElement.update (package:flutter/src/widgets/framework.dart:3957:5)
I/flutter (23463): #30     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #31     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #32     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #33     StatefulElement.update (package:flutter/src/widgets/framework.dart:3845:5)
I/flutter (23463): #34     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #35     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4813:14)
I/flutter (23463): #36     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #37     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #38     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #39     StatelessElement.update (package:flutter/src/widgets/framework.dart:3748:5)
I/flutter (23463): #40     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #41     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4813:14)
I/flutter (23463): #42     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #43     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #44     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #45     StatefulElement.update (package:flutter/src/widgets/framework.dart:3845:5)
I/flutter (23463): #46     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #47     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #48     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #49     StatefulElement.update (package:flutter/src/widgets/framework.dart:3845:5)
I/flutter (23463): #50     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #51     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #52     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #53     ProxyElement.update (package:flutter/src/widgets/framework.dart:3957:5)
I/flutter (23463): #54     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #55     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #56     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #57     ProxyElement.update (package:flutter/src/widgets/framework.dart:3957:5)
I/flutter (23463): #58     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #59     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #60     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #61     StatefulElement.update (package:flutter/src/widgets/framework.dart:3845:5)
I/flutter (23463): #62     Element.updateChild (package:flutter/src/widgets/framework.dart:2729:15)
I/flutter (23463): #63     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3699:16)
I/flutter (23463): #64     Element.rebuild (package:flutter/src/widgets/framework.dart:3541:5)
I/flutter (23463): #65     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2273:33)
I/flutter (23463): #66     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:653:20)
I/flutter (23463): #67     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:208:5)
I/flutter (23463): #68     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter (23463): #69     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter (23463): #70     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
I/flutter (23463): #71     _invoke (dart:ui/hooks.dart:128:13)
I/flutter (23463): #72     _drawFrame (dart:ui/hooks.dart:117:3)
awazgyawali commented 5 years ago

Was working on this, it would be great if you guys tested it out. https://github.com/flutter/plugins/pull/1137

iKK001 commented 5 years ago

@MaskyS: In my case, I was able to eliminate the error Invalid argumnent(s): String contains invalid characters by removing the following character: โ€™ (aprostrophe). Afterwards I was able to load a HTML-string into a WebView in Flutter...

zoechi commented 5 years ago

@iKK001 https://stackoverflow.com/questions/419718/html-code-for-an-apostrophe

Androidsignal commented 5 years ago

Having issue to display html string to normal string in flutter.. i was try all libs but not working suggest me new thank you......

vvlong-can commented 5 years ago

NSString* key = [_registrar lookupKeyForAsset:@"assets/xxx.html"]; NSURL* nsUrl = [[NSBundle mainBundle] URLForResource:key withExtension:nil]; [self.webview loadFileURL:nsUrl allowingReadAccessToURL:[NSURL URLWithString:@"file:///"]]; can solve this problem, but you should modify open source code for your url schema rule. (get _registrar from plugin entrance )

ref: https://github.com/flutter/plugins/pull/1247

neelu004 commented 5 years ago

Hi, I want to load the local Html file which is placed in my assets folder as "assets/file/privacy.html" this would be the path and I am using the same plugin and replacing the url as "assets/privacy.html" but still i am not able to view the html file content. Its showing the error as could not found the file. Please suggest some solution.

iKK001 commented 5 years ago

@neelu004: I saw that you use a subfolder (i.e. "assets/file/...") for your html file. The Flutter-webview plugin is very tedious if it comes to folder/subfolder/ locations. Try: 1. eliminate the subfolder and place it directly into "assets/privacy.html" instead. Or 2. make sure you also provide the subfolder in your URL when calling the webview. Also, if you have references to other html files or folders from within your html-file that the webview calls (normally index.html with references to other files and folders ... or in your case privacy.html), make sure you use a webserver together with the webview-plugin.

Here is what works in my case:

(Again: This server is only needed in the case you have references to files and folders from your root html you are trying to show in your webview). If you only show one single html-file (without references), then you don't need this Jaguar server...

But if you do have a webpage with references to other files and folders (all in your assets), then your webview-URL must be called with localhost such as: http://localhost:8080/fullPathName as can be seen below...

The webserver I set up at the very beginning (inside main())...

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// import 'dart:async';
import 'package:jaguar/jaguar.dart';
import 'package:jaguar_flutter_asset/jaguar_flutter_asset.dart';

void main() async {
  final server = Jaguar();
  server.addRoute(serveFlutterAssets());
  await server.serve(logRequests: false);
  // await server.serve(logRequests: true);
  // server.log.onRecord.listen((r) => print(r));
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]).then((_) {
    runApp(MyApp());
  });
}

If you want to navigate to your webview from somewhere:

  await navigateToWebView(context, 'file/privacy.html');

The navigation:

Future<String> navigateToWebView(
      BuildContext context, String pathName) async {
    return await Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => WebViewExample(pathName)));
  }

And here the webview implementation with the localhost-URL call:

(small detail: the comments are an alternative if you don't want the snackbar)

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

class WebViewExample extends StatelessWidget {
  final String pathName;

  WebViewExample(this.pathName);

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

  @override
  Widget build(BuildContext context) {
    final String serverURL = 'http://localhost:8080/' + this.pathName;
    final double screenWidth = MediaQuery.of(context).size.shortestSide;
    final double screenHeight = MediaQuery.of(context).size.longestSide;
    return Scaffold(
      appBar: MyAppBar(
        title: Text(
          'My Title',
          style: TextStyle(
              fontSize: 16.0, color: Colors.white, fontWeight: FontWeight.w500),
        ),
      ),
      // body: FutureBuilder<String>(
      //   future: loadAsset(),
      //   builder: (context, snapshot) {
      //     if (snapshot.hasData) {
      //       return WebView(
      //         initialUrl:
      //             Uri.dataFromString(snapshot.data, mimeType: 'text/html')
      //                 .toString(),
      //         javascriptMode: JavascriptMode.unrestricted,
      //         onWebViewCreated: (WebViewController webViewController) {
      //           _controller.complete(webViewController);
      //         },
      //         javascriptChannels: <JavascriptChannel>[
      //           _toasterJavascriptChannel(context),
      //         ].toSet(),
      //       );
      //     } else if (snapshot.hasError) {
      //       return Text("${snapshot.error}");
      //     }
      //     return CircularProgressIndicator();
      //   },
      // ),
      // We're using a Builder here so we have a context that is below the Scaffold
      // to allow calling Scaffold.of(context) so we can show a snackbar.
      body: Builder(builder: (BuildContext context) {
        return Container(
          color: Color.fromRGBO(
              0x3c, 0x3c, 0x3c, 1), // color for bottom part of iOS safe-area
          child: SafeArea(
            child: Stack(
              fit: StackFit.expand,
              alignment: AlignmentDirectional.topStart,
              children: <Widget>[
                Positioned(
                  left: 0.0,
                  top: 0.0,
                  width: screenWidth,
                  height: screenHeight,
                  child: Container(
                    child: WebView(
                      initialUrl: serverURL,
                      javascriptMode: JavascriptMode.unrestricted,
                      onWebViewCreated: (WebViewController webViewController) {
                        _controller.complete(webViewController);
                      },
                      javascriptChannels: <JavascriptChannel>[
                        _toasterJavascriptChannel(context),
                      ].toSet(),
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }),

      floatingActionButton: favoriteButton(),
    );
  }

  // Future<String> loadAsset() async {
  //   return await rootBundle.loadString('assets/web1/index.html');
  // }

  JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
        name: 'Toaster',
        onMessageReceived: (JavascriptMessage message) {
          Scaffold.of(context).showSnackBar(
            SnackBar(content: Text(message.message)),
          );
        });
  }

  Widget favoriteButton() {
    return FutureBuilder<WebViewController>(
        future: _controller.future,
        builder: (BuildContext context,
            AsyncSnapshot<WebViewController> controller) {
          if (controller.hasData) {
            return FloatingActionButton(
              onPressed: () async {
                final String url = await controller.data.currentUrl();
                Scaffold.of(context).showSnackBar(
                  SnackBar(content: Text('Favorited $url')),
                );
              },
              child: const Icon(Icons.favorite),
            );
          }
          return Container();
        });
  }
}

enum MenuOptions {
  showUserAgent,
  toast,
}

class SampleMenu extends StatelessWidget {
  SampleMenu(this.controller);
  final Future<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> controller) {
        return PopupMenuButton<MenuOptions>(
          onSelected: (MenuOptions value) {
            switch (value) {
              case MenuOptions.showUserAgent:
                _onShowUserAgent(controller.data, context);
                break;
              case MenuOptions.toast:
                Scaffold.of(context).showSnackBar(
                  SnackBar(
                    content: Text('You selected: $value'),
                  ),
                );
                break;
            }
          },
          itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
                PopupMenuItem<MenuOptions>(
                  value: MenuOptions.showUserAgent,
                  child: const Text('Show user agent'),
                  enabled: controller.hasData,
                ),
                const PopupMenuItem<MenuOptions>(
                  value: MenuOptions.toast,
                  child: Text('Make a toast'),
                ),
              ],
        );
      },
    );
  }

  void _onShowUserAgent(
      WebViewController controller, BuildContext context) async {
    // Send a message with the user agent string to the Toaster JavaScript channel we registered
    // with the WebView.
    controller.evaluateJavascript(
        'Toaster.postMessage("User Agent: " + navigator.userAgent);');
  }
}

class NavigationControls extends StatelessWidget {
  const NavigationControls(this._webViewControllerFuture)
      : assert(_webViewControllerFuture != null);

  final Future<WebViewController> _webViewControllerFuture;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: _webViewControllerFuture,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
        final bool webViewReady =
            snapshot.connectionState == ConnectionState.done;
        final WebViewController controller = snapshot.data;
        return Row(
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.arrow_back_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller.canGoBack()) {
                        controller.goBack();
                      } else {
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(content: Text("No back history item")),
                        );
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.arrow_forward_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller.canGoForward()) {
                        controller.goForward();
                      } else {
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(
                              content: Text("No forward history item")),
                        );
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.replay),
              onPressed: !webViewReady
                  ? null
                  : () {
                      controller.reload();
                    },
            ),
          ],
        );
      },
    );
  }
}

class MyAppBar extends PreferredSize {
  MyAppBar({Key key, Widget title})
      : super(
          key: key,
          preferredSize: Size.fromHeight(app_bar_height), // set AppBar height
          child: AppBar(
            elevation: 0.0, // represents shaddow of AppBar
            brightness: Brightness
                .dark, // makes statusbar shine in dark AppBar environment
            backgroundColor:
                Color.fromRGBO(0x3c, 0x3c, 0x3c, 1), // sets AppBar color
            title: title, // sets AppBar title
            // maybe other AppBar properties
          ),
        );
}
vvlong-can commented 5 years ago

You should change the pluginโ€™s origin code, because the plugin didnโ€™t use the correct API, you canโ€™t load local html by LoadRequest but LoadFile API, have a try~

ๅ‘่‡ชๆˆ‘็š„iPhone

------------------ Original ------------------ From: neelu004 notifications@github.com Date: Thu,Apr 18,2019 8:39 PM To: fluttercommunity/flutter_webview_plugin flutter_webview_plugin@noreply.github.com Cc: vvlong 741668847@qq.com, Comment comment@noreply.github.com Subject: Re: [fluttercommunity/flutter_webview_plugin] How to display custom html instead of URL (#23)

Hi, I want to load the local Html file which is placed in my assets folder as "assets/file/privacy.html" this would be the path and I am using the same plugin and replacing the url as "assets/privacy.html" but still i am not able to view the html file content. Its showing the error as could not found the file. Please suggest some solution.

โ€” You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

oms10 commented 5 years ago

@neelu004: I saw that you use a subfolder (i.e. "assets/file/...") for your html file. The Flutter-webview plugin is very tedious if it comes to folder/subfolder/ locations. Try: 1. eliminate the subfolder and place it directly into "assets/privacy.html" instead. Or 2. make sure you also provide the subfolder in your URL when calling the webview. Also, if you have references to other html files or folders from within your html-file that the webview calls (normally index.html with references to other files and folders ... or in your case privacy.html), make sure you use a webserver together with the webview-plugin.

Here is what works in my case:

(Again: This server is only needed in the case you have references to files and folders from your root html you are trying to show in your webview). If you only show one single html-file (without references), then you don't need this Jaguar server...

But if you do have a webpage with references to other files and folders (all in your assets), then your webview-URL must be called with localhost such as: http://localhost:8080/fullPathName as can be seen below...

The webserver I set up at the very beginning (inside main())...

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// import 'dart:async';
import 'package:jaguar/jaguar.dart';
import 'package:jaguar_flutter_asset/jaguar_flutter_asset.dart';

void main() async {
  final server = Jaguar();
  server.addRoute(serveFlutterAssets());
  await server.serve(logRequests: false);
  // await server.serve(logRequests: true);
  // server.log.onRecord.listen((r) => print(r));
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]).then((_) {
    runApp(MyApp());
  });
}

If you want to navigate to your webview from somewhere:

  await navigateToWebView(context, 'file/privacy.html');

The navigation:

Future<String> navigateToWebView(
      BuildContext context, String pathName) async {
    return await Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => WebViewExample(pathName)));
  }

And here the webview implementation with the localhost-URL call:

(small detail: the comments are an alternative if you don't want the snackbar)

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

class WebViewExample extends StatelessWidget {
  final String pathName;

  WebViewExample(this.pathName);

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

  @override
  Widget build(BuildContext context) {
    final String serverURL = 'http://localhost:8080/' + this.pathName;
    final double screenWidth = MediaQuery.of(context).size.shortestSide;
    final double screenHeight = MediaQuery.of(context).size.longestSide;
    return Scaffold(
      appBar: MyAppBar(
        title: Text(
          'My Title',
          style: TextStyle(
              fontSize: 16.0, color: Colors.white, fontWeight: FontWeight.w500),
        ),
      ),
      // body: FutureBuilder<String>(
      //   future: loadAsset(),
      //   builder: (context, snapshot) {
      //     if (snapshot.hasData) {
      //       return WebView(
      //         initialUrl:
      //             Uri.dataFromString(snapshot.data, mimeType: 'text/html')
      //                 .toString(),
      //         javascriptMode: JavascriptMode.unrestricted,
      //         onWebViewCreated: (WebViewController webViewController) {
      //           _controller.complete(webViewController);
      //         },
      //         javascriptChannels: <JavascriptChannel>[
      //           _toasterJavascriptChannel(context),
      //         ].toSet(),
      //       );
      //     } else if (snapshot.hasError) {
      //       return Text("${snapshot.error}");
      //     }
      //     return CircularProgressIndicator();
      //   },
      // ),
      // We're using a Builder here so we have a context that is below the Scaffold
      // to allow calling Scaffold.of(context) so we can show a snackbar.
      body: Builder(builder: (BuildContext context) {
        return Container(
          color: Color.fromRGBO(
              0x3c, 0x3c, 0x3c, 1), // color for bottom part of iOS safe-area
          child: SafeArea(
            child: Stack(
              fit: StackFit.expand,
              alignment: AlignmentDirectional.topStart,
              children: <Widget>[
                Positioned(
                  left: 0.0,
                  top: 0.0,
                  width: screenWidth,
                  height: screenHeight,
                  child: Container(
                    child: WebView(
                      initialUrl: serverURL,
                      javascriptMode: JavascriptMode.unrestricted,
                      onWebViewCreated: (WebViewController webViewController) {
                        _controller.complete(webViewController);
                      },
                      javascriptChannels: <JavascriptChannel>[
                        _toasterJavascriptChannel(context),
                      ].toSet(),
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }),

      floatingActionButton: favoriteButton(),
    );
  }

  // Future<String> loadAsset() async {
  //   return await rootBundle.loadString('assets/web1/index.html');
  // }

  JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
        name: 'Toaster',
        onMessageReceived: (JavascriptMessage message) {
          Scaffold.of(context).showSnackBar(
            SnackBar(content: Text(message.message)),
          );
        });
  }

  Widget favoriteButton() {
    return FutureBuilder<WebViewController>(
        future: _controller.future,
        builder: (BuildContext context,
            AsyncSnapshot<WebViewController> controller) {
          if (controller.hasData) {
            return FloatingActionButton(
              onPressed: () async {
                final String url = await controller.data.currentUrl();
                Scaffold.of(context).showSnackBar(
                  SnackBar(content: Text('Favorited $url')),
                );
              },
              child: const Icon(Icons.favorite),
            );
          }
          return Container();
        });
  }
}

enum MenuOptions {
  showUserAgent,
  toast,
}

class SampleMenu extends StatelessWidget {
  SampleMenu(this.controller);
  final Future<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> controller) {
        return PopupMenuButton<MenuOptions>(
          onSelected: (MenuOptions value) {
            switch (value) {
              case MenuOptions.showUserAgent:
                _onShowUserAgent(controller.data, context);
                break;
              case MenuOptions.toast:
                Scaffold.of(context).showSnackBar(
                  SnackBar(
                    content: Text('You selected: $value'),
                  ),
                );
                break;
            }
          },
          itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
                PopupMenuItem<MenuOptions>(
                  value: MenuOptions.showUserAgent,
                  child: const Text('Show user agent'),
                  enabled: controller.hasData,
                ),
                const PopupMenuItem<MenuOptions>(
                  value: MenuOptions.toast,
                  child: Text('Make a toast'),
                ),
              ],
        );
      },
    );
  }

  void _onShowUserAgent(
      WebViewController controller, BuildContext context) async {
    // Send a message with the user agent string to the Toaster JavaScript channel we registered
    // with the WebView.
    controller.evaluateJavascript(
        'Toaster.postMessage("User Agent: " + navigator.userAgent);');
  }
}

class NavigationControls extends StatelessWidget {
  const NavigationControls(this._webViewControllerFuture)
      : assert(_webViewControllerFuture != null);

  final Future<WebViewController> _webViewControllerFuture;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: _webViewControllerFuture,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
        final bool webViewReady =
            snapshot.connectionState == ConnectionState.done;
        final WebViewController controller = snapshot.data;
        return Row(
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.arrow_back_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller.canGoBack()) {
                        controller.goBack();
                      } else {
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(content: Text("No back history item")),
                        );
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.arrow_forward_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller.canGoForward()) {
                        controller.goForward();
                      } else {
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(
                              content: Text("No forward history item")),
                        );
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.replay),
              onPressed: !webViewReady
                  ? null
                  : () {
                      controller.reload();
                    },
            ),
          ],
        );
      },
    );
  }
}

class MyAppBar extends PreferredSize {
  MyAppBar({Key key, Widget title})
      : super(
          key: key,
          preferredSize: Size.fromHeight(app_bar_height), // set AppBar height
          child: AppBar(
            elevation: 0.0, // represents shaddow of AppBar
            brightness: Brightness
                .dark, // makes statusbar shine in dark AppBar environment
            backgroundColor:
                Color.fromRGBO(0x3c, 0x3c, 0x3c, 1), // sets AppBar color
            title: title, // sets AppBar title
            // maybe other AppBar properties
          ),
        );
}

You can provide the complete example of localhost. Jaguar. thank.

jazzbpn commented 4 years ago

got error java.lang.ClassCastException: android.webkit.WebView cannot be cast to com.flutter_webview_plugin.ObservableWebView for the following code

new WebviewScaffold(
              url: new Uri.dataFromString('<html><body>hello world</body></html>', mimeType: 'text/html').toString()

How to render image using HTML to create Flutter-iOS-App?

https://stackoverflow.com/questions/57702924/how-to-render-image-using-html-css-using-flutter-to-create-ios-app

Benqstanley commented 4 years ago
new WebviewScaffold(
              url: new Uri.dataFromString('<html><body>hello world</body></html>', mimeType: 'text/html').toString()

worked for me as well

I seem to be having trouble when the html is too large (maybe). It seems to only work half the time on iOS

dhinakaran-nithra commented 4 years ago

How to load custom font(ex: Baamini) in webviewScaffold..?

garshom commented 3 years ago

this worked for me: https://github.com/fluttercommunity/flutter_webview_plugin/issues/23#issuecomment-484595100