added empty screen; improvements & bugfixes

This commit is contained in:
Myzel394 2022-08-18 00:03:00 +02:00
parent 927b15b63f
commit e8c850f5f0
16 changed files with 379 additions and 222 deletions

File diff suppressed because one or more lines are too long

View File

@ -25,7 +25,7 @@ class Memory {
creationDate: DateTime.parse(jsonData['created_at']), creationDate: DateTime.parse(jsonData['created_at']),
location: jsonData['location'], location: jsonData['location'],
isPublic: jsonData['is_public'], isPublic: jsonData['is_public'],
userID: jsonData['user'], userID: jsonData['user_id'],
); );
} }

View File

@ -52,5 +52,11 @@
"memorySheetDownloadMemory": "In Gallerie speichern", "memorySheetDownloadMemory": "In Gallerie speichern",
"memorySheetUpdateMemoryMakePublic": "Veröffentlichen", "memorySheetUpdateMemoryMakePublic": "Veröffentlichen",
"memorySheetUpdateMemoryMakePrivate": "Privat machen", "memorySheetUpdateMemoryMakePrivate": "Privat machen",
"memorySheetDeleteMemory": "Erinnerung löschen" "memorySheetDeleteMemory": "Erinnerung löschen",
"emptyScreenTitle": "Houston, wir haben ein Problem",
"emptyScreenSubtitle": "Der Benutzer hat noch keine Erinnerungen erstellt!",
"emptyScreenDescription": "Um deinen Zeitstrahl sehen zu können musst du zuerst ein paar Erinnerungen erstellen! :)",
"emptyScreenCreateMemory": "Erinnerung erstellen"
} }

View File

@ -52,5 +52,11 @@
"memorySheetDownloadMemory": "Download to Gallery", "memorySheetDownloadMemory": "Download to Gallery",
"memorySheetUpdateMemoryMakePublic": "Make Public", "memorySheetUpdateMemoryMakePublic": "Make Public",
"memorySheetUpdateMemoryMakePrivate": "Make Private", "memorySheetUpdateMemoryMakePrivate": "Make Private",
"memorySheetDeleteMemory": "Delete Memory" "memorySheetDeleteMemory": "Delete Memory",
"emptyScreenTitle": "Houston, we have a problem",
"emptyScreenSubtitle": "The user hasn't created any memories yet!",
"emptyScreenDescription": "To view your timeline you need to create some memories first! :)",
"emptyScreenCreateMemory": "Create a Memory"
} }

View File

@ -15,6 +15,7 @@ import 'package:quid_faciam_hodie/screens/welcome_screen.dart';
import 'managers/global_values_manager.dart'; import 'managers/global_values_manager.dart';
import 'models/memories.dart'; import 'models/memories.dart';
import 'screens/empty_screen.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -62,6 +63,7 @@ class _MyAppState extends State<MyApp> {
GrantPermissionScreen.ID: (context) => const GrantPermissionScreen(), GrantPermissionScreen.ID: (context) => const GrantPermissionScreen(),
CalendarScreen.ID: (context) => const CalendarScreen(), CalendarScreen.ID: (context) => const CalendarScreen(),
ServerLoadingScreen.ID: (context) => const ServerLoadingScreen(), ServerLoadingScreen.ID: (context) => const ServerLoadingScreen(),
EmptyScreen.ID: (context) => const EmptyScreen(),
}, },
initialRoute: ServerLoadingScreen.ID, initialRoute: ServerLoadingScreen.ID,
), ),

View File

