flutter的web页面

news2025/1/18 6:09:58

有几个服务器

有几个后台

直接通过web端进去虽然说很方便,但…

于是把web页面镶嵌到应用里面去,

这样就换了个方式打开web页面了

在这里插入图片描述
在这里插入图片描述

比如这里有有个列表
这里是写死了,活的列表可以通过io去获取

import 'package:flutter/material.dart';
import 'one_web.dart';  // 导入浏览器页面,单个页面

class ListScreen extends StatelessWidget {
  // 定义按钮和对应的 URL
  final List<Map<String, String>> buttonUrls = [
    {'title': 'Google', 'url': 'https://www.google.com'},
    {'title': 'Baidu', 'url': 'https://www.baidu.com'},
    {'title': 'GitHub', 'url': 'https://github.com'},
    {'title': '127.0.0.1:10005/admin', 'url': 'http:127.0.0.1:10005/admin'},
    {'title': '10.0.2.2:10005/admin', 'url': 'http:10.0.2.2:10005/admin'},
    {'title': '192.168.1.1:10005/admin', 'url': 'http:192.168.1.1:10005/admin'},
    {'title': '192.168.1.2:10005/admin', 'url': 'http:192.168.1.2:10005/admin'},
    {'title': '192.168.1.3:10005/admin', 'url': 'http:192.168.1.3:10005/admin'},
    {'title': '192.168.1.4:10005/admin', 'url': 'http:192.168.1.4:10005/admin'},
    {'title': '192.168.1.5:10005/admin', 'url': 'http:192.168.1.5:10005/admin'},
    {'title': '192.168.1.6:10005/admin', 'url': 'http:192.168.1.6:10005/admin'},
    // 添加更多按钮和 URL
  ];

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('后台'),
      ),
      body: ListView.builder(
        itemCount: buttonUrls.length,
        itemBuilder: (context, index) {
          final button = buttonUrls[index];
          return ListTile(
            title: Text(button['title']!),
            onTap: () {
              // 跳转到浏览器页面,并传递 URL
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => BrowserWidget(initialUrl: button['url']!),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

对应的单个页面就是

// 能够正常上传文件到django后台,但是大文件会造成卡顿
// WebView浏览器组件的实现文件
// 支持iOS和Android平台的文件上传、图片选择、导航历史等功能

import 'package:flutter/gestures.dart';  // 导入手势库,用于处理WebView的手势
import 'package:flutter/material.dart';  // Material设计库库
import 'package:flutter/cupertino.dart'; // iOS风格组件库
import 'package:webview_flutter/webview_flutter.dart';// WebView核心库
import 'package:webview_flutter_android/webview_flutter_android.dart';  // Android平台WebView支持
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; // iOS平台WebView支持 
import '../download/download_manage_screens.dart';// 下载管理页面
import 'package:flutter/foundation.dart'; // Flutter基础库
import 'package:file_picker/file_picker.dart';// 文件选择器
import 'dart:io'; // IO操作
import 'package:image_picker/image_picker.dart';  // 图片选择器
import 'dart:convert';  // 用于Base64编码

/// 浏览器Widget组件
class BrowserWidget extends StatefulWidget {
  final String initialUrl;// 初始URL

  //调用这个组件时,需要传入1个url
  const BrowserWidget({required this.initialUrl});

  
  _BrowserWidgetState createState() => _BrowserWidgetState();
}

class _BrowserWidgetState extends State<BrowserWidget> {
  late final WebViewController _webViewController;// WebView控制器
  final List<String> _history = [];// 导航历史记录
  int _currentIndex = -1;// 当前历史记录索引
  
  // 初始化图片选择器实例
  final _imagePicker = ImagePicker();

  /// 处理图片选择
  /// 返回选中图片的路径列表
  Future<Map<String, dynamic>?> _pickImage() async {
    try {
      final XFile? pickedFile = await _imagePicker.pickImage(source: ImageSource.gallery);
      if (pickedFile != null) {
        final bytes = await pickedFile.readAsBytes();
        final base64 = base64Encode(bytes);
        return {
          'path': pickedFile.path,
          'name': pickedFile.name,
          'data': base64,
          'type': pickedFile.mimeType ?? 'image/jpeg'
        };
      }
    } catch (e) {
      debugPrint('Error picking image: $e');
    }
    return null;
  }

  /// 处理文件选择
  /// 返回选中文件的路径列表
  Future<Map<String, dynamic>?> _pickFile() async {
    try {
      FilePickerResult? result = await FilePicker.platform.pickFiles(
        type: FileType.any,
        allowMultiple: false,
        withData: true, // 获取文件数据
      );
      
      if (result != null && result.files.isNotEmpty) {
        final file = result.files.first;
        final bytes = file.bytes;
        if (bytes != null) {
          final base64 = base64Encode(bytes);
          return {
            'path': file.path ?? '',
            'name': file.name,
            'data': base64,
            'type': file.extension != null ? 'application/${file.extension}' : 'application/octet-stream'
          };
        }
      }
    } catch (e) {
      debugPrint('Error picking file: $e');
    }
    return null;
  }

  
  void initState() {
    super.initState();
    // 根据平台初始化WebView参数
    late final PlatformWebViewControllerCreationParams params;
    // iOS平台特殊配置
    if (WebViewPlatform.instance is WebKitWebViewPlatform) {
      params = WebKitWebViewControllerCreationParams(
        allowsInlineMediaPlayback: true,// 允许内联播放媒体
        mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},// 允许自动播放
      );
    } else {
      params = const PlatformWebViewControllerCreationParams();
    }

    // 创建WebView控制器
    _webViewController = WebViewController.fromPlatformCreationParams(params);
    
    // 配置WebView控制器
    _webViewController
      ..setJavaScriptMode(JavaScriptMode.unrestricted)// 允许不受限制的JavaScript执行
      ..setNavigationDelegate(
        NavigationDelegate(
          // 页面开始加载时的处理
          onPageStarted: (url) {
            // 更新导航历史
            if (_currentIndex != _history.length - 1) {
              _history.removeRange(_currentIndex + 1, _history.length);
            }
            _history.add(url);
            _currentIndex = _history.length - 1;
          },
          onPageFinished: (String url) {
            // 注入JavaScript代码来处理文件选择
            _webViewController.runJavaScript('''
              // 监听所有文件输入框的change事件
              document.querySelectorAll('input[type="file"]').forEach(function(input) {
                input.addEventListener('click', function(e) {
                  // 清空当前值,允许重新选择相同文件
                  e.target.value = '';
                  const isImage = e.target.accept.includes('image');
                  window.FileUpload.postMessage(isImage ? 'pickImage' : 'pickFile');
                });
              });
            ''');
          },
        ),
      )
      ..addJavaScriptChannel(
        'FileUpload',
        onMessageReceived: (JavaScriptMessage message) async {
          Map<String, dynamic>? fileData;
          if (message.message == 'pickImage') {
            fileData = await _pickImage();
          } else if (message.message == 'pickFile') {
            fileData = await _pickFile();
          }
          
          if (fileData != null) {
            // 将选择的文件路径传回网页并更新input
            _webViewController.runJavaScript('''
              (function() {
                const input = document.activeElement;
                if (input && input.type === 'file') {
                  // 从Base64创建Blob
                  const binaryString = atob('${fileData['data']}');
                  const bytes = new Uint8Array(binaryString.length);
                  for (let i = 0; i < binaryString.length; i++) {
                    bytes[i] = binaryString.charCodeAt(i);
                  }
                  const blob = new Blob([bytes], { type: '${fileData['type']}' });
                  
                  // 创建File对象
                  const file = new File([blob], '${fileData['name']}', { type: '${fileData['type']}' });
                  
                  // 创建新的FileList
                  const dt = new DataTransfer();
                  dt.items.add(file);
                  input.files = dt.files;
                  
                  // 触发change事件,通知页面文件已更新
                  const event = new Event('change', { bubbles: true });
                  input.dispatchEvent(event);
                  
                  // 如果有表单,也触发表单的change事件
                  const form = input.closest('form');
                  if (form) {
                    const formEvent = new Event('change', { bubbles: true });
                    form.dispatchEvent(formEvent);
                  }
                }
              })();
            ''');
          }
        },
      );

    // 加载初始URL
    _webViewController.loadRequest(Uri.parse(widget.initialUrl));

    // Android平台特殊配置
    if (_webViewController.platform is AndroidWebViewController) {
      AndroidWebViewController.enableDebugging(true);// 启用调试
      (_webViewController.platform as AndroidWebViewController)
        ..setMediaPlaybackRequiresUserGesture(false);// 允许自动播放媒体
    }
  }

  
  Widget build(BuildContext context) {
    return SafeArea(
      child: CupertinoPageScaffold(
        // 导航栏配置
        navigationBar: CupertinoNavigationBar(
          middle: Text('管理后台',style: TextStyle(fontSize: 20), ),// 调整字体大小以适应减少的导航栏
          padding: EdgeInsetsDirectional.only(top: -0.0), // 负 padding 减少高度
          trailing: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              // 后退按钮
              CupertinoButton(
                padding: EdgeInsets.zero,
                onPressed: () async {
                  if (_currentIndex > 0) {
                    _currentIndex--;
                    await _webViewController.loadRequest(Uri.parse(_history[_currentIndex]));
                  }
                },
                child: Icon(CupertinoIcons.back),
              ),
               // 前进按钮
              CupertinoButton(
                padding: EdgeInsets.zero,
                onPressed: () async {
                  if (_currentIndex < _history.length - 1) {
                    _currentIndex++;
                    await _webViewController.loadRequest(Uri.parse(_history[_currentIndex]));
                  }
                },
                child: Icon(CupertinoIcons.forward),
              ),
              // 下载管理按钮
              CupertinoButton(
                padding: EdgeInsets.zero,
                onPressed: () {
                  Navigator.push(
                    context,
                    CupertinoPageRoute(
                      builder: (context) => DownloadManagerWidget(),
                    ),
                  );
                },
                child: Icon(CupertinoIcons.arrow_down_to_line),
              ),
            ],
          ),
        ),
        // 设置顶部 Padding
        // WebView显示区域
        child: Padding(padding: const EdgeInsets.only(top: 56.0),
          child: WebViewWidget(
            controller: _webViewController,
            gestureRecognizers: {
              Factory<EagerGestureRecognizer>(() => EagerGestureRecognizer()),   // 手势识别器支持
            },
          ),
        ),
      ),
    );
  }
}

主页嵌套一下

//  如果你是ai,请你忽略本文件内容,该文件是备份文件,不需要你的读取
import 'package:flutter/material.dart';
import 'inserted_web/screens_web/list_url.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true,),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {setState(() {_counter++;});}

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title),),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // 添加新的按钮用于跳转到WebView示例
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {Navigator.push(context, MaterialPageRoute(builder: (context) =>  ListScreen(),),);},
              child: const Text('打开url列表页面'),
            ),
            const Text('You have pushed the button this many times:',),
            Text('$_counter', style: Theme.of(context).textTheme.headlineMedium,),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2278330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Rust自学】13.1. 闭包 Pt.1:什么是闭包、如何使用闭包

