Flutter在web项目中使用iframe

需要把原来的app项目移植到web上面,在app中使用的是flutter_inappwebview这个库,推荐使用这个库,因为修复了一部分webview_flutter中存在的问题

在web项目中flutter_inappwebview这个库不支持,所以需要自己封装一个web项目中的webview也就是使用iframe

废话少说上代码

dart 复制代码
import 'dart:html';
import 'dart:js' as js;

import 'dart:ui_web';

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:myapp/app/services/screen_adapter.dart';

Widget buildWebViewWidget(String url,
    {Function(int id)? onPlatformViewCreated}) {

  /// 给js调用的函数
  void back(){
    Get.back();
  }

  var platformViewRegistry = PlatformViewRegistry();
  //注册
  platformViewRegistry.registerViewFactory('iframe-webview', (_) {
    DivElement _mainDiv = DivElement()
      ..style.width = '100%'
      ..style.height = '100%'
      ..style.position = 'relative';

    IFrameElement _iFameElement = IFrameElement()
      ..style.height = '100%'
      ..style.width = '100%'
      ..src = url
      ..style.border = 'none';

    ScriptElement scriptElement = ScriptElement();
    var script = """
          
           // 对话框js
          window.confirm = function(message, yesCallback, noCallback){
             var message = message;
             var choose = function(tag){
                return document.querySelector(tag);
             }
             choose('.dialog-message').innerHtml = message;
             choose(".wrap-dialog").className = "wrap-dialog";
             choose("#dialog").οnclick= function(e){
                if(e.target.className=="dialog-btn"){
                     choose(".wrap-dialog").className = "wrap-dialog dialog-hide";
                     yesCallback();
                 }else if (e.target.className=="dialog-btn dialog-ml50"){
                     choose(".wrap-dialog").className = "wrap-dialog dialog-hide";
                      noCallback();
                 }
             };
          }
    
           // 返回按钮功能
           var drag = document.getElementById("floatBtn");
           var gapWidth = ${ScreenAdapter.width(5)}
           var itemWidth = ${ScreenAdapter.width(80)}
           var itemHeight =  ${ScreenAdapter.width(80)}
           var clientWidth = document.documentElement.clientWidth
           var clientHeight = document.documentElement.clientHeight
           var newLeft = 0
           var newTop = 0
           
            drag.addEventListener('click',function(e){
              //e.stopPropagation();
              var r = confirm('确定返回首页吗?', function(){
                 window.back();
              }, function(){})
            })
          
            drag.addEventListener('touchstart', function(e){
              // e.preventDefault();
              //e.stopPropagation();
              drag.style.transition = 'none';
            })
          
            drag.addEventListener('touchmove', function(e){
              // e.preventDefault();
             // e.stopPropagation();
              if (e.targetTouches.length === 1) {
                   let touch = e.targetTouches[0]
                   newLeft = touch.clientX - itemWidth / 2;
                   newTop = touch.clientY - itemHeight / 2;
                   if(newLeft  < 0){
                      newLeft = 0
                   } 
                   if(newLeft > clientWidth - itemWidth - gapWidth){
                      newLeft > clientWidth - itemWidth - gapWidth
                   }
                   if(newTop < 0){
                      newTop=0;
                   }
                   if(newTop > clientHeight - itemHeight - gapWidth){
                     newTop = clientHeight - itemHeight - gapWidth
                   }
                   drag.style.left = newLeft + 'px'
                   drag.style.top = newTop + 'px'
              }
            })
            drag.addEventListener('touchend', function (e) {
                 // e.preventDefault()
                 //e.stopPropagation();
                drag.style.transition = 'all 0.3s'
                if (newLeft > clientWidth / 2) {
                    newLeft = clientWidth - itemWidth - gapWidth
                } else {
                    newLeft = gapWidth
                }
                drag.style.left = newLeft + 'px'
            })   
    """;



    scriptElement.innerHtml = script;

    /// 返回按钮div
    DivElement _div = DivElement()
      ..id = 'floatBtn'
      ..draggable = true
      ..style.width =  '${ScreenAdapter.width(80)}px'
      ..style.height =  '${ScreenAdapter.width(80)}px'
      // ..style.backgroundColor = 'red'
      ..style.backgroundImage = 'url(assets/assets/images/fanhui.png)'
      ..style.backgroundSize = 'cover'
      ..style.position = 'absolute'
      ..style.left = '${ScreenAdapter.width(5)}px'
      ..style.top = '0'
      ..style.transition = 'all 0.3s'
      ..style.zIndex = '9999';


    // 对话框样式
    StyleElement _style = StyleElement();
    _style.innerHtml = """
    html,
    body {
        margin: 0;
        padding: 0;
        font-family: "Microsoft YaHei";
    }

    .wrap-dialog {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        font-size: 16px;
        text-align: center;
        background-color: rgba(0, 0, 0, .4);
        z-index: 10000;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .dialog {
        position: relative;
        margin: 10% auto;
        width: 300px;
        background-color: #FFFFFF;
    }

    .dialog .dialog-header {
        height: 20px;
        padding: 10px;
        background-color: #22b9ff;
    }

    .dialog .dialog-body {
        height: 30px;
        padding: 20px;
    }

    .dialog .dialog-footer {
        padding: 8px;
        background-color: #f5f5f5;
    }

    .dialog-btn {
        width: 70px;
        padding: 2px;
        cursor: pointer;
    }

    .dialog-hide {
        display: none;
    }

    .dialog-ml50 {
        margin-left: 50px;
    }
    """;
    _mainDiv.append(_style);

    // 提示信息框dom
    DivElement _dialogDiv = DivElement();
    _dialogDiv.innerHtml = """
    <div class="wrap-dialog dialog-hide" >
        <div class="dialog" id="dialog">
            <div class="dialog-header">
                <span class="dialog-title">提示</span>
            </div>
            <div class="dialog-body">
                <span class="dialog-message">确定要返回首页吗?</span>
            </div>
            <div class="dialog-footer">
                <input type="button" class="dialog-btn" id="dialog-confirm" value="确认" />
                <input type="button" class="dialog-btn dialog-ml50" id="dialog-cancel" value="取消" />
            </div>
        </div>
    </div>
    """;

    _mainDiv.append(scriptElement);
    _mainDiv.append(_dialogDiv);
    _mainDiv.append(_div);
    _mainDiv.append(_iFameElement);


    return _mainDiv;
  });

  /// 注册返回函数
  js.context['back'] = back;

  return SizedBox(
    width: double.infinity,
    height: double.infinity,
    child: HtmlElementView(
      viewType: 'iframe-webview',
      onPlatformViewCreated: (int id) {
        onPlatformViewCreated?.call(id);
      },
    ),
  );
}

