added annotations; improvements & bugfixes

This commit is contained in:
Myzel394 2022-08-20 22:12:10 +02:00
parent 9f37648762
commit 5a8a0352c1
16 changed files with 349 additions and 124 deletions

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:quid_faciam_hodie/constants/values.dart'; import 'package:quid_faciam_hodie/constants/values.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
@ -9,12 +10,17 @@ extension ShowSnackBar on BuildContext {
static ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? static ScaffoldFeatureController<SnackBar, SnackBarClosedReason>?
pendingSnackBar; pendingSnackBar;
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar({ ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? showSnackBar({
required final String message, required final String message,
final Color backgroundColor = Colors.white, final Color backgroundColor = Colors.white,
final Duration duration = const Duration(seconds: 4), final Duration duration = const Duration(seconds: 4),
final BuildContext? context, final BuildContext? context,
}) { }) {
if (!isMaterial(context ?? this)) {
// Not implemented yet
return null;
}
pendingSnackBar?.close(); pendingSnackBar?.close();
pendingSnackBar = null; pendingSnackBar = null;

View File

@ -12,6 +12,7 @@ class Memory {
final String filePath; final String filePath;
final bool isPublic; final bool isPublic;
final String userID; final String userID;
final String annotation;
final MemoryLocation? location; final MemoryLocation? location;
const Memory({ const Memory({
@ -20,6 +21,7 @@ class Memory {
required this.filePath, required this.filePath,
required this.isPublic, required this.isPublic,
required this.userID, required this.userID,
required this.annotation,
this.location, this.location,
}); });
@ -29,6 +31,7 @@ class Memory {
filePath: jsonData['location'], filePath: jsonData['location'],
isPublic: jsonData['is_public'], isPublic: jsonData['is_public'],
userID: jsonData['user_id'], userID: jsonData['user_id'],
annotation: jsonData['annotation'],
location: MemoryLocation.parse(jsonData), location: MemoryLocation.parse(jsonData),
); );

View File

@ -4,6 +4,7 @@
"generalError": "There was an error", "generalError": "There was an error",
"generalCancelButtonLabel": "Cancel", "generalCancelButtonLabel": "Cancel",
"generalContinueButtonLabel": "Continue", "generalContinueButtonLabel": "Continue",
"generalSaveButtonLabel": "Save",
"generalUnderstoodButtonLabel": "OK", "generalUnderstoodButtonLabel": "OK",
"generalLoadingLabel": "Loading...", "generalLoadingLabel": "Loading...",
@ -52,6 +53,10 @@
"mainScreenHelpSheetTakePhotoExplanation": "Tap on the shutter button once to take a photo.", "mainScreenHelpSheetTakePhotoExplanation": "Tap on the shutter button once to take a photo.",
"mainScreenHelpSheetTakeVideoExplanation": "Hold down the shutter button to start recording a video. Leave the button to stop recording.", "mainScreenHelpSheetTakeVideoExplanation": "Hold down the shutter button to start recording a video. Leave the button to stop recording.",
"mainScreenAnnotationDialogTitle": "Add an annotation",
"mainScreenAnnotationDialogExplanation": "You can add an annotation to your memory",
"mainScreenAnnotationDialogAnnotationFieldLabel": "Annotation",
"recordingOverlayIsRecording": "Recording", "recordingOverlayIsRecording": "Recording",
@ -151,6 +156,7 @@
"settingsScreenDeleteAccountConfirmLabel": "Delete Account now", "settingsScreenDeleteAccountConfirmLabel": "Delete Account now",
"settingsScreenGeneralSectionTitle": "General", "settingsScreenGeneralSectionTitle": "General",
"settingsScreenGeneralSectionQualityLabel": "Quality", "settingsScreenGeneralSectionQualityLabel": "Quality",
"settingsScreenGeneralSectionAskForMemoryAnnotationsLabel": "Ask for memory annotations",
"settingsScreenResetHelpSheetsLabel": "Reset Help Sheets", "settingsScreenResetHelpSheetsLabel": "Reset Help Sheets",
"settingsScreenResetHelpSheetsResetSuccessfully": "Help Sheets reset successfully.", "settingsScreenResetHelpSheetsResetSuccessfully": "Help Sheets reset successfully.",

View File

@ -36,7 +36,8 @@ class FileManager {
static uploadFile( static uploadFile(
final User user, final User user,
final File file, { final File file, {
LocationData? locationData, final LocationData? locationData,
final Future<String?>? annotationGetterFuture,
}) async { }) async {
await GlobalValuesManager.waitForInitialization(); await GlobalValuesManager.waitForInitialization();
@ -65,6 +66,15 @@ class FileManager {
data['location_heading'] = locationData.heading!; data['location_heading'] = locationData.heading!;
} }
if (annotationGetterFuture != null) {
final annotation = await annotationGetterFuture;
if (annotation != null) {
// User has specified annotation
data['annotation'] = annotation;
}
}
final memoryResponse = final memoryResponse =
await supabase.from('memories').insert(data).execute(); await supabase.from('memories').insert(data).execute();

View File

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:quid_faciam_hodie/constants/storage_keys.dart'; import 'package:quid_faciam_hodie/constants/storage_keys.dart';
@ -9,14 +10,20 @@ const secure = FlutterSecureStorage();
class Settings extends ChangeNotifier { class Settings extends ChangeNotifier {
ResolutionPreset _resolution = ResolutionPreset.max; ResolutionPreset _resolution = ResolutionPreset.max;
bool _askForMemoryAnnotations = false;
Settings({final ResolutionPreset resolution = ResolutionPreset.max}) Settings({
: _resolution = resolution; final ResolutionPreset? resolution,
final bool? askForMemoryAnnotations,
}) : _resolution = resolution ?? ResolutionPreset.max,
_askForMemoryAnnotations = askForMemoryAnnotations ?? true;
ResolutionPreset get resolution => _resolution; ResolutionPreset get resolution => _resolution;
bool get askForMemoryAnnotations => _askForMemoryAnnotations;
Map<String, dynamic> toJSONData() => { Map<String, dynamic> toJSONData() => {
'resolution': _resolution.toString(), 'resolution': _resolution.toString(),
'askForMemoryAnnotations': _askForMemoryAnnotations ? 'true' : 'false',
}; };
Future<void> save() async { Future<void> save() async {
@ -36,11 +43,23 @@ class Settings extends ChangeNotifier {
} }
final data = jsonDecode(rawData); final data = jsonDecode(rawData);
final resolution = ResolutionPreset.values.firstWhere( final resolution = ResolutionPreset.values.firstWhereOrNull(
(preset) => preset.toString() == data['resolution'], (preset) => preset.toString() == data['resolution'],
); );
final askForMemoryAnnotations = () {
switch (data['askForMemoryAnnotations']) {
case 'true':
return true;
case 'false':
return false;
default:
return null;
}
}();
return Settings( return Settings(
resolution: resolution, resolution: resolution,
askForMemoryAnnotations: askForMemoryAnnotations,
); );
} }
@ -49,4 +68,10 @@ class Settings extends ChangeNotifier {
notifyListeners(); notifyListeners();
save(); save();
} }
void setAskForMemoryAnnotations(final bool askForMemoryAnnotations) {
_askForMemoryAnnotations = askForMemoryAnnotations;
notifyListeners();
save();
}
} }

View File

@ -82,10 +82,9 @@ class _LoginScreenState extends AuthState<LoginScreen> with Loadable {
await _signUp(); await _signUp();
} catch (error) { } catch (error) {
if (mounted) { if (mounted) {
if (isMaterial(context)) context.showLongErrorSnackBar(
context.showLongErrorSnackBar( message: localizations.loginScreenLoginFailed,
message: localizations.loginScreenLoginFailed, );
);
passwordController.clear(); passwordController.clear();
} }

View File

@ -17,6 +17,7 @@ import 'package:quid_faciam_hodie/constants/values.dart';
import 'package:quid_faciam_hodie/extensions/snackbar.dart'; import 'package:quid_faciam_hodie/extensions/snackbar.dart';
import 'package:quid_faciam_hodie/managers/file_manager.dart'; import 'package:quid_faciam_hodie/managers/file_manager.dart';
import 'package:quid_faciam_hodie/managers/global_values_manager.dart'; import 'package:quid_faciam_hodie/managers/global_values_manager.dart';
import 'package:quid_faciam_hodie/screens/main_screen/annotation_dialog.dart';
import 'package:quid_faciam_hodie/screens/main_screen/camera_help_content.dart'; import 'package:quid_faciam_hodie/screens/main_screen/camera_help_content.dart';
import 'package:quid_faciam_hodie/screens/main_screen/settings_button_overlay.dart'; import 'package:quid_faciam_hodie/screens/main_screen/settings_button_overlay.dart';
import 'package:quid_faciam_hodie/utils/auth_required.dart'; import 'package:quid_faciam_hodie/utils/auth_required.dart';
@ -188,6 +189,62 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
}); });
} }
Future<String?> _createAskAnnotationDialog() => showPlatformDialog(
barrierDismissible: true,
context: context,
builder: (dialogContext) => const AnnotationDialog(),
);
void _lockCamera() => setState(() {
lockCamera = true;
});
void _releaseCamera() => setState(() {
lockCamera = false;
});
void _showUploadingPhotoAnimation(final File file) => setState(() {
uploadingPhotoAnimation = file.readAsBytesSync();
});
void _releaseUploadingPhotoAnimation() => setState(() {
uploadingPhotoAnimation = null;
});
Future<String?> getAnnotation() async {
final settings = GlobalValuesManager.settings!;
if (settings.askForMemoryAnnotations) {
return _createAskAnnotationDialog();
} else {
return '';
}
}
Future<void> setFlashModeBeforeApplyingAction() async {
if (isTorchEnabled) {
await controller!.setFlashMode(FlashMode.torch);
} else {
await controller!.setFlashMode(FlashMode.off);
}
}
Future<LocationData?> getLocation([
final File? fileToTag,
]) async {
if (!(await Permission.location.isGranted)) {
return null;
}
final locationData = await Location().getLocation();
if (fileToTag != null && Platform.isAndroid) {
await tagLocationToImage(fileToTag, locationData);
}
return locationData;
}
Future<void> takePhoto() async { Future<void> takePhoto() async {
final localizations = AppLocalizations.of(context)!; final localizations = AppLocalizations.of(context)!;
@ -195,109 +252,94 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
return; return;
} }
setState(() { _lockCamera();
lockCamera = true;
});
try { try {
if (isMaterial(context)) context.showPendingSnackBar(
context.showPendingSnackBar( message: localizations.mainScreenTakePhotoActionTakingPhoto,
message: localizations.mainScreenTakePhotoActionTakingPhoto, );
);
if (isTorchEnabled) { await setFlashModeBeforeApplyingAction();
await controller!.setFlashMode(FlashMode.torch);
} else {
await controller!.setFlashMode(FlashMode.off);
}
final file = File((await controller!.takePicture()).path); final file = File((await controller!.takePicture()).path);
setState(() { final annotationGetterFuture = getAnnotation();
uploadingPhotoAnimation = file.readAsBytesSync(); final locationData = await getLocation(file);
});
if (isMaterial(context)) _showUploadingPhotoAnimation(file);
context.showPendingSnackBar(
message: localizations.mainScreenTakePhotoActionUploadingPhoto,
);
LocationData? locationData; context.showPendingSnackBar(
message: localizations.mainScreenTakePhotoActionUploadingPhoto,
if (await Permission.location.isGranted) { );
locationData = await Location().getLocation();
if (Platform.isAndroid) {
await tagLocationToImage(file, locationData);
}
}
try { try {
await FileManager.uploadFile(_user, file, locationData: locationData); await FileManager.uploadFile(
_user,
file,
locationData: locationData,
annotationGetterFuture: annotationGetterFuture,
);
} catch (error) { } catch (error) {
if (isMaterial(context)) context.showErrorSnackBar(message: error.toString());
context.showErrorSnackBar(message: error.toString());
return; return;
} }
if (isMaterial(context)) context.showSuccessSnackBar(
context.showSuccessSnackBar( message: localizations.mainScreenUploadSuccess,
message: localizations.mainScreenUploadSuccess, );
);
} finally { } finally {
setState(() { _releaseCamera();
lockCamera = false; _releaseUploadingPhotoAnimation();
uploadingPhotoAnimation = null;
});
} }
} }
Future<void> takeVideo() async { Future<void> takeVideo() async {
final localizations = AppLocalizations.of(context)!; final localizations = AppLocalizations.of(context)!;
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(() {
lockCamera = true; isRecording = false;
}); });
_lockCamera();
try { try {
if (isMaterial(context)) context.showPendingSnackBar(
context.showPendingSnackBar( message: localizations.mainScreenTakeVideoActionSaveVideo,
message: localizations.mainScreenTakeVideoActionSaveVideo, );
);
final file = File((await controller!.stopVideoRecording()).path); final file = File((await controller!.stopVideoRecording()).path);
if (isMaterial(context)) final annotationGetterFuture = getAnnotation();
context.showPendingSnackBar( final locationData = await getLocation();
message: localizations.mainScreenTakeVideoActionUploadingVideo,
); context.showPendingSnackBar(
message: localizations.mainScreenTakeVideoActionUploadingVideo,
);
try { try {
await FileManager.uploadFile(_user, file); await FileManager.uploadFile(
_user,
file,
annotationGetterFuture: annotationGetterFuture,
locationData: locationData,
);
} catch (error) { } catch (error) {
if (isMaterial(context)) { context.showErrorSnackBar(message: error.toString());
context.showErrorSnackBar(message: error.toString());
}
return; return;
} }
if (isMaterial(context)) context.showSuccessSnackBar(
context.showSuccessSnackBar( message: localizations.mainScreenUploadSuccess,
message: localizations.mainScreenUploadSuccess, );
);
} finally { } finally {
setState(() { _releaseCamera();
lockCamera = false;
});
} }
} }

