mirror of
https://github.com/Myzel394/quid_faciam_hodie.git
synced 2025-06-22 00:40:32 +02:00
improved UX
This commit is contained in:
parent
da2d658557
commit
120685ed2c
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user