@ -47,7 +47,7 @@ class FileManager {
} }
final memoryResponse = await supabase.from('memories').insert({ final memoryResponse = await supabase.from('memories').insert({
'user': user.id, 'user_id': user.id,
'location': path, 'location': path,
}).execute(); }).execute();
@ -60,7 +60,7 @@ class FileManager {
final response = await supabase final response = await supabase
.from('memories') .from('memories')
.select() .select()
.eq('user', user.id) .eq('user_id', user.id)
.order('created_at', ascending: false) .order('created_at', ascending: false)
.limit(1) .limit(1)
.single() .single()

View File

@ -39,7 +39,6 @@ class Memories extends PropertyChangeNotifier<String> {
} }
void removeMemoryByID(final String id) { void removeMemoryByID(final String id) {
print("remooooooved");
_memories.removeWhere((memory) => memory.id == id); _memories.removeWhere((memory) => memory.id == id);
notifyListeners('memories'); notifyListeners('memories');
} }
@ -93,8 +92,9 @@ class Memories extends PropertyChangeNotifier<String> {
final memory = Memory.parse(response.newRecord!); final memory = Memory.parse(response.newRecord!);
final id = response.oldRecord!['id']; final id = response.oldRecord!['id'];
removeMemoryByID(id); _memories.removeWhere((memory) => memory.id == id);
addMemory(memory); memories.add(memory);
break; break;
} }

View File

@ -0,0 +1,68 @@
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:lottie/lottie.dart';
import 'package:quid_faciam_hodie/constants/spacing.dart';
import 'package:quid_faciam_hodie/screens/main_screen.dart';
import 'package:quid_faciam_hodie/utils/theme.dart';
import '../widgets/icon_button_child.dart';
class EmptyScreen extends StatelessWidget {
static const ID = '/empty';
const EmptyScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
return PlatformScaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Lottie.asset('assets/lottie/flying-astronaut.json'),
const SizedBox(height: LARGE_SPACE),
Padding(
padding: const EdgeInsets.all(MEDIUM_SPACE),
child: Column(
children: <Widget>[
Text(
localizations.emptyScreenTitle,
textAlign: TextAlign.center,
style: getTitleTextStyle(context),
),
const SizedBox(height: MEDIUM_SPACE),
Text(
localizations.emptyScreenSubtitle,
textAlign: TextAlign.center,
style: getSubTitleTextStyle(context),
),
const SizedBox(height: SMALL_SPACE),
Text(
textAlign: TextAlign.center,
localizations.emptyScreenDescription,
style: getBodyTextTextStyle(context),
),
],
),
),
const SizedBox(height: MEDIUM_SPACE),
PlatformElevatedButton(
child: IconButtonChild(
icon: Icon(context.platformIcons.back),
label: Text(localizations.emptyScreenCreateMemory),
),
onPressed: () {
Navigator.pushNamedAndRemoveUntil(
context, MainScreen.ID, (_) => false);
},
),
],
),
),
);
}
}

View File