View File

@ -0,0 +1,74 @@
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';
import 'package:quid_faciam_hodie/utils/theme.dart';
class AnnotationDialog extends StatefulWidget {
const AnnotationDialog({Key? key}) : super(key: key);
@override
State<AnnotationDialog> createState() => _AnnotationDialogState();
}
class _AnnotationDialogState extends State<AnnotationDialog> {
final TextEditingController controller = TextEditingController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
return PlatformAlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(MEDIUM_SPACE),
child: Column(
children: <Widget>[
Text(
localizations.mainScreenAnnotationDialogTitle,
style: getTitleTextStyle(context),
),
const SizedBox(height: MEDIUM_SPACE),
Text(
localizations.mainScreenAnnotationDialogExplanation,
style: getBodyTextTextStyle(context),
),
const SizedBox(height: MEDIUM_SPACE),
TextField(
controller: controller,
autofocus: true,
decoration: InputDecoration(
labelText: localizations
.mainScreenAnnotationDialogAnnotationFieldLabel,
),
onSubmitted: (value) {
Navigator.of(context).pop(value);
},
),
],
),
),
],
),
actions: <Widget>[
PlatformDialogAction(
child: Text(localizations.generalCancelButtonLabel),
onPressed: () => Navigator.pop(context),
),
PlatformDialogAction(
child: Text(localizations.generalSaveButtonLabel),
onPressed: () => Navigator.pop(context, controller.text.trim()),
),
],
);
}
}

