added recording overlay

This commit is contained in:
Myzel394 2022-08-17 21:40:09 +02:00
parent 0b0c2ce15f
commit c10cdb254f
5 changed files with 138 additions and 7 deletions

View File

@ -21,6 +21,8 @@
"mainScreenTakeVideoActionSaveVideo": "Video wird aufgenommen, halte still...",
"mainScreenTakeVideoActionUploadingVideo": "Video wird hochgeladen...",
"recordingOverlayIsRecording": "Aufnahme",
"loginScreenTitle": "Anmelden",
"loginScreenLoginError": "E-Mail oder Passwort inkorrekt",

View File

@ -21,6 +21,8 @@
"mainScreenTakeVideoActionSaveVideo": "Taking video, please hold still...",
"mainScreenTakeVideoActionUploadingVideo": "Uploading video...",
"recordingOverlayIsRecording": "Recording",
"loginScreenTitle": "Login",
"loginScreenLoginError": "Invalid password or email",

View File

@ -15,11 +15,13 @@ import 'package:quid_faciam_hodie/utils/auth_required.dart';
import 'package:quid_faciam_hodie/utils/loadable.dart';
import 'package:quid_faciam_hodie/widgets/animate_in_builder.dart';
import 'package:quid_faciam_hodie/widgets/fade_and_move_in_animation.dart';
import 'package:quid_faciam_hodie/widgets/icon_button_child.dart';
import 'package:quid_faciam_hodie/widgets/sheet_indicator.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'main_screen/change_camera_button.dart';
import 'main_screen/record_button.dart';
import 'main_screen/recording_overlay.dart';
import 'main_screen/today_photo_button.dart';
import 'main_screen/uploading_photo.dart';
@ -178,6 +180,12 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
message: localizations.mainScreenTakePhotoActionTakingPhoto,
);
if (isTorchEnabled) {
await controller!.setFlashMode(FlashMode.torch);
} else {
await controller!.setFlashMode(FlashMode.off);
}
final file = File((await controller!.takePicture()).path);
setState(() {
@ -302,8 +310,11 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
child: AspectRatio(
aspectRatio: 1 / controller!.value.aspectRatio,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
controller!.buildPreview(),
if (isRecording)
RecordingOverlay(controller: controller!),
if (uploadingPhotoAnimation != null)
UploadingPhoto(
data: uploadingPhotoAnimation!,
@ -347,7 +358,7 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
opacityDuration: DEFAULT_OPACITY_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
child: ChangeCameraButton(
disabled: lockCamera,
disabled: lockCamera || isRecording,
onChangeCamera: () {
final currentCameraIndex = GlobalValuesManager
.cameras
@ -421,10 +432,7 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton.icon(
icon: const Icon(Icons.flashlight_on_rounded),
label: Text(AppLocalizations.of(context)!
.mainScreenActionsTorchButton),
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith<Color>(
@ -446,6 +454,10 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
}
});
},
child: IconButtonChild(
icon: const Icon(Icons.flashlight_on_rounded),
label: Text(localizations.mainScreenActionsTorchButton),
),
),
ElevatedButton(
style: ButtonStyle(

View File

@ -29,7 +29,7 @@ class _RecordButtonState extends State<RecordButton> {
bool videoInAnimationActive = false;
void cancelAnimation() {
if (videoInAnimationActive || animateToVideoIcon) {
if (videoInAnimationActive) {
return;
}
@ -168,7 +168,7 @@ class _RecordButtonState extends State<RecordButton> {
decoration: BoxDecoration(
color: videoInAnimationActive ? Colors.red : Colors.white,
borderRadius: videoInAnimationActive
? BorderRadius.circular(4)
? BorderRadius.circular(8)
: BorderRadius.circular(50),
),
),

View File

@ -0,0 +1,115 @@
import 'dart:async';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:quid_faciam_hodie/constants/spacing.dart';
class RecordingOverlay extends StatefulWidget {
final CameraController controller;
const RecordingOverlay({
Key? key,
required this.controller,
}) : super(key: key);
@override
State<RecordingOverlay> createState() => _RecordingOverlayState();
}
class _RecordingOverlayState extends State<RecordingOverlay> {
late final Timer _timer;
bool animateIn = false;
bool initialAnimateIn = false;
int recordingTime = 0;
@override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
setState(() {
if (!mounted) {
return;
}
recordingTime++;
animateIn = !animateIn;
});
});
WidgetsBinding.instance.addPostFrameCallback((_) {
initialAnimateIn = true;
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
String getFormattedTime() {
final minutes = recordingTime ~/ 60;
final seconds = recordingTime % 60;
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
return Positioned(
left: 0,
top: SMALL_SPACE,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 300),
opacity: initialAnimateIn ? 1.0 : 0.0,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedOpacity(
curve: Curves.linear,
opacity: animateIn ? 1.0 : 0.0,
duration: const Duration(seconds: 1),
child: Icon(
Icons.circle,
size: platformThemeData(
context,
material: (data) => data.textTheme.subtitle1!.fontSize,
cupertino: (data) => data.textTheme.textStyle.fontSize,
),
color: Colors.red,
),
),
const SizedBox(width: SMALL_SPACE),
Text(
localizations.recordingOverlayIsRecording,
style: platformThemeData(
context,
material: (data) => data.textTheme.bodyLarge,
cupertino: (data) => data.textTheme.textStyle,
),
),
const SizedBox(width: SMALL_SPACE),
Text(
getFormattedTime(),
style: platformThemeData(
context,
material: (data) => data.textTheme.bodyLarge,
cupertino: (data) => data.textTheme.textStyle,
),
),
],
),
),
),
);
}
}