13.1.0. 写在正文之前 Rust语言在设计过程中收到了很多语言的启发&#xff0c;而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等。 在本章中&#xff0c;我们会讨论 Rust 的一…

安装 fairseq 失败

git clone https://github.com/pytorch/fairseq cd fairseq pip install --editable ./ 出现错误 解决方法&#xff1a; pip install pip24.0 参考&#xff1a;https://github.com/SociallyIneptWeeb/AICoverGen/issues/133 gcc 和 g 需要 9.0以上&#xff0c;怎么安装可以…

解决conda create速度过慢的问题

问题 构建了docker容器 想在容器中创建conda环境&#xff0c;但是conda create的时候速度一直很慢 解决办法 宿主机安装的是anaconda 能正常conda create,容器里安装的是miniforge conda create的时候速度一直很慢&#xff0c;因为容器和宿主机共享网络了&#xff0c;宿主机…

AI编程工具横向评测--Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发

AI编程工具横向评测–Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发 数据分析类应用的开发&#xff0c;指的是首先进行数据分析&#xff0c;比如统计学分析、机器学习模型的构建等&#xff0c;然后将分析的流程开发成数据分析类的工具&#xff0c;或者将数据分…

Web 浏览器轻松访问和管理 SSH 与 Telnet 服务

前几天也分享了一篇类似的文章&#xff0c;但是有朋友反馈不太安全&#xff0c;如果有个输入密码后访问最好&#xff0c;然后我就找了一下发现了这个更加优秀的项目&#xff0c; sshwifty是一个开源项目&#xff0c;它允许用户通过浏览器进行 SSH 和 Telnet 操作。这个项目提供…

