tip: 用Flutter实现通用框架功能,然后把业务功能用Vue去实现,可以解决公司移动开发资源紧张,但是每个业务组都备有H5开发的场景。
而H5开发又不需要管理麻烦的app上架,原生硬件调用等移动端知识。
所以最近使用Flutter_inappwebview
做了个app内置浏览器来实现这个功能。
下面说下实现方案:
引入Vue资源文件
用webview直接打开网页显然是体验比较卡的,所以需要把Vue做好的页面build成资源文件,打包到Flutter工程里。
在pubspec.yaml
配置文件引入资源文件。
assets:
- assets/
- assets/fonts/
- assets/css/
- assets/home/
- assets/js/
- assets/test/
如果这里我就引入了两个Vue的页面。一个test、一个home。
准备Flutter_inappwebview
Flutter_inappwebview官方案例里面用的例子使用的是最原始的方式,没有进度条, 随便页面状态发生变动都会重新rebuild webview。这显然不符合目前的Flutter开发理念。webview加载了网页后应该是固定而不需要重新build的。
当时用的Flutter_bloc框架来解决,(最近发现GetX框架更简便,这里推荐下)这里很简单的在页面头部加了个进度条,中间来了个load动画。
构建一个HPWebViewPage,核心代码
InAppWebView webviewInit(BuildContext context) {
HPWebViewBloc vbloc = BlocProvider.of<HPWebViewBloc>(context);
print("view init: ${this.viewInfo.url}");
return InAppWebView(
key: const Key("in_app_webview"),
initialUrlRequest: this.viewInfo.url.startsWith(HPWebViewConst.filePath)
? null
: URLRequest(url: Uri.parse(this.viewInfo.url)),
onWebViewCreated: (controller) {
if (jsHandler != null) {
jsHandler!(controller, context);
}
},
onLoadStart: (controller, uri) =>
vbloc.add(HPWebViewLoadStartEvent(controller, uri)),
onLoadError: (controller, uri, code, message) =>
vbloc.add(HPWebViewLoadErrorEvent(controller, uri, code, message)),
onLoadHttpError: (controller, uri, code, message) =>
vbloc.add(HPWebViewLoadErrorEvent(controller, uri, code, message)),
onLoadStop: (controller, uri) =>
vbloc.add(WebViewLoadStopEvent(controller, uri)),
onProgressChanged: (controller, progress) =>
vbloc.add(WebViewProgressEvent(controller, progress)),
initialUserScripts: this.injectJSList);
}
jsHandler、injectJSList 是由外部传入的。Flutter与H5交互的代码。这个是由不同的业务定义。
build 代码
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
fit: StackFit.expand,
children: [
webviewInit(context),
Positioned(
top: 0,
child: BlocBuilder<HPWebViewBloc, HPWebViewState>(
builder: (context, state) {
print(state);
if (state is HPWebViewLoadStartState) {
return Container(
child: LinearProgressIndicator(value: 0), height: 2);
}
if (state is HPWebViewProgressState) {
return Container(
child:
LinearProgressIndicator(value: state.progress / 100),
height: 2);
}
return Container(height: 0, width: 0);
},
),
left: 0,
right: 0),
Center(
child: BlocBuilder<HPWebViewBloc, HPWebViewState>(
builder: (context, state) {
if (state is HPWebViewLoadStartState ||
state is HPWebViewProgressState) {
return CircularProgressIndicator();
}
return Container(height: 0, width: 0);
},
),
)
],
);
}
打开Webview
打开webview需要注入交互的代码,不需要的话可以不加。
class WebViewUtil {
static void openWebView(WebViewModel viewInfo, BuildContext context) async {
String injectJS = await rootBundle.loadString("assets/files/inject.js");
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return HPWebViewPage(
viewInfo: viewInfo,
injectJSList: UnmodifiableListView<UserScript>([
UserScript(
source: injectJS,
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END),
]),
jsHandler: _addJSHandler);
}));
}
static void _addJSHandler(
InAppWebViewController controller, BuildContext context) {
controller.addJavaScriptHandler(
handlerName: JSHandlerConst.close,
callback: (_) {
Navigator.of(context).pop();
});
controller.addJavaScriptHandler(
handlerName: JSHandlerConst.openUrl,
callback: (args) {
var url = args[0]['url'];
var title = args[0]['title'];
var filterUrl = args[0]['filterurl'];
var filterTitle = args[0]['filtertitle'];
Navigator.of(context).pushNamed(HPWebViewPage.routeName,
arguments: WebViewModel(url,
title: title,
filterUrl: filterUrl,
filterTitle: filterTitle));
// bloc.add(JSHandlerOpenUrlEvent(args));
});
controller.addJavaScriptHandler(
handlerName: JSHandlerConst.back,
callback: (args) {
Navigator.of(context).pop();
});
}
}
inject.js 只要是定义一套window.js.handler接口
//侧滑返回
handler.back = function() {
window.flutter_inappwebview.callHandler('back');
}
目前的代码只能打开一个url,而不能打开本地的vue页面
void _openWebPage(WebViewModel viewInfo, BuildContext context) {
WebViewUtil.openWebView(viewInfo, context);
}
_openWebPage(
WebViewModel("https://github.com/wesin/hp_webview",
title: "github"),
context);
加载本地网页
加载本地网页需要在app里启动一个代理服务,当通过代理加载url时,拦截请求。返回H5资源文件。通过这种方式,我们可以加载从服务端下载过来的html文件,也可以加载已经打包在工程的html文件。
在main里启动本地代理服务,指定端口
final HPWebViewProxy localhostServer = new HPWebViewProxy(port: 8765);
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await localhostServer.start();
print(localhostServer.isRunning());
如此就可以打开本地网页了
ElevatedButton(
onPressed: () => _openWebPage(
WebViewModel("http://localhost:8765/home/"), context),
child: Text("打开本地网页")),
代理服务的代码和全部的代码都可以在我的github上看。 传送门
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!