@ -4,6 +4,7 @@ import 'dart:typed_data';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart'; import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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:quid_faciam_hodie/constants/spacing.dart'; import 'package:quid_faciam_hodie/constants/spacing.dart';
@ -270,233 +271,244 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!; final localizations = AppLocalizations.of(context)!;
return PlatformScaffold( return WillPopScope(
backgroundColor: Colors.black, onWillPop: () async {
body: () { SystemChannels.platform.invokeMethod('SystemNavigator.pop');
if (isLoading) { SystemNavigator.pop();
return Center( exit(0);
child: Column( },
mainAxisAlignment: MainAxisAlignment.center, child: PlatformScaffold(
crossAxisAlignment: CrossAxisAlignment.center, backgroundColor: Colors.black,
children: <Widget>[ body: () {
PlatformCircularProgressIndicator(), if (isLoading) {
const SizedBox(height: MEDIUM_SPACE), return Center(
Text(
localizations.mainScreenLoadingCamera,
style: platformThemeData(
context,
material: (data) => data.textTheme.bodyText1,
cupertino: (data) => data.textTheme.textStyle,
),
),
],
),
);
}
return Container(
color: Colors.black,
child: ExpandableBottomSheet(
background: SafeArea(
child: Align(
alignment: Alignment.topCenter,
child: AnimateInBuilder(
builder: (showPreview) => AnimatedOpacity(
opacity: showPreview ? 1.0 : 0.0,
duration: const Duration(milliseconds: 1100),
curve: Curves.easeOutQuad,
child: ClipRRect(
borderRadius: BorderRadius.circular(SMALL_SPACE),
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!,
),
],
),
),
),
),
),
),
),
persistentHeader: Container(
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(LARGE_SPACE),
topRight: Radius.circular(LARGE_SPACE),
),
),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
const Padding( PlatformCircularProgressIndicator(),
padding: EdgeInsets.symmetric( const SizedBox(height: MEDIUM_SPACE),
vertical: MEDIUM_SPACE, Text(
horizontal: MEDIUM_SPACE, localizations.mainScreenLoadingCamera,
), style: platformThemeData(
child: SheetIndicator(), context,
), material: (data) => data.textTheme.bodyText1,
Padding( cupertino: (data) => data.textTheme.textStyle,
padding: const EdgeInsets.symmetric(vertical: SMALL_SPACE),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: FadeAndMoveInAnimation(
translationDuration: DEFAULT_TRANSLATION_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
opacityDuration: DEFAULT_OPACITY_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
child: ChangeCameraButton(
disabled: lockCamera || isRecording,
onChangeCamera: () {
final currentCameraIndex = GlobalValuesManager
.cameras
.indexOf(controller!.description);
final availableCameras =
GlobalValuesManager.cameras.length;
onNewCameraSelected(
GlobalValuesManager.cameras[
(currentCameraIndex + 1) %
availableCameras],
);
},
),
),
),
Expanded(
child: FadeAndMoveInAnimation(
child: RecordButton(
disabled: lockCamera,
active: isRecording,
onVideoBegin: () async {
setState(() {
isRecording = true;
});
if (controller!.value.isRecordingVideo) {
// A recording has already started, do nothing.
return;
}
await controller!.startVideoRecording();
},
onVideoEnd: takeVideo,
onPhotoShot: takePhoto,
),
),
),
Expanded(
child: FadeAndMoveInAnimation(
translationDuration: DEFAULT_TRANSLATION_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
opacityDuration: DEFAULT_OPACITY_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
child: TodayPhotoButton(
onLeave: () {
controller!.setFlashMode(FlashMode.off);
},
onComeBack: () {
if (isTorchEnabled) {
controller!.setFlashMode(FlashMode.torch);
}
},
),
),
),
],
), ),
), ),
], ],
), ),
), );
expandableContent: Container( }
color: Colors.black,
child: Padding(
padding: const EdgeInsets.only(
left: LARGE_SPACE,
right: LARGE_SPACE,
bottom: MEDIUM_SPACE,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith<Color>(
(_) => isTorchEnabled ? Colors.white : Colors.black,
),
foregroundColor:
MaterialStateProperty.resolveWith<Color>(
(_) => isTorchEnabled ? Colors.black : Colors.white,
),
),
onPressed: () {
setState(() {
isTorchEnabled = !isTorchEnabled;
if (isTorchEnabled) { return Container(
controller!.setFlashMode(FlashMode.torch); color: Colors.black,
} else { child: ExpandableBottomSheet(
controller!.setFlashMode(FlashMode.off); background: SafeArea(
} child: Align(
}); alignment: Alignment.topCenter,
}, child: AnimateInBuilder(
child: IconButtonChild( builder: (showPreview) => AnimatedOpacity(
icon: const Icon(Icons.flashlight_on_rounded), opacity: showPreview ? 1.0 : 0.0,
label: Text(localizations.mainScreenActionsTorchButton), duration: const Duration(milliseconds: 1100),
curve: Curves.easeOutQuad,
child: ClipRRect(
borderRadius: BorderRadius.circular(SMALL_SPACE),
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!,
),
],
),
),
), ),
), ),
ElevatedButton( ),
style: ButtonStyle( ),
backgroundColor: ),
MaterialStateProperty.resolveWith<Color>( persistentHeader: Container(
(_) => Colors.white10, decoration: const BoxDecoration(
), color: Colors.black,
foregroundColor: borderRadius: BorderRadius.only(
MaterialStateProperty.resolveWith<Color>( topLeft: Radius.circular(LARGE_SPACE),
(_) => Colors.white, topRight: Radius.circular(LARGE_SPACE),
), ),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.symmetric(
vertical: MEDIUM_SPACE,
horizontal: MEDIUM_SPACE,
), ),
onPressed: zoomLevels == null child: SheetIndicator(),
? null ),
: () { Padding(
final newZoomLevelIndex = padding:
((currentZoomLevelIndex + 1) % const EdgeInsets.symmetric(vertical: SMALL_SPACE),
zoomLevels!.length); child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: FadeAndMoveInAnimation(
translationDuration:
DEFAULT_TRANSLATION_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
opacityDuration: DEFAULT_OPACITY_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
child: ChangeCameraButton(
disabled: lockCamera || isRecording,
onChangeCamera: () {
final currentCameraIndex = GlobalValuesManager
.cameras
.indexOf(controller!.description);
final availableCameras =
GlobalValuesManager.cameras.length;
controller! onNewCameraSelected(
.setZoomLevel(zoomLevels![newZoomLevelIndex]); GlobalValuesManager.cameras[
(currentCameraIndex + 1) %
setState(() { availableCameras],
currentZoomLevelIndex = newZoomLevelIndex; );
}); },
}, ),
child: zoomLevels == null
? const Text('1x')
: Text(
formatZoomLevel(currentZoomLevel),
), ),
),
Expanded(
child: FadeAndMoveInAnimation(
child: RecordButton(
disabled: lockCamera,
active: isRecording,
onVideoBegin: () async {
setState(() {
isRecording = true;
});
if (controller!.value.isRecordingVideo) {
// A recording has already started, do nothing.
return;
}
await controller!.startVideoRecording();
},
onVideoEnd: takeVideo,
onPhotoShot: takePhoto,
),
),
),
Expanded(
child: FadeAndMoveInAnimation(
translationDuration:
DEFAULT_TRANSLATION_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
opacityDuration: DEFAULT_OPACITY_DURATION *
SECONDARY_BUTTONS_DURATION_MULTIPLIER,
child: TodayPhotoButton(
onLeave: () {
controller!.setFlashMode(FlashMode.off);
},
onComeBack: () {
if (isTorchEnabled) {
controller!.setFlashMode(FlashMode.torch);
}
},
),
),
),
],
),
), ),
], ],
), ),
), ),
expandableContent: Container(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.only(
left: LARGE_SPACE,
right: LARGE_SPACE,
bottom: MEDIUM_SPACE,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith<Color>(
(_) => isTorchEnabled ? Colors.white : Colors.black,
),
foregroundColor:
MaterialStateProperty.resolveWith<Color>(
(_) => isTorchEnabled ? Colors.black : Colors.white,
),
),
onPressed: () {
setState(() {
isTorchEnabled = !isTorchEnabled;
if (isTorchEnabled) {
controller!.setFlashMode(FlashMode.torch);
} else {
controller!.setFlashMode(FlashMode.off);
}
});
},
child: IconButtonChild(
icon: const Icon(Icons.flashlight_on_rounded),
label:
Text(localizations.mainScreenActionsTorchButton),
),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith<Color>(
(_) => Colors.white10,
),
foregroundColor:
MaterialStateProperty.resolveWith<Color>(
(_) => Colors.white,
),
),
onPressed: zoomLevels == null
? null
: () {
final newZoomLevelIndex =
((currentZoomLevelIndex + 1) %
zoomLevels!.length);
controller!.setZoomLevel(
zoomLevels![newZoomLevelIndex]);
setState(() {
currentZoomLevelIndex = newZoomLevelIndex;
});
},
child: zoomLevels == null
? const Text('1x')
: Text(
formatZoomLevel(currentZoomLevel),
),
),
],
),
),
),
), ),
), );
); }(),
}(), ),
); );
} }
} }