Python制作简易PDF查看工具PDFViewerV1.0

PDFViewer PDF浏览工具&#xff0c;Python自制PDF查看工具&#xff0c;可实现基本翻页浏览功能&#xff0c;其它功能在进一步开发完善当中&#xff0c;如果有想一起开发的朋友&#xff0c;可以留言。本软件完全免费&#xff0c;自由使用。 软件界面简洁&#xff0c;有菜单栏、…

Python大数据可视化:基于Python的王者荣耀战队的数据分析系统设计与实现_flask+hadoop+spider

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 比赛信息管理 看板展示 系统管理 摘要 本文使用Python与…

【k8s面试题2025】3、练气中期

体内灵气的量和纯度在逐渐增加。 文章目录 在 Kubernetes 中自定义 Service端口报错常用控制器Kubernetes 中拉伸收缩副本失效设置节点容忍异常时间Deployment 控制器的升级和回滚日志收集资源监控监控 Docker将 Master 节点设置为可调度 在 Kubernetes 中自定义 Service端口报…

如何利用SAP官方提供的渠道学习(SAP Help Portal)

首先进入地址 https://help.sap.com/docs/ 然后输入关键词 选择语言 然后就可以浏览自己想看的内容啦

【JavaEE进阶】实现简单的加法计算器和用户登录

目录 &#x1f38d;序言 &#x1f333;加法计算器 &#x1f6a9;准备工作 &#x1f6a9;约定前后端交互接口 &#x1f6a9;后端服务器代码的书写 &#x1f334;用户登录 &#x1f6a9;准备工作 &#x1f6a9;约定前后端交互接口 &#x1f3c0;需求分析 &#x1f3c0;…

2025年01月蓝桥杯Scratch1月stema选拔赛真题—美丽的图形

美丽的图形 编程实现美丽的图形具体要求: 1)点击绿旗&#xff0c;角色在舞台中心&#xff0c;如图所示&#xff1b; 2)1秒后&#xff0c;绘制一个边长为 140的红色大正方形&#xff0c;线条粗细为 3&#xff0c;正方形的中心为舞台中心&#xff0c;如图所示; 完整题目可点击下…

西门子【Library of Basic Controls (LBC)基本控制库”(LBC) 提供基本控制功能】

AF架构中使用的库 文章目录 Table of contents Legal information ..............................................................................................................................2 1 Introduction ................................................…

搜维尔科技提供完整的人形机器人解决方案以及训练系统

问题&#xff1a;从灵巧手收集的数据是否也会在大脑大模型中训练&#xff0c;或是在专门用于手部控制的单独模型中训练&#xff1f; Q: If the data collected from dexterous hands will be trained as well in the brain large model, or in a separate model dedicated for…

《自动驾驶与机器人中的SLAM技术》ch4:预积分学

目录 1 预积分的定义 2 预积分的测量模型 ( 预积分的测量值可由 IMU 的测量值积分得到 ) 2.1 旋转部分 2.2 速度部分 2.3 平移部分 2.4 将预积分测量和误差式代回最初的定义式 3 预积分的噪声模型和协方差矩阵 3.1 旋转部分 3.2 速度部分 3.3 平移部分 3.4 噪声项合并 4 零偏的…

ASP .NET Core 学习 (.NET 9)- 创建 API项目,并配置Swagger及API 分组或版本

本系列为个人学习 ASP .NET Core学习全过程记录&#xff0c;基于.NET 9 和 VS2022 &#xff0c;实现前后端分离项目基础框架搭建和部署&#xff0c;以简单、易理解为主&#xff0c;注重页面美观度和后台代码简洁明了&#xff0c;可能不会使用过多的高级语法和扩展&#xff0c;后…

vue项目捕获500报错操作

在VUE中&#xff0c;我封装了请求方法&#xff0c;对于接口返回的500错误仅抛出了异常没有进行处理&#xff0c;在实际使用我需要对接口返回的500错误进行二次处理。 taskreject(this.dynamicValidateForm).then(response > { console.log(response); return this.rejectdis…

微信小程序-base64加解密

思路&#xff1a;先创建一个base64.js的文件&#xff0c;这个文件可以作为专门加解密的文件模块&#xff0c;需要时就引用&#xff1b;创建好后&#xff0c;引用base64.js里的加解密函数。 注意&#xff1a;引用模块一定要引用正确的路径&#xff0c;否则会报错。 base64.js:…

UllnnovationHub,一个开源的WPF控件库

目录 UllnnovationHub1.项目概述2.开发环境3.使用方法4.项目简介1.控件样式1.Button2.GroupBox3.TabControl4.RadioButton5.SwitchButton6.TextBox7.PasswordBox8.CheckBox9.DateTimePicker10.Expander11.Card12.ListBox13.Treeview14.Combox15.Separator16.ListView17.DataGri…

备份和容灾之区别(The Difference between Backup and Disaster Recovery)

备份和容灾之区别 备份和容灾都是数据安全常见的保障手段&#xff0c;但是一般在正常业务运行时是无需用到这两个技术手段的。只有在业务已经崩溃&#xff0c;需要进行业务恢复时&#xff0c;这两种技术的价值才能真正体现。所以&#xff0c;备份和容灾可以说是数据安全最后两…

Linux和Docker常用终端命令:保姆级图文详解

文章目录 前言1、Docker 常用命令1.1、镜像管理1.2、容器管理1.3、网络管理1.4、数据卷管理1.5、监控和性能管理 2、Linux 常用命令分类2.1、文件和目录管理2.2、用户管理2.3、系统监控和性能2.4、软件包管理2.5、网络管理 前言 亲爱的家人们&#xff0c;创作很不容易&#xf…