|
|
|
@@ -1,554 +0,0 @@ |
|
|
|
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<String> filePaths) onChangeFiles; |
|
|
|
WidgetMediaHelper({@required this.onChangeFiles}); |
|
|
|
@override |
|
|
|
_WidgetMediaHelperState createState() => _WidgetMediaHelperState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _WidgetMediaHelperState extends State<WidgetMediaHelper> { |
|
|
|
final globalScaffoldKey = GlobalKey<ScaffoldState>(); |
|
|
|
PickedFile _imageFile; |
|
|
|
dynamic _pickImageError; |
|
|
|
bool isVideo = false; |
|
|
|
VideoPlayerController _controller; |
|
|
|
VideoPlayerController _toBeDisposed; |
|
|
|
String _retrieveDataError; |
|
|
|
|
|
|
|
final ImagePicker _picker = ImagePicker(); |
|
|
|
List<Media> currentItems = []; |
|
|
|
List<String> files = new List<String>(); |
|
|
|
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<MediaHelperBloc>( |
|
|
|
create: (BuildContext contextA) => |
|
|
|
MediaHelperBloc()..add(ChangeListMedia(items: currentItems)), |
|
|
|
child: BlocBuilder<MediaHelperBloc, MediaHelperState>( |
|
|
|
builder: (contextB, state) { |
|
|
|
if (state is MediaHelperFailure) { |
|
|
|
return Container(); |
|
|
|
} else if (state is MediaHelperSuccess) { |
|
|
|
return Container( |
|
|
|
padding: EdgeInsets.all(8), |
|
|
|
child: Column( |
|
|
|
children: <Widget>[ |
|
|
|
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: <Widget>[ |
|
|
|
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<void>( |
|
|
|
future: retrieveLostData(context), |
|
|
|
builder: (BuildContext context, |
|
|
|
AsyncSnapshot<void> 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<MediaHelperBloc, MediaHelperState>( |
|
|
|
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<MediaHelperBloc>(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: <Widget>[ |
|
|
|
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<MediaHelperBloc, MediaHelperState>( |
|
|
|
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: <Widget>[ |
|
|
|
Icon(Icons.image, color: Colors.white), |
|
|
|
SizedBox( |
|
|
|
width: 8.0, |
|
|
|
), |
|
|
|
Text( |
|
|
|
button_add_media, |
|
|
|
style: TextStyle( |
|
|
|
fontWeight: FontWeight.bold, color: COLOR_CONST.WHITE), |
|
|
|
) |
|
|
|
], |
|
|
|
), |
|
|
|
)), |
|
|
|
); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _playVideo(PickedFile file) async { |
|
|
|
if (file != null && mounted) { |
|
|
|
await _disposeVideoController(); |
|
|
|
File f = File(file.path); |
|
|
|
_controller = VideoPlayerController.file(File(file.path)); |
|
|
|
await _controller.setVolume(1.0); |
|
|
|
await _controller.initialize(); |
|
|
|
await _controller.setLooping(false); |
|
|
|
await _controller.play(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_onImageButtonPressed(ImageSource source, {BuildContext context}) async { |
|
|
|
if (_controller != null) { |
|
|
|
await _controller.setVolume(0.0); |
|
|
|
} |
|
|
|
if (isVideo) { |
|
|
|
// final video = await FilePicker.getFile(type: FileType.video); |
|
|
|
final PickedFile pickedFile = await _picker.getVideo(source: source); |
|
|
|
Get.find<ChangeImageController>().updateFile(pickedFile); |
|
|
|
Media newMedia = Media() |
|
|
|
..isVideo = false |
|
|
|
..isServerFile = false |
|
|
|
..pathFile = pickedFile.path; |
|
|
|
currentItems.add(newMedia); |
|
|
|
print(pickedFile.path); |
|
|
|
files.add(pickedFile.path); |
|
|
|
_playVideo(pickedFile); |
|
|
|
// BlocProvider.of<MediaHelperBloc>(context) |
|
|
|
// ..add(ChangeListMedia(items: currentItems)); |
|
|
|
// widget.onChangeFiles(files); |
|
|
|
} 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<ChangeImageController>().updateFile(pickedFile); |
|
|
|
Media newMedia = Media() |
|
|
|
..isVideo = false |
|
|
|
..isServerFile = false |
|
|
|
..pathFile = pickedFile.path; |
|
|
|
currentItems.add(newMedia); |
|
|
|
files.add(pickedFile.path); |
|
|
|
BlocProvider.of<MediaHelperBloc>(context) |
|
|
|
..add(ChangeListMedia(items: currentItems)); |
|
|
|
widget.onChangeFiles(files); |
|
|
|
} catch (e) { |
|
|
|
Get.find<ChangeImageController>().updateFileError(e); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
; |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void deactivate() { |
|
|
|
if (_controller != null) { |
|
|
|
_controller.setVolume(0.0); |
|
|
|
_controller.pause(); |
|
|
|
} |
|
|
|
super.deactivate(); |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void dispose() { |
|
|
|
_disposeVideoController(); |
|
|
|
super.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _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<MediaHelperBloc>(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<void> retrieveLostData( |
|
|
|
BuildContext context, |
|
|
|
) 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<void> _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<AspectRatioVideo> { |
|
|
|
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: <Widget>[ |
|
|
|
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(); |
|
|
|
} |
|
|
|
} |