View File

@ -57,6 +57,14 @@ class _TodayPhotoButtonState extends State<TodayPhotoButton> {
final memories = context.read<Memories>(); final memories = context.read<Memories>();
if (memories.memories.isEmpty) {
setState(() {
data = null;
type = null;
});
return;
}
final lastMemory = memories.memories.first; final lastMemory = memories.memories.first;
final file = await lastMemory.downloadToFile(); final file = await lastMemory.downloadToFile();

View File

@ -7,12 +7,13 @@ import 'package:quid_faciam_hodie/managers/global_values_manager.dart';
import 'package:quid_faciam_hodie/models/memories.dart'; import 'package:quid_faciam_hodie/models/memories.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
import 'empty_screen.dart';
import 'main_screen.dart'; import 'main_screen.dart';
import 'server_loading_screen/dot_animation.dart'; import 'server_loading_screen/dot_animation.dart';
import 'welcome_screen.dart'; import 'welcome_screen.dart';
class ServerLoadingScreen extends StatefulWidget { class ServerLoadingScreen extends StatefulWidget {
static const ID = '/server_loading'; static const ID = '/';
final String? nextScreen; final String? nextScreen;
@ -44,12 +45,29 @@ class _ServerLoadingScreenState extends State<ServerLoadingScreen> {
await memories.initialize(); await memories.initialize();
} }
if (widget.nextScreen == null) {
Navigator.pushNamed(
context,
MainScreen.ID,
);
} else {
if (memories.memories.isEmpty) {
Navigator.pushReplacementNamed(
context,
EmptyScreen.ID,
);
} else {
Navigator.pushReplacementNamed(
context,
widget.nextScreen!,
);
}
}
} else {
Navigator.pushReplacementNamed( Navigator.pushReplacementNamed(
context, context,
widget.nextScreen ?? MainScreen.ID, WelcomeScreen.ID,
); );
} else {
Navigator.pushReplacementNamed(context, WelcomeScreen.ID);
} }
} }

