You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
13KB

  1. import 'dart:io';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:farm_tpf/custom_model/Media.dart';
  4. import 'package:farm_tpf/presentation/custom_widgets/camera_helper.dart';
  5. import 'package:farm_tpf/presentation/custom_widgets/widget_show_video.dart';
  6. import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
  7. import 'package:farm_tpf/presentation/screens/actions/util_action.dart';
  8. import 'package:farm_tpf/utils/const_assets.dart';
  9. import 'package:farm_tpf/utils/const_common.dart';
  10. import 'package:farm_tpf/utils/const_string.dart';
  11. import 'package:file_picker/file_picker.dart';
  12. import 'package:flutter/cupertino.dart';
  13. import 'package:flutter/material.dart';
  14. import 'package:flutter_bloc/flutter_bloc.dart';
  15. import 'package:image_picker/image_picker.dart';
  16. import 'bloc/media_helper_bloc.dart';
  17. import 'hoz_list_view.dart';
  18. class WidgetMediaPicker extends StatefulWidget {
  19. final List<Media>? currentItems;
  20. final Function(List<String> addNewFilePaths, List<String> deleteFilePaths) onChangeFiles;
  21. WidgetMediaPicker({this.currentItems, required this.onChangeFiles});
  22. @override
  23. _WidgetMediaPickerState createState() => _WidgetMediaPickerState();
  24. }
  25. class _WidgetMediaPickerState extends State<WidgetMediaPicker> {
  26. List<Media> currentItems = [];
  27. List<String> addNewFilePaths = <String>[];
  28. List<String> deleteFilePaths = <String>[];
  29. final ImagePicker _picker = ImagePicker();
  30. @override
  31. void initState() {
  32. super.initState();
  33. }
  34. @override
  35. Widget build(BuildContext context) {
  36. return BlocProvider<MediaHelperBloc>(
  37. create: (BuildContext contextA) => MediaHelperBloc()..add(ChangeListMedia(items: currentItems)),
  38. child: BlocBuilder<MediaHelperBloc, MediaHelperState>(builder: (contextB, state) {
  39. if (state is MediaHelperFailure) {
  40. return Container();
  41. } else if (state is MediaHelperSuccess) {
  42. currentItems = widget.currentItems ?? [];
  43. return Container(
  44. padding: EdgeInsets.all(8),
  45. child: Column(
  46. crossAxisAlignment: CrossAxisAlignment.start,
  47. children: <Widget>[
  48. Text('Hình ảnh/ Video', style: TextStyle(color: Colors.black54, fontSize: 16)),
  49. SizedBox(
  50. height: 8,
  51. ),
  52. Row(
  53. children: [
  54. InkWell(
  55. child: Image.asset(
  56. AppAssets.icAddMedia,
  57. fit: BoxFit.contain,
  58. width: 114,
  59. height: 114,
  60. ),
  61. onTap: () {
  62. showDialog(
  63. context: context,
  64. barrierDismissible: true,
  65. builder: (context) => Opacity(
  66. child: multipleChoice(contextB),
  67. opacity: 1,
  68. ));
  69. },
  70. ),
  71. SizedBox(
  72. width: 8.0,
  73. ),
  74. Expanded(
  75. child: _buildListPoster(),
  76. ),
  77. ],
  78. ),
  79. ],
  80. ));
  81. }
  82. return Container();
  83. }));
  84. }
  85. Widget multipleChoice(BuildContext context) {
  86. return CupertinoAlertDialog(
  87. title: Text(label_title_select_media),
  88. actions: <Widget>[
  89. CupertinoDialogAction(
  90. child: const Text('Chụp ảnh'),
  91. onPressed: () async {
  92. Navigator.pop(context, 'Discard');
  93. try {
  94. final image = await _picker.pickImage(source: ImageSource.camera, imageQuality: 90);
  95. if (image?.path != null) {
  96. var imageFile = await UtilAction.compressImage(File(image?.path ?? ''));
  97. print(imageFile.lengthSync());
  98. if (imageFile.lengthSync() > ConstCommon.kFileSize) {
  99. Utils.showSnackBarWarning(message: label_file_to_large);
  100. } else {
  101. Media newMedia = Media()
  102. ..isVideo = false
  103. ..isServerFile = false
  104. ..pathFile = imageFile.path;
  105. currentItems.add(newMedia);
  106. addNewFilePaths.add(imageFile.path);
  107. BlocProvider.of<MediaHelperBloc>(context)..add(ChangeListMedia(items: currentItems));
  108. widget.onChangeFiles(addNewFilePaths, deleteFilePaths);
  109. }
  110. }
  111. } catch (e) {
  112. print(e);
  113. }
  114. }),
  115. CupertinoDialogAction(
  116. child: const Text('Chọn ảnh'),
  117. onPressed: () async {
  118. Navigator.pop(context, 'Discard');
  119. FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
  120. if (result != null) {
  121. var listFuture = <Future<File>>[];
  122. result.files.forEach((element) {
  123. listFuture.add(UtilAction.compressImage(File(element.path ?? '')));
  124. });
  125. Future.wait(listFuture).then((values) {
  126. bool isExistedFileTooLarge = false;
  127. values.forEach((compressFile) {
  128. if (compressFile.lengthSync() > ConstCommon.kFileSize) {
  129. isExistedFileTooLarge = true;
  130. } else {
  131. Media newMedia = Media()
  132. ..isVideo = false
  133. ..isServerFile = false
  134. ..pathFile = compressFile.path;
  135. currentItems.add(newMedia);
  136. addNewFilePaths.add(compressFile.path);
  137. }
  138. });
  139. if (isExistedFileTooLarge) {
  140. Utils.showSnackBarWarning(message: "Tập tin có kích thước lớn đã được loại bỏ.");
  141. }
  142. BlocProvider.of<MediaHelperBloc>(context)..add(ChangeListMedia(items: currentItems));
  143. widget.onChangeFiles(addNewFilePaths, deleteFilePaths);
  144. });
  145. }
  146. }),
  147. // CupertinoDialogAction(
  148. // child: const Text(label_select_video_from_library),
  149. // onPressed: () async {
  150. // Navigator.pop(context, 'Discard');
  151. // FilePickerResult result = await FilePicker.platform
  152. // .pickFiles(type: FileType.video, allowMultiple: true);
  153. // if (result != null) {
  154. // bool isExistedFileTooLarge = false;
  155. // result.files?.forEach((videoFile) {
  156. // if (videoFile.size * 1000 > ConstCommon.kFileSize) {
  157. // isExistedFileTooLarge = true;
  158. // } else {
  159. // Media newMedia = Media()
  160. // ..isVideo = true
  161. // ..isServerFile = false
  162. // ..pathFile = videoFile.path;
  163. // currentItems.add(newMedia);
  164. // addNewFilePaths.add(videoFile.path);
  165. // }
  166. // });
  167. // if (isExistedFileTooLarge) {
  168. // Utils.showSnackBarWarning(
  169. // message: "Tập tin có kích thước lớn đã được loại bỏ.");
  170. // }
  171. // BlocProvider.of<MediaHelperBloc>(context)
  172. // ..add(ChangeListMedia(items: currentItems));
  173. // widget.onChangeFiles(addNewFilePaths, deleteFilePaths);
  174. // }
  175. // }),
  176. CupertinoDialogAction(
  177. child: const Text(label_cancel),
  178. textStyle: TextStyle(fontWeight: FontWeight.bold),
  179. isDefaultAction: true,
  180. onPressed: () {
  181. Navigator.pop(context, 'Cancel');
  182. }),
  183. ],
  184. );
  185. }
  186. _buildListPoster() {
  187. return BlocBuilder<MediaHelperBloc, MediaHelperState>(builder: (context, state) {
  188. if (state is MediaHelperSuccess) {
  189. return WrapContentHozListView(
  190. list: currentItems,
  191. itemBuilder: (context, index) {
  192. var item = currentItems[index];
  193. return Container(
  194. width: 120,
  195. height: 120,
  196. child: _WidgetItemMedia(
  197. item: item,
  198. deleteImage: (item) {
  199. if (item.isServerFile) {
  200. var url = item.pathFile?.replaceAll(ConstCommon.baseImageUrl, '');
  201. deleteFilePaths.add(url ?? '');
  202. }
  203. addNewFilePaths.remove(item.pathFile);
  204. currentItems.remove(item);
  205. widget.onChangeFiles(addNewFilePaths, deleteFilePaths);
  206. BlocProvider.of<MediaHelperBloc>(context).add(ChangeListMedia(items: currentItems));
  207. }),
  208. );
  209. });
  210. }
  211. return Container();
  212. });
  213. }
  214. }
  215. class _WidgetItemMedia extends StatelessWidget {
  216. ItemMediaCallback deleteImage;
  217. final Media item;
  218. _WidgetItemMedia({required this.item, required this.deleteImage});
  219. void _showPhotoPreview(BuildContext context) {
  220. if (item.isVideo ?? false) return;
  221. showDialog(
  222. context: context,
  223. builder: (context) => Dialog(
  224. backgroundColor: Colors.transparent,
  225. insetPadding: EdgeInsets.all(20),
  226. child: Container(
  227. decoration: BoxDecoration(
  228. color: Colors.white,
  229. borderRadius: BorderRadius.circular(16),
  230. boxShadow: [
  231. BoxShadow(
  232. color: Colors.black.withOpacity(0.1),
  233. blurRadius: 10,
  234. spreadRadius: 2,
  235. ),
  236. ],
  237. ),
  238. child: Stack(
  239. children: [
  240. Container(
  241. constraints: BoxConstraints(
  242. maxHeight: MediaQuery.of(context).size.height * 0.7,
  243. ),
  244. child: ClipRRect(
  245. borderRadius: BorderRadius.circular(16),
  246. child: item.isServerFile
  247. ? CachedNetworkImage(
  248. imageUrl: item.pathFile ?? '',
  249. fit: BoxFit.contain,
  250. placeholder: (context, url) => Center(
  251. child: CircularProgressIndicator(),
  252. ),
  253. errorWidget: (context, url, error) => Center(
  254. child: Icon(Icons.error, color: Colors.red),
  255. ),
  256. )
  257. : Image.file(
  258. File(item.pathFile ?? ''),
  259. fit: BoxFit.contain,
  260. ),
  261. ),
  262. ),
  263. Positioned(
  264. top: 0,
  265. right: 0,
  266. child: Material(
  267. color: Colors.transparent,
  268. child: IconButton(
  269. icon: Icon(Icons.close, color: Colors.black),
  270. onPressed: () => Navigator.pop(context),
  271. ),
  272. ),
  273. )
  274. ],
  275. ),
  276. ),
  277. ),
  278. );
  279. }
  280. @override
  281. Widget build(BuildContext context) {
  282. return Stack(
  283. alignment: Alignment.topRight,
  284. children: <Widget>[
  285. GestureDetector(
  286. onTap: () => _showPhotoPreview(context),
  287. child: Container(
  288. width: 120,
  289. height: 120,
  290. child: item.isVideo ?? false
  291. ? VideoWidget(
  292. pathFile: item.pathFile ?? '',
  293. isServerFile: item.isServerFile,
  294. play: false,
  295. )
  296. : Container(
  297. margin: EdgeInsets.all(4.0),
  298. decoration: BoxDecoration(
  299. color: Colors.white,
  300. border: Border.all(color: Colors.grey),
  301. borderRadius: BorderRadius.all(
  302. Radius.circular(8.0),
  303. ),
  304. ),
  305. child: item.isServerFile
  306. ? CachedNetworkImage(
  307. placeholder: (context, url) => Icon(Icons.crop_original),
  308. imageUrl: item.pathFile ?? '',
  309. )
  310. : Image.file(File(item.pathFile ?? '')),
  311. ),
  312. ),
  313. ),
  314. Positioned(
  315. top: 0,
  316. right: 0,
  317. child: Material(
  318. color: Colors.transparent,
  319. child: IconButton(
  320. icon: Icon(
  321. Icons.delete,
  322. color: Colors.redAccent,
  323. size: 24,
  324. ),
  325. onPressed: () {
  326. print("On tap delete media");
  327. deleteImage(item);
  328. },
  329. ),
  330. ),
  331. )
  332. ],
  333. );
  334. }
  335. }
  336. typedef ItemMediaCallback = void Function(Media item);