import 'dart:io'; import 'dart:async'; import 'package:farm_tpf/custom_model/Media.dart'; import 'package:farm_tpf/presentation/custom_widgets/bloc/media_helper_bloc.dart'; import 'package:farm_tpf/presentation/custom_widgets/hoz_list_view.dart'; import 'package:farm_tpf/presentation/custom_widgets/shimmer_image.dart'; import 'package:farm_tpf/utils/const_color.dart'; import 'package:farm_tpf/utils/const_string.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get/get.dart'; import 'package:get/state_manager.dart'; import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; class WidgetMediaHelper extends StatefulWidget { final Function(List filePaths) onChangeFiles; WidgetMediaHelper({@required this.onChangeFiles}); @override _WidgetMediaHelperState createState() => _WidgetMediaHelperState(); } class _WidgetMediaHelperState extends State { final globalScaffoldKey = GlobalKey(); PickedFile _imageFile; dynamic _pickImageError; bool isVideo = false; VideoPlayerController _controller; VideoPlayerController _toBeDisposed; String _retrieveDataError; final ImagePicker _picker = ImagePicker(); List currentItems = []; List files = new List(); var changeImageController = Get.put(ChangeImageController()); double imageWidth = 90; double imageHeight = 90; @override void initState() { super.initState(); changeImageController.initValue(); } @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext contextA) => MediaHelperBloc()..add(ChangeListMedia(items: currentItems)), child: BlocBuilder( builder: (contextB, state) { if (state is MediaHelperFailure) { return Container(); } else if (state is MediaHelperSuccess) { return Container( padding: EdgeInsets.all(8), child: Column( children: [ SizedBox( width: double.infinity, height: 44, child: FlatButton( onPressed: () { showDialog( context: context, barrierDismissible: true, builder: (context) => Opacity( child: multipleChoice(contextB), opacity: 1, )); }, color: COLOR_CONST.DEFAULT, shape: RoundedRectangleBorder( borderRadius: new BorderRadius.circular(7.0), ), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.image, color: Colors.white), SizedBox( width: 8.0, ), Text( button_add_media, style: TextStyle( fontWeight: FontWeight.bold, color: COLOR_CONST.WHITE), ) ], ), )), ), SizedBox( height: 4.0, ), Container( height: 150, child: _buildListPoster(), ), defaultTargetPlatform == TargetPlatform.android ? FutureBuilder( future: retrieveLostData(), builder: (BuildContext context, AsyncSnapshot snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: case ConnectionState.waiting: return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); case ConnectionState.done: return isVideo ? _previewVideo() : _previewImage(); default: if (snapshot.hasError) { return Text( 'Pick image/video error: ${snapshot.error}}', textAlign: TextAlign.center, ); } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); } } }, ) : (isVideo ? _previewVideo() : _previewImage()), ], )); } return Container(); })); } _buildListPoster() { return BlocBuilder( builder: (context, state) { if (state is MediaHelperSuccess) { return WrapContentHozListView( itemBuilder: (context, index) { var item = state.items[index]; return _WidgetItemMedia( item: item, deleteImage: (item) { files.remove(item.pathFile); currentItems.remove(item); widget.onChangeFiles(files); BlocProvider.of(context) .add(ChangeListMedia(items: currentItems)); }); }, separatorBuilder: (context, index) { return SizedBox(width: 14); }, list: state.items, ); } return Container(); }); } Widget multipleChoice(BuildContext context) { return CupertinoAlertDialog( title: Text(label_title_select_media), actions: [ CupertinoDialogAction( child: const Text(label_select_image_from_library), onPressed: () { Navigator.pop(context, 'Discard'); isVideo = false; _onImageButtonPressed(ImageSource.gallery, context: context); }), CupertinoDialogAction( child: const Text(label_take_photo), onPressed: () { Navigator.pop(context, 'Discard'); isVideo = false; _onImageButtonPressed(ImageSource.camera, context: context); }), CupertinoDialogAction( child: const Text(label_select_video_from_library), onPressed: () { Navigator.pop(context, 'Discard'); isVideo = true; _onImageButtonPressed(ImageSource.gallery); }), CupertinoDialogAction( child: const Text(label_record_video), onPressed: () { Navigator.pop(context, 'Discard'); isVideo = true; _onImageButtonPressed(ImageSource.camera); }), CupertinoDialogAction( child: const Text(label_cancel), textStyle: TextStyle(fontWeight: FontWeight.bold), isDefaultAction: true, onPressed: () { Navigator.pop(context, 'Cancel'); }), ], ); } Widget _actionButton() { return BlocListener( listener: (context, state) { SizedBox( width: double.infinity, height: 44, child: FlatButton( onPressed: () { showDialog( context: context, barrierDismissible: true, builder: (context) => Opacity( child: Text(""), opacity: 1, )); }, color: COLOR_CONST.DEFAULT, shape: RoundedRectangleBorder( borderRadius: new BorderRadius.circular(7.0), ), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.image, color: Colors.white), SizedBox( width: 8.0, ), Text( button_add_media, style: TextStyle( fontWeight: FontWeight.bold, color: COLOR_CONST.WHITE), ) ], ), )), ); }); } Future _playVideo(PickedFile file) async { if (file != null && mounted) { await _disposeVideoController(); _controller = VideoPlayerController.file(File(file.path)); await _controller.setVolume(1.0); await _controller.initialize(); await _controller.setLooping(true); await _controller.play(); } } _onImageButtonPressed(ImageSource source, {BuildContext context}) async { if (_controller != null) { await _controller.setVolume(0.0); } if (isVideo) { final PickedFile file = await _picker.getVideo( source: source, maxDuration: const Duration(seconds: 10)); await _playVideo(file); } else { await _displayPickImageDialog(context, (double maxWidth, double maxHeight, int quality) async { try { final pickedFile = await _picker.getImage( source: source, maxWidth: imageWidth, maxHeight: imageHeight, imageQuality: quality, ); Get.find().updateFile(pickedFile); Media newMedia = Media() ..isVideo = false ..isServerFile = false ..pathFile = pickedFile.path; currentItems.add(newMedia); files.add(pickedFile.path); BlocProvider.of(context) ..add(ChangeListMedia(items: currentItems)); widget.onChangeFiles(files); } catch (e) { Get.find().updateFileError(e); } }); } ; } @override void deactivate() { if (_controller != null) { _controller.setVolume(0.0); _controller.pause(); } super.deactivate(); } @override void dispose() { _disposeVideoController(); super.dispose(); } Future _disposeVideoController() async { if (_toBeDisposed != null) { await _toBeDisposed.dispose(); } _toBeDisposed = _controller; _controller = null; } Widget _previewVideo() { final Text retrieveError = _getRetrieveErrorWidget(); if (retrieveError != null) { return retrieveError; } if (_controller == null) { return const Text( 'You have not yet picked a video', textAlign: TextAlign.center, ); } return Padding( padding: const EdgeInsets.all(10.0), child: AspectRatioVideo(_controller), ); } Widget _previewImage() { return Builder(builder: (context) { final Text retrieveError = _getRetrieveErrorWidget(); if (retrieveError != null) { return retrieveError; } if (_imageFile != null) { var imageResult = Image.file(File(_imageFile.path), width: imageWidth, height: 100); Media newMedia = Media() ..isVideo = false ..isServerFile = false ..pathFile = _imageFile.path; currentItems.add(newMedia); BlocProvider.of(context) .add(ChangeListMedia(items: currentItems)); //widget.onChangeFiles(files); return Container( child: Text("ok"), ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', textAlign: TextAlign.center, ); } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); } }); } Future retrieveLostData() async { final LostData response = await _picker.getLostData(); if (response.isEmpty) { return; } if (response.file != null) { if (response.type == RetrieveType.video) { isVideo = true; await _playVideo(response.file); } else { isVideo = false; setState(() { _imageFile = response.file; }); } } else { _retrieveDataError = response.exception.code; } } Text _getRetrieveErrorWidget() { if (_retrieveDataError != null) { final Text result = Text(_retrieveDataError); _retrieveDataError = null; return result; } return null; } Future _displayPickImageDialog( BuildContext context, OnPickImageCallback onPick) async { onPick(null, null, null); } } typedef void OnPickImageCallback( double maxWidth, double maxHeight, int quality); class AspectRatioVideo extends StatefulWidget { AspectRatioVideo(this.controller); final VideoPlayerController controller; @override AspectRatioVideoState createState() => AspectRatioVideoState(); } class AspectRatioVideoState extends State { VideoPlayerController get controller => widget.controller; bool initialized = false; void _onVideoControllerUpdate() { if (!mounted) { return; } if (initialized != controller.value.initialized) { initialized = controller.value.initialized; setState(() {}); } } @override void initState() { super.initState(); controller.addListener(_onVideoControllerUpdate); } @override void dispose() { controller.removeListener(_onVideoControllerUpdate); super.dispose(); } @override Widget build(BuildContext context) { if (initialized) { return Container( width: 100, height: 100, child: AspectRatio( aspectRatio: controller.value?.aspectRatio, child: VideoPlayer(controller), ), ); } else { return Container(); } } } class _WidgetItemMedia extends StatelessWidget { ItemMediaCallback deleteImage; final Media item; _WidgetItemMedia({@required this.item, @required this.deleteImage}); BuildContext _context; @override Widget build(BuildContext context) { _context = context; return GestureDetector( onTap: () { print("Show preview image or video"); }, child: Stack( alignment: Alignment.bottomCenter, overflow: Overflow.visible, children: [ Positioned( child: ClipRRect( borderRadius: BorderRadius.circular(8), child: item.isServerFile ? ShimmerImage( item.pathFile, width: 93, height: 124, fit: BoxFit.cover, ) : Image.file(File(item.pathFile), width: 100, height: 100), )), Positioned( top: -5, right: -5, child: IconButton( icon: Icon( Icons.cancel, color: Colors.redAccent, ), onPressed: () { print("On tap delete media"); deleteImage(item); }), ) ], )); } } typedef ItemMediaCallback = void Function(Media item); class ItemMediaVM { String photo; bool isVideo; Image image; ItemMediaVM(this.photo, this.isVideo, this.image); } class ChangeImageController extends GetxController { PickedFile _imageFile; dynamic _pickImageError; void initValue() { update(); } void updateFile(PickedFile imageFile) { _imageFile = imageFile; update(); } void updateFileError(dynamic pickImageError) { _pickImageError = pickImageError; update(); } }