自学内容网 自学内容网

flutter的web页面

有几个服务器

有几个后台

直接通过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),
      ),
    );
  }
}




原文地址:https://blog.csdn.net/weixin_47021806/article/details/145196480

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!