【flutter封装图片/视频选择控件】
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:other
SelectFileData({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 createState
return _SelectImageVideo();
}
}
class _SelectImageVideo extends State<SelectImageVideo> {
List<SelectFileData> _selectFileList = [];
List<File> _backFileList = [];
VideoPlayerController _videoPlayerController;
Widget build(BuildContext context) {
// TODO: implement build
return 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,不是ClipRect
borderRadius: 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(