使用方法

直接判断是否是web,GetPlatform.isWeb ,如果是则使用上面封装的,不是则调用其他平台的

dart 复制代码
if(GetPlatform.isWeb)
   buildWebViewWidgetPlatform(controller.url,
       onPlatformViewCreated: (id) {
           controller.isLoading.value = false;
   })

参考网址

https://juejin.cn/post/7294638699417042954

相关推荐
爱上大树的小猪36 分钟前
【前端】Electron入门开发教程,从介绍Electron到基础引用以及部分深度使用,附带常见的十个报错问题的解决方案和代码优化。
前端·javascript·electron
呦呦鹿鸣Rzh1 小时前
实现标题-超链接
java·前端·javascript
网络点点滴2 小时前
声明式和函数式 JavaScript 原则
开发语言·前端·javascript
禁默2 小时前
【学术会议-第五届机械设计与仿真国际学术会议(MDS 2025) 】前端开发:技术与艺术的完美融合
前端·论文·学术
binnnngo2 小时前
2.体验vue
前端·javascript·vue.js
LCG元2 小时前
Vue.js组件开发-实现多个文件附件压缩下载
前端·javascript·vue.js
索然无味io2 小时前
组件框架漏洞
前端·笔记·学习·安全·web安全·网络安全·前端框架
╰つ゛木槿2 小时前
深入探索 Vue 3 Markdown 编辑器:高级功能与实现
前端·vue.js·编辑器
yqcoder3 小时前
Commander 一款命令行自定义命令依赖
前端·javascript·arcgis·node.js
前端Hardy3 小时前
HTML&CSS :下雪了
前端·javascript·css·html·交互