Flutter 中加载 web 内容的方式探索

进击的学霸...大约 3 分钟前端Flutter

Flutter 渲染主要通过通过三种方式,第一种是自带的原生 widget ,第二种是将 html 等转为 widget 再做渲染(类似 webf 等的自渲染框架),第三种是直接使用 webview 渲染。

Widget

因为是加载已有的业务内容,所以这种方式就不说了,而且也没啥可说的。

webf 框架

webf 官网open in new window

首先注意 webf 的版本和 flutter 的版本是相关的(此处使用 flutter 3.13.9webf 0.16.0),根据 webf 的版本选择相应版本的 flutter(反之同理) ,参照文档open in new window 中的步骤进行初始化,完整 demo (完整 demo)如下。

/*
 * Copyright (C) 2019-2022 The Kraken authors. All rights reserved.
 * Copyright (C) 2022-present The WebF authors. All rights reserved.
 */

import 'package:flutter/material.dart';
import 'package:webf/webf.dart';
import 'package:webf/devtools.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Kraken Browser',
      // theme: ThemeData.dark(),
      debugShowCheckedModeBanner: false,
      home: FirstPage(title: 'Landing Bay'),
    );
  }
}

class FirstPage extends StatefulWidget {
  const FirstPage({Key? key, required this.title}) : super(key: key);
  final String title;

  
  State<StatefulWidget> createState() {
    return FirstPageState();
  }
}

class FirstPageState extends State<FirstPage> {
  late WebFController controller;

  
  void didChangeDependencies() {
    super.didChangeDependencies();
    controller = WebFController(
      context,
      // devToolsService: ChromeDevToolsService(),
      viewportWidth: 200,
      viewportHeight: 200,
      onLoad: (controller) {
        print('onLoad');
      },
      onDOMContentLoaded: (controller) {
        print('onDOMContentLoaded');
      },
      onLoadError: (flutterErr, stactTrace) {
        print("onLoadError $flutterErr $stactTrace");
      },
      onJSError: (jserror) {
        print('onJSError $jserror');
      },
    );
    print('controller.preload');
    controller.preload(WebFBundle.fromUrl('http://127.0.0.1:8080/'));
  }

  
  void dispose() {
    super.dispose();
    controller.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) {
              return WebFDemo(controller: controller);
            }));
          },
          child: const Text('Open WebF Page'),
        ),
      ),
    );
  }
}

class WebFDemo extends StatelessWidget {
  final WebFController controller;

  WebFDemo({required this.controller});

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('WebF Demo'),
        ),
        body: Center(
          // Center is a layout widget. It takes a single child and positions it
          // in the middle of the parent.
          child: WebF(controller: controller),
        ));
  }
}

问题

  1. macos 上运行报错  [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: SocketException: Connection failed (OS Error: Operation not permitted, errno = 1), address = 100.65.182.70, port = 6000  ,原因是没有开启网络权限。在  macos/Runner/DebugProfile.entitlements 文件中添加以下代码即可
<key>com.apple.security.network.client</key>
<true/>
  1. pub.dev 上的 readme 不如文档中新,优先看官网文档

  2. web 项目需要是 vue-cli 创建的(示例步骤仔细读就好了,tips 也很重要

体验

新启的项目估计可以使用,老项目想直接接入估计有点麻烦,尝试稍复杂些的页面就会有很多不支持的 api 报错,具体可能还得看 case

webview

通过两个插件 window_manageropen in new windowdesktop_webview_windowopen in new window 配合使用,前者作用是管理窗口,主要作用是将 flutter 初始化的窗口隐藏,后者作用是管理 webview,包括加载、隐藏、展示等。通过 window_manager 配置窗口在启动时隐藏,然后初始化 webview 并加载 url ,只是 webview 的方法中没有提供 show和 hide 的方法,显示&隐藏只能通过 reload 和 close 方法

另外 webview 目前未暴露可以将状态栏去掉的配置。

也有尝试  flutter_inappwebviewopen in new window 插件,api 丰富,但是 webview 不支持 windows 平台;而且在主窗口隐藏,webview 也隐藏的情况下会自动退出应用

webview_cefopen in new window 支持两个平台,但是文档显示还在开发中

问题

desktop_webview_window 在 windows 上使用的是 webview2(基于chromium) ,macos 上使用的是 wkwebview ,和 electron 现有的chromium 略有差异,可能会有一些兼容问题

评论
  • 按正序
  • 按倒序
  • 按热度