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,7 +118,14 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
return; return;
} }
context.showPendingSnackBar(message: 'Taking photo...'); setState(() {
lockCamera = true;
});
try {
context.showPendingSnackBar(
message: 'Taking photo, please hold still...',
);
controller!.setFlashMode(FlashMode.off); controller!.setFlashMode(FlashMode.off);
final file = File((await controller!.takePicture()).path); final file = File((await controller!.takePicture()).path);
@ -136,6 +144,11 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
} }
context.showSuccessSnackBar(message: 'Photo uploaded!'); context.showSuccessSnackBar(message: 'Photo uploaded!');
} finally {
setState(() {
lockCamera = false;
});
}
if (mounted) { if (mounted) {
await getLastPhoto(); await getLastPhoto();
@ -143,15 +156,20 @@ 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;
}); });
try {
context.showPendingSnackBar(message: 'Saving video...'); context.showPendingSnackBar(message: 'Saving video...');
final file = File((await controller!.stopVideoRecording()).path); final file = File((await controller!.stopVideoRecording()).path);
@ -168,6 +186,11 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
} }
context.showSuccessSnackBar(message: 'Video uploaded!'); 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,40 +93,46 @@ 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(
Icons.circle,
size: 75,
color: Colors.white,
),
Icon(
Icons.circle,
size: 65,
color: Colors.red,
),
Icon(
Icons.stop,
size: 45,
color: Colors.white,
),
]
: <Widget>[
Icon( Icon(
Icons.circle, Icons.circle,
size: 75, size: 75,
color: Colors.white.withOpacity(.2), color: Colors.white.withOpacity(.2),
), ),
AnimatedScale( AnimatedScale(
duration: kLongPressTimeout, duration: animateToVideoIcon ? kLongPressTimeout : OUT_DURATION,
curve: Curves.easeInOut, curve: Curves.easeInOut,
scale: shrinkIcon ? .8 : 1, scale: animateToVideoIcon ? (75 / 50) : 1,
child: const Icon( child: const Icon(
Icons.circle, Icons.circle,
size: 50, size: 50,
color: Colors.white, color: Colors.white,
), ),
), ),
AnimatedScale(
curve: Curves.easeInOut,
duration: animateToVideoIcon
? const Duration(milliseconds: 180)
: OUT_DURATION,
scale: videoInAnimationActive ? 1 : 0,
child: const Icon(
Icons.circle,
size: 65,
color: Colors.red,
),
),
AnimatedScale(
curve: Curves.easeOutCirc,
duration: animateToVideoIcon
? const Duration(milliseconds: 1000)
: OUT_DURATION,
scale: videoInAnimationActive ? 1 : .6,
child: const Icon(
Icons.stop,
size: 45,
color: Colors.white,
),
),
], ],
), ),
), ),