【flutter封装图片/视频选择控件】

在这里插入图片描述在这里插入图片描述
引入库 wechat_assets_picker: ^6.0.5video_player: ^2.5.1 # 视频播放 flutter_screenutil: ^5.7.0

import 'dart:async';
import 'dart:io';
import 'package:generated/l10n.dart';
import 'package:jade/configs/PathConfig.dart';
import 'package:jade/customWidget/addImageVideoBtn.dart';
import 'package:jade/utils/DialogUtils.dart';
import 'package:jade/utils/JadeColors.dart';
import 'package:jade/utils/Utils.dart';
import 'package:util/easy_loading_util.dart';
import 'package:util/permission_util.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:video_player/video_player.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';class SelectFileData {File file;int type; // 1:image  2:video 3:audio  default:otherSelectFileData({this.file, this.type});
}/*
* 图片/视频选择
* 只能选择一条视频,选择多条视频未完善所以存在选多条视频时每条视频都相同的bug
* */
class SelectImageVideo extends StatefulWidget {String title;String desc;String postscript;int maxLength; //最大选择数量RequestType requestType;bool discrete; //是否分离单独选择(只能选图片或视频)bool showExample; //是否显示查看示例按钮Color bgColor; //按钮背景颜色Function selectBack;SelectImageVideo({this.title, this.desc,this.postscript, this.maxLength, this.requestType, this.discrete = false,this.showExample = false, this.bgColor,this.selectBack});State<StatefulWidget> createState() {// TODO: implement createStatereturn _SelectImageVideo();}
}class _SelectImageVideo extends State<SelectImageVideo> {List<SelectFileData> _selectFileList = [];List<File> _backFileList = [];VideoPlayerController _videoPlayerController;Widget build(BuildContext context) {// TODO: implement buildreturn Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Text.rich(TextSpan(children: [TextSpan(text: widget.title,style: TextStyle(color: JadeColors.grey_2, fontSize: 30.sp, fontWeight: FontWeight.w600)),if(widget.postscript != null)TextSpan(text: widget.postscript,style: TextStyle(color: JadeColors.grey, fontSize: 24.sp, fontWeight: FontWeight.w600)),])),if (widget.desc != null)Container(margin: EdgeInsets.only(top: 10.w),child: Text(widget.desc, style: TextStyle(color: JadeColors.grey, fontSize: 24.sp))),SizedBox(height: 30.w),Row(mainAxisAlignment: MainAxisAlignment.start,children: [if (widget.showExample)GestureDetector(child: Container(margin: EdgeInsets.only(right: 20.w),child: Stack(alignment: Alignment.center,children: [ClipRRect(borderRadius: BorderRadius.circular(8),child: Image.asset(PathConfig.imageExperienceExample,fit: BoxFit.fill, width: 220.w, height: 220.w),),Container(width: 220.w,height: 220.w,decoration: BoxDecoration(color: Colors.black45, borderRadius: BorderRadius.circular(8)),),Text('点击查看示例', style: TextStyle(color: Colors.white, fontSize: 28.sp))],)),onTap: () {Utils().hideKeyboard(context);DialogUtils().experienceStationRealisticImagesDialog(title: '实景图示例',desc: '需拍摄清晰格口照片,并参照线上体验秀格口序号,在图片对应位置标注对应序号。',imageUrl: PathConfig.httpExperienceRealisticImages);},),Expanded(child: SizedBox(height: 220.w,child: ListView.separated(scrollDirection: Axis.horizontal,itemBuilder: (context, index) {if (_selectFileList.length < widget.maxLength && index == _selectFileList.length) {return GestureDetector(child: addImageVideoBtn(widget.requestType == RequestType.video? '添加视频': widget.requestType == RequestType.image? '添加图片': widget.requestType == RequestType.common? '添加图片/视频': '添加图片/视频/音频',widget.bgColor ?? JadeColors.grey_5),onTap: () async {Utils().hideKeyboard(context);bool _isAuth = await PermissionUtil.isAuthStorage();if (!_isAuth) {WidgetsBinding.instance.addPostFrameCallback((_) {DialogUtils().showGeneralDialogFunction(context, '存储权限', '用于上传照片、视频等场景', notClose: true);Future.delayed(Duration(seconds: 5), () {Navigator.of(context).pop();});});}if(widget.discrete){_openImageOrVideoSelect(index);}else{_callSelectImageVideo(index);}_backFileCall();});}return Stack(alignment: Alignment.topRight,children: [Container(height: 220.w,width: 220.w,decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),child: ClipRRect(//是ClipRRect,不是ClipRectborderRadius: BorderRadius.circular(8),child: _selectFileList[index].type == 2? Stack(alignment: Alignment.center,children: [VideoPlayer(_videoPlayerController),Container(width: 60.w,height: 60.w,child: Image.asset('images/video/icon_pause.png',fit: BoxFit.fill,))],): Image.file(_selectFileList[index].file,width: 220.w,height: 220.w,cacheWidth: 100,cacheHeight: 100,fit: BoxFit.fill,frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {if (wasSynchronouslyLoaded) {return child;}return AnimatedOpacity(child: child,opacity: frame == null ? 0 : 1,duration: const Duration(seconds: 1),curve: Curves.easeOut,);}))),GestureDetector(child: Container(padding: EdgeInsets.all(5),child: Image.asset(PathConfig.iconDeleteImageWhite, width: 34.w, height: 34.w)),onTap: () {if(_selectFileList[index].type == 2){_videoPlayerController = null;}_selectFileList.removeAt(index);_backFileCall();})],);},shrinkWrap: true,separatorBuilder: (context, index) => Container(width: 20.w),itemCount:_selectFileList.length < widget.maxLength ? _selectFileList.length + 1 : _selectFileList.length),))],)],);}//判断是否已经选择了视频bool _selectedVideo(){for (var selectFile in _selectFileList) {if(selectFile.type == 2){return true;}}return false;}//选择弹窗_openImageOrVideoSelect(int index) async {int value = await showCupertinoModalPopup<int>(builder: (BuildContext context) => CupertinoActionSheet(actions: <Widget>[CupertinoActionSheetAction(child: Text(S.current.p12),onPressed: (){widget.requestType = RequestType.image;_callSelectImageVideo(index);Navigator.pop(context, 1);},),CupertinoActionSheetAction(child: Text(S.current.p13),onPressed: (){if(_selectedVideo()){esLoadingToast('已选择一条视频');Navigator.pop(context, 2);return;}widget.requestType = RequestType.video;_callSelectImageVideo(index);Navigator.pop(context, 2);},),],cancelButton: CupertinoActionSheetAction(child: Text(S.current.quxiao),onPressed: () => Navigator.pop(context, 3),), // 取消按钮),context: context,);}//调用图片选择器_callSelectImageVideo(int index) async {List<SelectFileData> _resultFileList = await selectImages(requestType: widget.requestType);if (_resultFileList.isNotEmpty) {setState(() {_selectFileList.addAll(_resultFileList);});if (_selectFileList[index].type == 2) {VideoPlayerController _dvideoPlayerController = VideoPlayerController.file(_selectFileList[index].file);_dvideoPlayerController.initialize().then((_) {Duration duration = _videoPlayerController.value.duration;int videoTime = (duration.inMinutes * 60) + duration.inSeconds;if (videoTime > 60) {esLoadingToast('发布视频长度不能大于1分钟');_dvideoPlayerController = null;_videoPlayerController = null;setState(() {_selectFileList.removeAt(index);});}});_videoPlayerController = _dvideoPlayerController;}}}_backFileCall() {_backFileList.clear();if (widget.selectBack != null) {_selectFileList.forEach((element) {_backFileList.add(element.file);});widget.selectBack(_backFileList);}setState(() {});}//图片选择器Future<List<SelectFileData>> selectImages({RequestType requestType}) async {Completer<List<SelectFileData>> _completer = Completer<List<SelectFileData>>();List<SelectFileData> _imageFiles = [];try {List<AssetEntity> images = await AssetPicker.pickAssets(context,maxAssets: requestType == RequestType.video ? 1 : widget.maxLength - _selectFileList.length, requestType: requestType ?? RequestType.image);if (images != null && images.length > 0) {for (int i = 0; i < images.length; i++) {var _type = images[i].typeInt;File _file = await images[i].file;SelectFileData _selectFileData = SelectFileData(file: _file, type: _type);_imageFiles.add(_selectFileData);}_completer.complete(_imageFiles);} else {_completer.complete([]);}} on Exception catch (e) {print(e);}return _completer.future;}
}

添加按钮

import 'package:jade/configs/PathConfig.dart';
import 'package:jade/utils/JadeColors.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';Widget addImageVideoBtn(String btnTitle,Color bgColor){return Container(width: 220.w,height: 220.w,padding: EdgeInsets.symmetric(horizontal: 10),decoration: BoxDecoration(color: bgColor,borderRadius: BorderRadius.circular(10)),child: Row(mainAxisAlignment: MainAxisAlignment.center,children: [Image.asset(PathConfig.iconAddGrey,width: 22.w,height: 22.w),Flexible(child: Text(btnTitle,style: TextStyle(fontSize: 24.sp,color: JadeColors.grey_18),maxLines: 2,textAlign: TextAlign.center))]),);
}

调用

 //上传反馈图片模块_feedbackSelectImage(){return Container(margin: EdgeInsets.only(top: 40.w),child: SelectImageVideo(title: '反馈',postscript: '(可上传5张图和60s视频)',maxLength: 6,requestType: RequestType.common,discrete: true,bgColor: Colors.white,selectBack: (selectedFiles){_selectFeedbackImageFiles = selectedFiles;}));}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据质量决定大模型能力,景联文科技提供高质量大模型数据

随着大模型的深入发展&#xff0c;各类资源要素的配置状态已悄然变化。其中&#xff0c;数据的价值已被提升到一个新高度。 大模型往往拥有庞大的参数和复杂的网络结构&#xff0c;需要大量的数据来学习和优化。数据的质量和数量直接决定了模型的训练效果。若数据不足或质量不佳…

React + 项目(从基础到实战) -- 第三期

react内置hooks useState 如何让页面动起来(实时更新) import React,{FC,useState} from "react";const Demo:FC()>{let count0; //普通js变量无法触发组件更新function add(){count;console.log("count: ",count);}return <div><button onCl…

远程登录服务器(ubuntu20.04)在自己账号下的虚拟环境(python3.6)安装Jupyter并连接pycharm使用

参考&#xff1a;Jupyter notebook/lab安装及远程访问 1、安装jupyter pip install notebook遇到的问题&#xff1a; &#xff08;1&#xff09;运行这个指令之前尝试了好多方法都安不上 此前还尝试了更新pip之类的&#xff0c;大家安不上也可以先更新pip试试。 &#xff0…

JavaScript简介

目录 概要&#xff1a; 说明&#xff1a; 学习JS的原因&#xff1a; JS可以干什么&#xff1a; 了解JavaScript&#xff1a; 前言&#xff1a; JavaScript的历史&#xff1a; JavaScript与ECMAScript&#xff1a; 如何运行JavaScript以及JavaScrip的特点&#xff1a; …

基于Python的微博旅游情感分析、微博舆论可视化系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

HTML - 你知道b与strong标签的区别吗

难度级别&#xff1a;初级及以上 提问概率&#xff1a;50% 不单单是初学者&#xff0c;即便是有好几年工作经验的前端开发工作者&#xff0c;也会有一大部分人把这两个标签搞混&#xff0c;甚至在工作中&#xff0c;很大一部人不会使用这两个标…

玩转Django分页器

一、Pagination 分页器编程步骤 View, 导入django.core.paginator.Paginator类&#xff0c;创建Paginator 对象时&#xff0c;输入qs对象&#xff0c;以及每页显示条数。 接收 URL, 从请求参数中读取page数值 &#xff0c;通过 paginator.page(page_num) 返回请求页的page_obj…

电商好评语整理与优化:让繁琐工作变得轻松高效

在电子商务领域&#xff0c;客户的好评是店铺信誉和产品质量的重要体现。然而&#xff0c;整理和优化这些好评语却是一项既繁琐又需要细致耐心的工作。本文将探讨如何高效地进行电商好评语的筛选、分类和优化&#xff0c;让这一工作变得更加轻松和高效。 一、明确整理目的 在开…

环保用电监测系统诞生与作用

随着全球能源危机的加剧和环境保护意识的提高&#xff0c;环保用电监测系统应运而生。这一系统以其独特的监测能力、数据分析和节能减排功能&#xff0c;在提高用电效率和促进环境可持续发展方面发挥着重要作用。本文将从环保用电监测系统的诞生背景、主要功能、作用以及在实际…

语音识别:基于HMM

HMM语音识别的解码过程 从麦克风采集的输入音频波形被转换为固定尺寸的一组声学向量&#xff1a; 其中是维的语音特征向量&#xff08;例如MFCC&#xff09;。 解码器尝试去找到上述特征向量序列对应的单词&#xff08;word&#xff09;的序列&#xff1a; 单词序列的长度是。…

2024人工智能与机器人系统国际学术会议(ICAIRS2024)

2024人工智能与机器人系统国际学术会议(ICAIRS2024) 会议简介 2024人工智能与机器人系统国际学术会议(ICAIRS2024)将在杭州举行。该会议旨在为人工智能和机器人系统的专家学者提供一个平台&#xff0c;以分享最新的研究成果、交流思想、探讨学术问题&#xff0c;并促进跨学科…

Open3D(C++) 基于随机抽样与特征值法的点云平面稳健拟合方法

目录 一、算法原理1、论文概述2、参考文献二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的GPT爬虫。 一、算法原理 1、论文概述 针对点云数据含有异常值且传统拟合方法拟合结果不理想的情…

动态规划刷题(算法竞赛、蓝桥杯)--合唱队形(线性DP)

1、题目链接&#xff1a;[NOIP2004 提高组] 合唱队形 - 洛谷 #include <bits/stdc.h> using namespace std; int n,ans; int a[105],f[105][2];//f[i][2]中2表示正反两个方向int main(){cin>>n;for(int i1;i<n;i){cin>>a[i];}//正方向求最长上升子序列 a[…

总结UDP协议各类知识点

前言 本篇博客博主将详细地介绍UDP有关知识点&#xff0c;坐好板凳发车啦~ 一.UDP特点 1.无连接 UDP传输的过程类似于发短信&#xff0c;知道对端的IP和端口号就直接进行传输&#xff0c;不需要建立连接&#xff1b; 2.不可靠传输 没有任何的安全机制&#xff0c;发送端发…

C语言:文件操作(一)

目录 前言 1、为什么使用文件 2、什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 3、文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 结&#xff08;一&#xff09; 前言 本篇文章将介绍C语言的文件操作&#xff0c;在后面的内容讲到&#xff1a;为什么使用文…

大数据设计为何要分层,行业常规设计会有几层数据

大数据设计通常采用分层结构的原因是为了提高数据管理的效率、降低系统复杂度、增强数据质量和可维护性。这种分层结构能够将数据按照不同的处理和应用需求进行分类和管理&#xff0c;从而更好地满足不同层次的数据处理和分析需求。行业常规设计中&#xff0c;数据通常按照以下…

C++11:声明 初始化

C11&#xff1a;声明 & 初始化 初始化{ }初始化initializer_list 声明autodecltypenullptr 初始化 { }初始化 在C98中&#xff0c;允许使用花括号{ }对数组或者结构体元素进行统一的列表初始化。 用{ }初始化数组&#xff1a; int arr[] { 1, 2, 3, 4, 5 };用{ }初始化…

Qt C++ | Qt 元对象系统、信号和槽及事件(第一集)

01 元对象系统 一、元对象系统基本概念 1、Qt 的元对象系统提供的功能有:对象间通信的信号和槽机制、运行时类型信息和动态属性系统等。 2、元对象系统是 Qt 对原有的 C++进行的一些扩展,主要是为实现信号和槽机制而引入的, 信号和槽机制是 Qt 的核心特征。 3、要使用元…

VMware虚拟机三种网络模式配置

vmware有三种网络工作模式&#xff1a;Bridged&#xff08;桥接模式&#xff09;、NAT&#xff08;网络地址转换模式&#xff09;、Host-Only&#xff08;仅主机模式&#xff09;。 1. 打开网络编辑器&#xff08;编辑 --> 虚拟网络编辑器&#xff09; 在主机上有VMware Ne…

【阿里淘天笔试题汇总】2024-04-03-阿里淘天春招笔试题(第一套)-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新淘天近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…