Flutter嵌入式浏览器插件webview_cef的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter嵌入式浏览器插件webview_cef的使用

简介

webview_cef 是一个基于Chromium Embedded Framework (CEF) 的Flutter桌面WebView插件。它支持Windows、macOS和Linux平台,适用于需要在Flutter应用中嵌入Web内容的场景。

支持的操作系统

  • ✅ Windows 7+
  • ✅ macOS 10.12+
  • ✅ Linux (x64 和 arm64)

设置步骤

Windows

在您的应用程序文件夹中,需要在 windows\runner\main.cpp 中添加一些代码:

// Introduce the source code of this plugin into your own project
#include "webview_cef/webview_cef_plugin_c_api.h"

int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
                      _In_ wchar_t *command_line, _In_ int show_command) {
  // Start cef deamon processes. MUST CALL FIRST
  initCEFProcesses();

  ::MSG msg;
  while (::GetMessage(&msg, nullptr, 0, 0)) {
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);

    // Add this line to enable cef keybord input, and enable to post messages to flutter engine thread from cef message loop thread.
    handleWndProcForCEF(msg.hwnd, msg.message, msg.wParam, msg.lParam);
  }
}

首次构建项目时,会自动下载预构建的cef bin包(200MB),因此第一次构建可能需要较长时间。

macOS

将仓库克隆到您的项目位置,并更新 pubspec.yaml 文件:

dependencies:
  # Webview
  webview_cef:
    path: ./packages/webview_cef     # Or wherever you cloned the repo

然后按照以下步骤操作:

  1. 下载预构建的cef捆绑包(根据您的目标机器架构选择arm64或intel版本)。
  2. 解压缩并将所有文件放入 macos/third/cef 目录(在克隆的仓库中,而不是您的项目中)。
  3. 运行示例应用程序。

Linux

只需在 pubspec.yaml 中添加 webview_cef 即可:

dependencies:
  webview_cef: ^latest_version

示例代码

以下是完整的示例代码,展示如何使用 webview_cef 插件:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_cef/webview_cef.dart';
import 'package:webview_cef/src/webview_inject_user_script.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late WebViewController _controller;
  final _textController = TextEditingController();
  String title = "";
  Map allCookies = {};

  @override
  void initState() {
    var injectUserScripts = InjectUserScripts();
    injectUserScripts.add(UserScript("console.log('injectScript_in_LoadStart')", ScriptInjectTime.LOAD_START));
    injectUserScripts.add(UserScript("console.log('injectScript_in_LoadEnd')", ScriptInjectTime.LOAD_END));

    _controller = WebviewManager().createWebView(
        loading: const Text("not initialized"),
        injectUserScripts: injectUserScripts);
    super.initState();
    initPlatformState();
  }

  @override
  void dispose() {
    _controller.dispose();
    WebviewManager().quit();
    super.dispose();
  }

  Future<void> initPlatformState() async {
    await WebviewManager().initialize(userAgent: "test/userAgent");
    String url = "www.baidu.com";
    _textController.text = url;

    _controller.setWebviewListener(WebviewEventsListener(
      onTitleChanged: (t) {
        setState(() {
          title = t;
        });
      },
      onUrlChanged: (url) {
        _textController.text = url;
        final Set<JavascriptChannel> jsChannels = {
          JavascriptChannel(
              name: 'Print',
              onMessageReceived: (JavascriptMessage message) {
                print(message.message);
                _controller.sendJavaScriptChannelCallBack(false, "{'code':'200','message':'print succeed!'}", message.callbackId, message.frameId);
              }),
        };
        _controller.setJavaScriptChannels(jsChannels);
        _controller.executeJavaScript("function abc(e){return 'abc:'+ e}");
        _controller.evaluateJavascript("abc('test')").then((value) => print(value));
      },
      onLoadStart: (controller, url) {
        print("onLoadStart => $url");
      },
      onLoadEnd: (controller, url) {
        print("onLoadEnd => $url");
      },
    ));

    await _controller.initialize(_textController.text);

    if (!mounted) return;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(useMaterial3: true),
      home: Scaffold(
          body: Column(
        children: [
          SizedBox(height: 20, child: Text(title)),
          Row(
            children: [
              SizedBox(height: 48, child: MaterialButton(onPressed: () { _controller.reload(); }, child: const Icon(Icons.refresh))),
              SizedBox(height: 48, child: MaterialButton(onPressed: () { _controller.goBack(); }, child: const Icon(Icons.arrow_left))),
              SizedBox(height: 48, child: MaterialButton(onPressed: () { _controller.goForward(); }, child: const Icon(Icons.arrow_right))),
              SizedBox(height: 48, child: MaterialButton(onPressed: () { _controller.openDevTools(); }, child: const Icon(Icons.developer_mode))),
              Expanded(child: TextField(controller: _textController, onSubmitted: (url) {
                _controller.loadUrl(url);
                WebviewManager().visitAllCookies().then((value) {
                  allCookies = Map.of(value);
                  if (url == "baidu.com") {
                    if (!allCookies.containsKey('.$url') || !Map.of(allCookies['.$url']).containsKey('test')) {
                      WebviewManager().setCookie(url, 'test', 'test123');
                    } else {
                      WebviewManager().deleteCookie(url, 'test');
                    }
                  }
                });
              })),
            ],
          ),
          Expanded(
              child: Row(
            children: [
              ValueListenableBuilder(
                valueListenable: _controller,
                builder: (context, value, child) {
                  return _controller.value ? Expanded(child: _controller.webviewWidget) : _controller.loadingWidget;
                },
              ),
            ],
          ))
        ],
      )),
    );
  }
}

截图

您可以参考以下截图来了解 webview_cef 在不同平台上的运行效果:

  • Windows: Windows Screenshot
  • macOS: macOS Screenshot
  • Linux: Linux Screenshot

通过以上步骤和示例代码,您可以在Flutter应用中集成并使用 webview_cef 插件。希望这些信息对您有所帮助!


更多关于Flutter嵌入式浏览器插件webview_cef的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter嵌入式浏览器插件webview_cef的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,使用webview_cef插件可以嵌入一个基于Chromium Embedded Framework (CEF) 的WebView组件。这对于需要在Flutter应用中展示网页内容或者嵌入网页应用的情况非常有用。以下是一个简单的代码示例,展示了如何在Flutter应用中使用webview_cef插件。

首先,确保你已经在pubspec.yaml文件中添加了webview_cef依赖:

dependencies:
  flutter:
    sdk: flutter
  webview_cef: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter应用中配置和使用webview_cef。以下是一个完整的示例,展示了如何嵌入WebView并加载一个URL。

主文件 main.dart

import 'package:flutter/material.dart';
import 'package:webview_cef/webview_cef.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter WebView CEF Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: WebViewPage(),
    );
  }
}

class WebViewPage extends StatefulWidget {
  @override
  _WebViewPageState createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  WebViewController? _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView CEF Demo'),
      ),
      body: WebView(
        initialUrl: 'https://www.example.com',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _controller = webViewController;
        },
        onPageFinished: (String url) {
          print('Page finished loading: $url');
        },
        geolocationEnabled: true,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          if (_controller != null) {
            _controller!.evaluateJavascript('alert("Hello from Flutter!");');
          }
        },
        tooltip: 'Execute JS',
        child: Icon(Icons.code),
      ),
    );
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }
}

平台配置

由于webview_cef是一个平台相关的插件,你可能需要在iOS和Android平台上进行一些额外的配置。以下是一些基本的配置步骤:

iOS

  1. ios/Runner/Info.plist中添加必要的权限配置,例如访问网络的权限。
  2. 确保在ios/Podfile中启用了CEF相关的配置(这通常由插件自动处理,但你可能需要检查)。

Android

  1. android/app/src/main/AndroidManifest.xml中添加必要的权限配置,例如访问网络的权限。
  2. 确保android/build.gradleandroid/app/build.gradle文件中的配置支持CEF的编译。

注意事项

  • 由于CEF是一个重量级的组件,它可能会增加应用的体积和启动时间。
  • 确保你遵循了CEF的许可协议。
  • 在实际项目中,你可能需要处理更多的WebView事件和配置,例如错误处理、导航控制等。

以上示例提供了一个基本的起点,你可以根据具体需求进行扩展和定制。

回到顶部