likes
comments
collection
share

Flutter Webview与Vue结合的案例

作者站长头像
站长
· 阅读数 23

Flutter Webview与Vue结合的案例 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上看。 传送门

转载自:https://juejin.cn/post/6957977017157419045
评论
请登录