View File

@ -8,6 +8,7 @@ import 'package:quid_faciam_hodie/utils/loadable.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
import 'calendar_screen.dart'; import 'calendar_screen.dart';
import 'empty_screen.dart';
import 'timeline_screen/timeline_page.dart'; import 'timeline_screen/timeline_page.dart';
final supabase = Supabase.instance.client; final supabase = Supabase.instance.client;
@ -49,6 +50,18 @@ class _TimelineScreenState extends State<TimelineScreen> with Loadable {
timeline.setCurrentIndex(initialIndex); timeline.setCurrentIndex(initialIndex);
memoriesModel.addListener(() { memoriesModel.addListener(() {
if (!mounted) {
return;
}
if (memoriesModel.memories.isEmpty) {
Navigator.pushReplacementNamed(
context,
EmptyScreen.ID,
);
return;
}
timeline.refresh(memoriesModel.memories); timeline.refresh(memoriesModel.memories);
setState(() {}); setState(() {});

View File

@ -8,7 +8,7 @@ import 'package:quid_faciam_hodie/widgets/logo.dart';
import 'grant_permission_screen.dart'; import 'grant_permission_screen.dart';
class WelcomeScreen extends StatelessWidget { class WelcomeScreen extends StatelessWidget {
static const ID = '/'; static const ID = '/welcome';
const WelcomeScreen({Key? key}) : super(key: key); const WelcomeScreen({Key? key}) : super(key: key);
@ -60,7 +60,7 @@ class WelcomeScreen extends StatelessWidget {
label: Text(localizations.welcomeScreenStartButtonTitle), label: Text(localizations.welcomeScreenStartButtonTitle),
), ),
onPressed: () { onPressed: () {
Navigator.pushReplacementNamed( Navigator.pushNamed(
context, context,
GrantPermissionScreen.ID, GrantPermissionScreen.ID,
); );

View File

@ -55,6 +55,13 @@ class _FadeAndMoveInAnimationState extends State<FadeAndMoveInAnimation>
); );
} }
@override
void dispose() {
translationController.dispose();
super.dispose();
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();

View File

@ -1,6 +1,13 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.1"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -329,6 +336,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
lottie:
dependency: "direct main"
description:
name: lottie
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:

View File

@ -54,6 +54,7 @@ dependencies:
flutter_sticky_header: ^0.6.4 flutter_sticky_header: ^0.6.4
flutter_calendar_widget: ^0.0.2 flutter_calendar_widget: ^0.0.2
flutter_platform_widgets: ^2.0.0 flutter_platform_widgets: ^2.0.0
lottie: ^1.4.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -80,6 +81,7 @@ flutter:
assets: assets:
- assets/ - assets/
- assets/lottie/flying-astronaut.json
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
# assets: # assets: