improved UX

This commit is contained in:
Myzel394 2022-08-14 17:11:52 +02:00
parent da2d658557
commit 120685ed2c
3 changed files with 114 additions and 74 deletions

View File

@ -35,6 +35,7 @@ extension ShowSnackBar on BuildContext {
void showSuccessSnackBar({required final String message}) { void showSuccessSnackBar({required final String message}) {
showSnackBar( showSnackBar(
message: message, message: message,
duration: const Duration(milliseconds: 550),
backgroundColor: Colors.green, backgroundColor: Colors.green,
); );
} }

View File

@ -28,6 +28,7 @@ class MainScreen extends StatefulWidget {
class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable { class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
bool isRecording = false; bool isRecording = false;
bool hasGrantedPermissions = false; bool hasGrantedPermissions = false;
bool lockCamera = false;
List? lastPhoto; List? lastPhoto;
Uint8List? uploadingPhotoAnimation; Uint8List? uploadingPhotoAnimation;
@ -117,25 +118,37 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
return; return;
} }
context.showPendingSnackBar(message: 'Taking photo...');
controller!.setFlashMode(FlashMode.off);
final file = File((await controller!.takePicture()).path);
setState(() { setState(() {
uploadingPhotoAnimation = file.readAsBytesSync(); lockCamera = true;
}); });
context.showPendingSnackBar(message: 'Uploading photo...');
try { try {
await FileManager.uploadFile(_user, file); context.showPendingSnackBar(
} catch (error) { message: 'Taking photo, please hold still...',
context.showErrorSnackBar(message: error.toString()); );
return;
}
context.showSuccessSnackBar(message: 'Photo uploaded!'); controller!.setFlashMode(FlashMode.off);
final file = File((await controller!.takePicture()).path);
setState(() {
uploadingPhotoAnimation = file.readAsBytesSync();
});
context.showPendingSnackBar(message: 'Uploading photo...');
try {
await FileManager.uploadFile(_user, file);
} catch (error) {
context.showErrorSnackBar(message: error.toString());
return;
}
context.showSuccessSnackBar(message: 'Photo uploaded!');
} finally {
setState(() {
lockCamera = false;
});
}
if (mounted) { if (mounted) {
await getLastPhoto(); await getLastPhoto();
@ -143,31 +156,41 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
} }
Future<void> takeVideo() async { Future<void> takeVideo() async {
setState(() {
isRecording = false;
});
if (!controller!.value.isRecordingVideo) { if (!controller!.value.isRecordingVideo) {
// Recording has already been stopped // Recording has already been stopped
return; return;
} }
setState(() { setState(() {
isRecording = false; lockCamera = true;
}); });
context.showPendingSnackBar(message: 'Saving video...');
final file = File((await controller!.stopVideoRecording()).path);
context.showPendingSnackBar(message: 'Uploading video...');
try { try {
await FileManager.uploadFile(_user, file); context.showPendingSnackBar(message: 'Saving video...');
} catch (error) {
if (mounted) {
context.showErrorSnackBar(message: error.toString());
}
return;
}
context.showSuccessSnackBar(message: 'Video uploaded!'); final file = File((await controller!.stopVideoRecording()).path);
context.showPendingSnackBar(message: 'Uploading video...');
try {
await FileManager.uploadFile(_user, file);
} catch (error) {
if (mounted) {
context.showErrorSnackBar(message: error.toString());
}
return;
}
context.showSuccessSnackBar(message: 'Video uploaded!');
} finally {
setState(() {
lockCamera = false;
});
}
if (mounted) { if (mounted) {
await getLastPhoto(); await getLastPhoto();
@ -231,24 +254,25 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
}, },
), ),
CameraButton( CameraButton(
disabled: lockCamera,
active: isRecording, active: isRecording,
onVideoBegin: () async { onVideoBegin: () async {
setState(() {
isRecording = true;
});
if (controller!.value.isRecordingVideo) { if (controller!.value.isRecordingVideo) {
// A recording has already started, do nothing. // A recording has already started, do nothing.
return; return;
} }
setState(() {
isRecording = true;
});
await controller!.startVideoRecording(); await controller!.startVideoRecording();
}, },
onVideoEnd: takeVideo, onVideoEnd: takeVideo,
onPhotoShot: takePhoto, onPhotoShot: takePhoto,
), ),
lastPhoto == null lastPhoto == null
? TodayPhotoButton() ? const TodayPhotoButton()
: TodayPhotoButton( : TodayPhotoButton(
data: lastPhoto![0], data: lastPhoto![0],
type: lastPhoto![1], type: lastPhoto![1],
@ -266,7 +290,7 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
uploadingPhotoAnimation = null; uploadingPhotoAnimation = null;
}); });
}, },
) ),
], ],
), ),
); );

View File

@ -22,8 +22,11 @@ class CameraButton extends StatefulWidget {
State<CameraButton> createState() => _CameraButtonState(); State<CameraButton> createState() => _CameraButtonState();
} }
const OUT_DURATION = Duration(milliseconds: 300);
class _CameraButtonState extends State<CameraButton> { class _CameraButtonState extends State<CameraButton> {
bool shrinkIcon = false; bool animateToVideoIcon = false;
bool videoInAnimationActive = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -34,7 +37,8 @@ class _CameraButtonState extends State<CameraButton> {
} }
setState(() { setState(() {
shrinkIcon = false; videoInAnimationActive = false;
animateToVideoIcon = false;
}); });
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
@ -51,7 +55,7 @@ class _CameraButtonState extends State<CameraButton> {
} }
setState(() { setState(() {
shrinkIcon = true; animateToVideoIcon = true;
}); });
}, },
onLongPressUp: () { onLongPressUp: () {
@ -60,7 +64,8 @@ class _CameraButtonState extends State<CameraButton> {
} }
setState(() { setState(() {
shrinkIcon = false; videoInAnimationActive = false;
animateToVideoIcon = false;
}); });
if (widget.active) { if (widget.active) {
@ -74,6 +79,10 @@ class _CameraButtonState extends State<CameraButton> {
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
setState(() {
videoInAnimationActive = true;
});
if (widget.active) { if (widget.active) {
widget.onVideoEnd(); widget.onVideoEnd();
} else { } else {
@ -84,41 +93,47 @@ class _CameraButtonState extends State<CameraButton> {
opacity: widget.disabled ? 0.5 : 1.0, opacity: widget.disabled ? 0.5 : 1.0,
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: widget.active children: <Widget>[
? const <Widget>[ Icon(
Icon( Icons.circle,
Icons.circle, size: 75,
size: 75, color: Colors.white.withOpacity(.2),
color: Colors.white, ),
), AnimatedScale(
Icon( duration: animateToVideoIcon ? kLongPressTimeout : OUT_DURATION,
Icons.circle, curve: Curves.easeInOut,
size: 65, scale: animateToVideoIcon ? (75 / 50) : 1,
color: Colors.red, child: const Icon(
), Icons.circle,
Icon( size: 50,
Icons.stop, color: Colors.white,
size: 45, ),
color: Colors.white, ),
), AnimatedScale(
] curve: Curves.easeInOut,
: <Widget>[ duration: animateToVideoIcon
Icon( ? const Duration(milliseconds: 180)
Icons.circle, : OUT_DURATION,
size: 75, scale: videoInAnimationActive ? 1 : 0,
color: Colors.white.withOpacity(.2), child: const Icon(
), Icons.circle,
AnimatedScale( size: 65,
duration: kLongPressTimeout, color: Colors.red,
curve: Curves.easeInOut, ),
scale: shrinkIcon ? .8 : 1, ),
child: const Icon( AnimatedScale(
Icons.circle, curve: Curves.easeOutCirc,
size: 50, duration: animateToVideoIcon
color: Colors.white, ? const Duration(milliseconds: 1000)
), : OUT_DURATION,
), scale: videoInAnimationActive ? 1 : .6,
], child: const Icon(
Icons.stop,
size: 45,
color: Colors.white,
),
),
],
), ),
), ),
); );