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}) {
showSnackBar(
message: message,
duration: const Duration(milliseconds: 550),
backgroundColor: Colors.green,
);
}

View File

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

View File

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