Flutter中使用WebView加载本地Html文件

当前的WebView插件都是使用HTML的URL方式加载网页。因此我们有时需要把数据保存为文件,再使用WebView去加载本地网页。

流程变更为:网络请求数据 -> 组装成标准的HTML(一般是静态的)-> 写入本地设备html(包括通用的css文件和html文件) -> WebView加载html

Flutter WebView插件

flutter_webview_plugin:https://pub.dartlang.org/packages/flutter_webview_plugin

主要用来打开网站,非常方便,但需要指定相对于屏幕的固定的显示区域Rect。而在开发时往往不好计算得到十分精确的区域。

flutter_inappbrowser:https://github.com/pichillilorenzo/flutter_inappbrowser

应该算着flutter_webview_plugin的改进版,支持内嵌入页面。也需要固定的widget宽高。

Flutter WebView插件

自定义flutter_webview_plugin加载页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';

class FlutterWebView extends StatefulWidget {
String url;
final Rect webRect;
final bool withZoom;
final bool withLocalStorage;
final bool scrollBar;

FlutterWebView({Key key, this.url, this.webRect, this.withZoom, this.withLocalStorage, this.scrollBar}) : super(key: key);

@override
State<StatefulWidget> createState() => new FlutterWebViewPageState();
}

class FlutterWebViewPageState extends State<FlutterWebView> {
FlutterWebviewPlugin flutterWebviewPlugin;

@override
void initState() {
super.initState();
flutterWebviewPlugin = new FlutterWebviewPlugin();
//监听页面状态改变
flutterWebviewPlugin.onStateChanged.listen((WebViewStateChanged wvs) {
print(wvs.type);
});
//监听页面滚动事件
flutterWebviewPlugin.onScrollYChanged.listen((double offsetY) {
print('offsetY: $offsetY');
});
flutterWebviewPlugin.onScrollXChanged.listen((double offsetX) {
print('offsetX: $offsetX');
});

flutterWebviewPlugin.launch(
widget.url,
rect: widget.webRect,
withZoom: widget.withZoom,
withLocalStorage: widget.withLocalStorage,
scrollBar: widget.scrollBar
);
}

@override
Widget build(BuildContext context) {
return WebviewScaffold(
url: widget.url,
withZoom: false,
);
}
}

自定义flutter_inappbrowser加载页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import 'package:flutter/material.dart';
import 'package:flutter_inappbrowser/flutter_inappbrowser.dart';

class MyInAppWebView extends StatelessWidget {
String webUrl;
final Rect webRect;
InAppWebViewController webView;

MyInAppWebView({Key key, this.webUrl, this.webRect}) : super(key: key);

@override
Widget build(BuildContext context) {
InAppWebView webWidget = new InAppWebView(
initialUrl: webUrl,
initialHeaders: {},
initialOptions: {},
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
print("InAppWebView.onLoadStart: $url");
this.webUrl = url;
},
onProgressChanged: (InAppWebViewController controller, int progress) {
double prog = progress / 100;
print('InAppWebView.onProgressChanged: $prog');
});

return Container(
width: webRect.width,
height: webRect.height,
child: webWidget,
);
}
}

自定义加载本地Html的WebView

可分别通过FlutterWebView或MyInAppWebView方式加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_app/webview/flutter_web_view.dart';
import 'package:flutter_app/webview/inapp_web_view.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' as html;
import 'package:path_provider/path_provider.dart';

class NativeWebView extends StatefulWidget {
String body;
List<Widget> widgets;

NativeWebView(
{Key key, @required String this.body, List<Widget> this.widgets})
: super(key: key);

@override
NativeWebViewState createState() {
return NativeWebViewState();
}
}

class NativeWebViewState extends State<NativeWebView> {
final String htmlFileName = 'goods_description.html';
//final String cssFileName = 'goods_description.css';
final String cssFileName = '';
String _webUrl = '';
double top = 156.899;

@override
void initState() {
super.initState();
_createHtmlContent();
}

//先写入通用的CSS文件,再写入Html内容
void _createHtmlContent() async {
String headHtml;
if (cssFileName != null && cssFileName.length > 0) {
String cssUrl = (await _getLocalCssFile()).uri.toString();
headHtml = '''
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
<link rel="stylesheet" type="text/css" href="${cssUrl}" />
''';
} else {
headHtml = '''
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
''';
}
String newHtml = headHtml + widget.body;
dom.Document doc = html.parse(newHtml);
String htmlContent = doc.outerHtml;
//print('htmlContent: $htmlContent');
_writeDataFile(htmlContent);
}

void _checkCssFile() async {
File file = await _getLocalCssFile();
bool isExist = await file.exists();
int fileLength = isExist ? await file.length() : -1;
print('css file length: $fileLength');
if (!isExist || fileLength <= 0) {
if (isExist) {
await file.delete();
}
await file.create();
String cssStr = await DefaultAssetBundle.of(context)
.loadString('htmlsource/css/main.css');
print('css: $cssStr');
await file.writeAsString(cssStr);
}
}

//写入Html文件,并使用URL更新WebView
void _writeDataFile(String data) async {
if (cssFileName != null && cssFileName.length > 0) {
_checkCssFile();
}
File file = await _getLocalHtmlFile();
File afterFile = await file.writeAsString(data);
String webUrl = afterFile.uri.toString();
print('webUrl: $webUrl');
setState(() {
_webUrl = webUrl;
});
}

Future<File> _getLocalCssFile() async {
//获取本地文档目录
String dir = (await getApplicationDocumentsDirectory()).path;
//返回本地文件目录
return new File('$dir/$cssFileName');
}

Future<File> _getLocalHtmlFile() async {
//获取本地文档目录
String dir = (await getApplicationDocumentsDirectory()).path;
//返回本地文件目录
return new File('$dir/$htmlFileName');
}

@override
Widget build(BuildContext context) {
//return getInAppWebView();
return getFlutterWebView();
}

Widget getInAppWebView() {
return _webUrl.isNotEmpty
? MyInAppWebView(
webUrl: _webUrl,
webRect: Rect.fromLTWH(0.0, 0.0, MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height - AppBar().preferredSize.height
-MediaQuery.of(context).padding.top),
)
: new Container(
height: 300.0,
color: Colors.transparent,
);
}

Widget getFlutterWebView() {
return _webUrl.isNotEmpty
? new FlutterWebView(
url: _webUrl,
webRect: Rect.fromLTWH(0.0, top, MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height - top),
withZoom: false,
withLocalStorage: true,
scrollBar: false,
)
: new Container(
height: 300.0,
color: Colors.transparent,
);
}

Widget getHtmlView() {
return Container(
child: Html(
data: widget.body,
padding: EdgeInsets.all(8.0),
),
);
}
}

使用NativeWebView

1
2
3
NativeWebView(
body: body,
),

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :