【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.hqwc.cn/news/589234.html

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

相关文章

洛谷B3735题解

题目描述 圣诞树共有 n 层&#xff0c;从上向下数第 1 层有 1 个星星、第 2 层有 2 个星星、以此类推&#xff0c;排列成下图所示的形状。 星星和星星之间用绳子连接。第 1,2,⋯,n−1 层的每个星星都向下一层最近的两个星星连一段绳子&#xff0c;最后一层的相邻星星之间连一段…

算法知识点汇总

知识点 1. 求二进制中1的个数 int get_count(int x)//返回x的二进制有多少个1 int get_count(int x) {int res 0;while (x){res ;x - x & -x;}return res; }2. 建树&#xff0c;和树的DFS 记得初始化头节点 const int N 1e5 10, M N * 2; int h[N], e[M], ne[M], id…

RD55UP06-V 三菱iQ-R系列C语言功能模块

RD55UP06-V 三菱iQ-R系列C语言功能模块 RD55UP06-V用户手册&#xff0c;RD55UP06-V功能&#xff0c;RD55UP06-V系统配置 RD55UP06-V参数规格&#xff1a;10BASE-T/100BASE-TX/1000BASE-T 1通道&#xff1b;字节存储次序格式小端模式; 可使用SD存储卡插槽&#xff1b;工作RAM 1…

争光树脂邀您到场参观2024年第13届生物发酵展

参展企业介绍 宁波争光树脂有限公司成立于2006年11月&#xff0c;是浙江争光实业股份有限公司的全资子公司&#xff0c;公司专业生产离子交换树脂&#xff0c;产品的应用领域主要涉及电厂、核能、石油、化工、轻工、医药、食品、饮料、冶金、环保、生物等领域&#xff0c;年生…

AGV无人驾驶跨境运输新模式引领未来物流

agv AGV即“自动导引运输车”&#xff0c;这一概念起源于欧美&#xff0c;在欧美及日韩市场的发展比较成熟&#xff0c;于上世纪末被引入国内。这种自动导引运输车可以广泛应用于汽车、化工、医药以及食品饮料等制造业场景&#xff0c;以及机场、码头等仓储物流行业场景&#x…

JavaScript中什么叫深拷贝?

在 JavaScript 中&#xff0c;深拷贝指的是创建一个新的对象&#xff0c;这个新的对象与原始对象完全独立&#xff0c;没有任何共享的属性或者数据&#xff0c;它们不共享同一块内存地址。深拷贝会复制原始对象的所有属性和嵌套对象的所有属性&#xff0c;包括嵌套对象中的属性…

【深度学习】图像自然语言描述生成

案例 6&#xff1a;图像自然语言描述生成&#xff08;让计算机“看图说话”&#xff09; 相关知识点&#xff1a;RNN、Attention 机制、图像和文本数据的处理 1 任务目标 1.1 任务和数据简介 ​ 本次案例将使用深度学习技术来完成图像自然语言描述生成任务&#xff0c;输入…

物联网实战--入门篇之(四)嵌入式-UART驱动

目录 一、串口简介 二、串口驱动设计 三、串口发送 四、串口接收处理 五、PM2.5数据接收处理 六、printf重定义 七、总结 一、串口简介 串口在单片机的开发中属于非常常用的外设&#xff0c;最基本的都会预留一个调试串口用来输出调试信息&#xff0c;串口时序这里就不谈…

代码随想录阅读笔记-二叉树【合并二叉树】

题目 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们的值相加作为节点合并后的新值&#xff0c;否则不为 NULL 的节…

HackTheBox-Machines--Wifinetic

文章目录 1 端口扫描2 测试思路3 21端口测试&权限获取4 权限提升方法一方法二&#xff1a; Wifinetic 测试过程 1 端口扫描 nmap -sC -sV 10.129.229.902 测试思路 目标开启了21、22、53端口&#xff0c;并且21端口FTP服务允许匿名登录&#xff0c;所以从21端口开始进行测试…

谈谈考研数学几个常见误区

25考研数学&#xff0c;一定一定要吃透基础&#xff0c;练好计算 我之所以要强调这个&#xff0c;是因为现在的考研数学&#xff0c;越来越重视基础和计算的考察&#xff0c;题海战术已经过时&#xff0c;如果想要有效的提升自己&#xff0c;要进行针对性的学习。我去年考研的…

又一AI工具开源!企业应该如何搭上这趟AI快车

大模型技术在近两年来飞速发展&#xff0c;企业对大模型的认知更加理性、务实。大模型本身不会直接产生价值&#xff0c;但在大模型基础架构之上开发出的AI应用&#xff0c;带来技术创新及业务增长&#xff0c;成为企业真正关心的问题。 基于大模型开发的又一个AI工具诞生&…