View File

@ -83,7 +83,7 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
); );
} }
Widget getPicker() { Widget getQualityPicker() {
final settings = GlobalValuesManager.settings!; final settings = GlobalValuesManager.settings!;
final resolutionTextMapping = getResolutionTextMapping(context); final resolutionTextMapping = getResolutionTextMapping(context);
final items = ResolutionPreset.values final items = ResolutionPreset.values
@ -125,6 +125,7 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final settings = GlobalValuesManager.settings!;
final localizations = AppLocalizations.of(context)!; final localizations = AppLocalizations.of(context)!;
return PlatformScaffold( return PlatformScaffold(
@ -203,7 +204,15 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
localizations localizations
.settingsScreenGeneralSectionQualityLabel, .settingsScreenGeneralSectionQualityLabel,
), ),
title: getPicker(), title: getQualityPicker(),
),
SettingsTile.switchTile(
initialValue: settings.askForMemoryAnnotations,
onToggle: settings.setAskForMemoryAnnotations,
title: Text(
localizations
.settingsScreenGeneralSectionAskForMemoryAnnotationsLabel,
),
), ),
SettingsTile( SettingsTile(
leading: Icon(context.platformIcons.help), leading: Icon(context.platformIcons.help),
@ -213,12 +222,10 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
onPressed: (_) async { onPressed: (_) async {
await UserHelpSheetsManager.deleteAll(); await UserHelpSheetsManager.deleteAll();
if (isMaterial(context)) { context.showSuccessSnackBar(
context.showSuccessSnackBar( message: localizations
message: localizations .settingsScreenResetHelpSheetsResetSuccessfully,
.settingsScreenResetHelpSheetsResetSuccessfully, );
);
}
}, },
) )
], ],

View File

@ -65,13 +65,11 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
Navigator.pop(context); Navigator.pop(context);
if (isMaterial(context)) context.showSuccessSnackBar(
context.showSuccessSnackBar( message: localizations.memorySheetSavedToGallery,
message: localizations.memorySheetSavedToGallery, );
);
} catch (error) { } catch (error) {
if (isMaterial(context)) context.showErrorSnackBar(message: localizations.generalError);
context.showErrorSnackBar(message: localizations.generalError);
} }
} }
@ -94,10 +92,9 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
Navigator.pop(context); Navigator.pop(context);
if (isNowPublic) { if (isNowPublic) {
if (isMaterial(context)) context.showSuccessSnackBar(
context.showSuccessSnackBar( message: localizations.memorySheetMemoryUpdatedToPublic,
message: localizations.memorySheetMemoryUpdatedToPublic, );
);
} else { } else {
if (isMaterial(context)) if (isMaterial(context))
context.showSuccessSnackBar( context.showSuccessSnackBar(
@ -105,8 +102,7 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
); );
} }
} catch (error) { } catch (error) {
if (isMaterial(context)) context.showErrorSnackBar(message: localizations.generalError);
context.showErrorSnackBar(message: localizations.generalError);
} }
} }

View File

@ -1,12 +1,15 @@
import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:provider/provider.dart';
import 'package:quid_faciam_hodie/constants/spacing.dart'; import 'package:quid_faciam_hodie/constants/spacing.dart';
import 'package:quid_faciam_hodie/enums.dart'; import 'package:quid_faciam_hodie/enums.dart';
import 'package:quid_faciam_hodie/foreign_types/memory.dart'; import 'package:quid_faciam_hodie/foreign_types/memory.dart';
import 'package:quid_faciam_hodie/models/timeline.dart';
import 'package:quid_faciam_hodie/widgets/raw_memory_display.dart'; import 'package:quid_faciam_hodie/widgets/raw_memory_display.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
@ -37,6 +40,7 @@ class MemoryView extends StatefulWidget {
class _MemoryViewState extends State<MemoryView> { class _MemoryViewState extends State<MemoryView> {
MemoryFetchStatus status = MemoryFetchStatus.downloading; MemoryFetchStatus status = MemoryFetchStatus.downloading;
Uint8List? data; Uint8List? data;
Timer? _nextMemoryTimer;
@override @override
void initState() { void initState() {
@ -45,7 +49,16 @@ class _MemoryViewState extends State<MemoryView> {
loadMemoryFile(); loadMemoryFile();
} }
@override
void dispose() {
_nextMemoryTimer?.cancel();
super.dispose();
}
Future<void> loadMemoryFile() async { Future<void> loadMemoryFile() async {
final timeline = context.read<TimelineModel>();
setState(() { setState(() {
status = MemoryFetchStatus.downloading; status = MemoryFetchStatus.downloading;
}); });
@ -75,6 +88,12 @@ class _MemoryViewState extends State<MemoryView> {
setState(() { setState(() {
status = MemoryFetchStatus.error; status = MemoryFetchStatus.error;
}); });
_nextMemoryTimer = Timer(
const Duration(seconds: 1),
timeline.nextMemory,
);
return; return;
} }
} }

View File

@ -51,44 +51,70 @@ class TimelineOverlay extends StatelessWidget {
), ),
), ),
Positioned( Positioned(
right: SMALL_SPACE, left: 0,
right: 0,
bottom: SMALL_SPACE * 2, bottom: SMALL_SPACE * 2,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: SMALL_SPACE), padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE),
child: AnimatedOpacity( child: AnimatedOpacity(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
curve: Curves.linearToEaseOut, curve: Curves.linearToEaseOut,
opacity: timeline.showOverlay ? 1.0 : 0.0, opacity: timeline.showOverlay ? 1.0 : 0.0,
child: Row( child: Row(
mainAxisAlignment:
(timeline.currentMemory.annotation.isNotEmpty)
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
AnimatedOpacity( if (timeline.currentMemory.annotation.isNotEmpty)
opacity: timeline.currentMemory.isPublic ? 1.0 : 0.0, Text(
duration: const Duration(milliseconds: 500), timeline.currentMemory.annotation,
curve: Curves.linearToEaseOut, style: platformThemeData(
child: Icon(
Icons.public,
size: platformThemeData(
context, context,
material: (data) => data.textTheme.bodyLarge!.fontSize, material: (data) => data.textTheme.titleSmall!.copyWith(
cupertino: (data) => data.textTheme.textStyle.fontSize, color: Colors.white,
),
cupertino: (data) =>
data.textTheme.navTitleTextStyle.copyWith(
color: Colors.white,
),
), ),
color: Colors.white,
), ),
Row(
children: <Widget>[
AnimatedOpacity(
opacity: timeline.currentMemory.isPublic ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
curve: Curves.linearToEaseOut,
child: Icon(
Icons.public,
size: platformThemeData(
context,
material: (data) =>
data.textTheme.bodyLarge!.fontSize,
cupertino: (data) =>
data.textTheme.textStyle.fontSize,
),
color: Colors.white,
),
),
const SizedBox(width: SMALL_SPACE),
Text(
'$memoryIndex/$memoriesAmount',
style: platformThemeData(
context,
material: (data) =>
data.textTheme.titleSmall!.copyWith(
color: Colors.white,
),
cupertino: (data) =>
data.textTheme.navTitleTextStyle.copyWith(
color: Colors.white,
),
),
),
],
), ),
const SizedBox(width: SMALL_SPACE),
Text(
'$memoryIndex/$memoriesAmount',
style: platformThemeData(
context,
material: (data) => data.textTheme.titleSmall!.copyWith(
color: Colors.white,
),
cupertino: (data) =>
data.textTheme.navTitleTextStyle.copyWith(
color: Colors.white,
),
),
)
], ],
), ),
), ),

View File

@ -60,11 +60,9 @@ class KeyValueInfo extends StatelessWidget {
HapticFeedback.lightImpact(); HapticFeedback.lightImpact();
Clipboard.setData(ClipboardData(text: value)); Clipboard.setData(ClipboardData(text: value));
if (isMaterial(context)) { context.showSuccessSnackBar(
context.showSuccessSnackBar( message: 'Copied to clipboard!',
message: 'Copied to clipboard!', );
);
}
}, },
) )
: null, : null,

View File

@ -43,12 +43,18 @@ class _RawMemoryDisplayState extends State<RawMemoryDisplay> {
Future<File> createTempVideo() async { Future<File> createTempVideo() async {
final tempDirectory = await getTemporaryDirectory(); final tempDirectory = await getTemporaryDirectory();
final path = '${tempDirectory.path}/${widget.filename ?? 'video.mp4'}'; final path = '${tempDirectory.path}/${widget.filename ?? 'video.mp4'}';
print("#" * 50);
print(widget.filename);
print(path);
final file = File(path); final file = File(path);
print(await file.exists());
print(widget.data);
/*
if (await file.exists()) { if (await file.exists()) {
// File already exists, so just return it // File already exists, so just return it
return file; return file;
} }*/
// File needs to be created // File needs to be created
await file.create(); await file.create();

View File

@ -441,6 +441,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
morphing_text:
dependency: "direct main"
description:
name: morphing_text
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
nested: nested:
dependency: transitive dependency: transitive
description: description:

View File

@ -63,6 +63,7 @@ dependencies:
flutter_osm_plugin: ^0.39.0 flutter_osm_plugin: ^0.39.0
url_launcher: ^6.1.5 url_launcher: ^6.1.5
apple_maps_flutter: ^1.2.0 apple_maps_flutter: ^1.2.0
morphing_text: ^1.0.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: