fullscreen: fixed route transition
This commit is contained in:
parent
68766d0e17
commit
aafcc1da63
24 changed files with 186 additions and 175 deletions
|
@ -4,8 +4,8 @@ import 'package:aves/widgets/album/all_collection_drawer.dart';
|
|||
import 'package:aves/widgets/album/all_collection_page.dart';
|
||||
import 'package:aves/widgets/common/fake_app_bar.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/common/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/media_store_collection_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_store_collection_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
|
@ -64,7 +64,7 @@ class _HomePageState extends State<HomePage> {
|
|||
// TODO reduce permission check time
|
||||
// TODO TLAD ask android.permission.ACCESS_MEDIA_LOCATION (unredacted EXIF with scoped storage)
|
||||
final permissions = await PermissionHandler().requestPermissions([
|
||||
PermissionGroup.storage
|
||||
PermissionGroup.storage,
|
||||
]); // 350ms
|
||||
if (permissions[PermissionGroup.storage] != PermissionStatus.granted) {
|
||||
unawaited(SystemNavigator.pop());
|
||||
|
|
|
@ -60,7 +60,7 @@ class ImageCollection with ChangeNotifier {
|
|||
break;
|
||||
case SortFactor.size:
|
||||
sections = Map.fromEntries([
|
||||
MapEntry('All', _rawEntries)
|
||||
MapEntry('All', _rawEntries),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -181,7 +181,7 @@ class ImageEntry {
|
|||
return {
|
||||
addressDetails.countryName,
|
||||
addressDetails.adminArea,
|
||||
addressDetails.locality
|
||||
addressDetails.locality,
|
||||
}.where((part) => part != null && part.isNotEmpty).join(', ');
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ String _decimal2sexagesimal(final double dec) {
|
|||
final List<String> tmp = NumberFormat('0.0#####').format(_round(value, decimals: 10)).split('.');
|
||||
return <int>[
|
||||
int.parse(tmp[0]).abs(),
|
||||
int.parse(tmp[1])
|
||||
int.parse(tmp[1]),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,6 @@ List<String> toDMS(Tuple2<double, double> latLng) {
|
|||
final lng = latLng.item2;
|
||||
return [
|
||||
'${_decimal2sexagesimal(lat)} ${lat < 0 ? 'S' : 'N'}',
|
||||
'${_decimal2sexagesimal(lng)} ${lng < 0 ? 'W' : 'E'}'
|
||||
'${_decimal2sexagesimal(lng)} ${lng < 0 ? 'W' : 'E'}',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -60,22 +60,22 @@ class AllCollectionDrawer extends StatelessWidget {
|
|||
Row(children: [
|
||||
Icon(Icons.photo_library),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.imageCount}')
|
||||
Text('${collection.imageCount}'),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.video_library),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.videoCount}')
|
||||
Text('${collection.videoCount}'),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.photo_album),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.albumCount}')
|
||||
Text('${collection.albumCount}'),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.label),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.tagCount}')
|
||||
Text('${collection.tagCount}'),
|
||||
]),
|
||||
],
|
||||
),
|
||||
|
|
121
lib/widgets/album/collection_section.dart
Normal file
121
lib/widgets/album/collection_section.dart
Normal file
|
@ -0,0 +1,121 @@
|
|||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/sections.dart';
|
||||
import 'package:aves/widgets/album/thumbnail.dart';
|
||||
import 'package:aves/widgets/album/transparent_material_page_route.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||
|
||||
class SectionSliver extends StatelessWidget {
|
||||
final ImageCollection collection;
|
||||
final Map<dynamic, List<ImageEntry>> sections;
|
||||
final dynamic sectionKey;
|
||||
final double screenWidth;
|
||||
|
||||
const SectionSliver({
|
||||
Key key,
|
||||
@required this.collection,
|
||||
@required this.sections,
|
||||
@required this.sectionKey,
|
||||
@required this.screenWidth,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const columnCount = 4;
|
||||
return SliverStickyHeader(
|
||||
header: SectionHeader(
|
||||
collection: collection,
|
||||
sections: sections,
|
||||
sectionKey: sectionKey,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
// TODO TLAD find out why thumbnails are rebuilt (with `initState`) when:
|
||||
// - config change (show/hide status bar)
|
||||
// - navigating away/back
|
||||
(sliverContext, index) {
|
||||
final sectionEntries = sections[sectionKey];
|
||||
if (index >= sectionEntries.length) return null;
|
||||
final entry = sectionEntries[index];
|
||||
return GestureDetector(
|
||||
onTap: () => _showFullscreen(sliverContext, entry),
|
||||
child: Thumbnail(
|
||||
entry: entry,
|
||||
extent: screenWidth / columnCount,
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: sections[sectionKey].length,
|
||||
addAutomaticKeepAlives: false,
|
||||
addRepaintBoundaries: true,
|
||||
),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: columnCount,
|
||||
),
|
||||
),
|
||||
overlapsContent: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _showFullscreen(BuildContext context, ImageEntry entry) {
|
||||
Navigator.push(
|
||||
context,
|
||||
TransparentMaterialPageRoute(
|
||||
pageBuilder: (context, _, __) => FullscreenPage(
|
||||
collection: collection,
|
||||
initialUri: entry.uri,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionHeader extends StatelessWidget {
|
||||
final ImageCollection collection;
|
||||
final Map<dynamic, List<ImageEntry>> sections;
|
||||
final dynamic sectionKey;
|
||||
|
||||
const SectionHeader({
|
||||
Key key,
|
||||
@required this.collection,
|
||||
@required this.sections,
|
||||
@required this.sectionKey,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget header = const SizedBox.shrink();
|
||||
if (collection.sortFactor == SortFactor.date) {
|
||||
switch (collection.groupFactor) {
|
||||
case GroupFactor.album:
|
||||
Widget albumIcon = IconUtils.getAlbumIcon(context, sectionKey as String);
|
||||
if (albumIcon != null) {
|
||||
albumIcon = Material(
|
||||
type: MaterialType.circle,
|
||||
elevation: 3,
|
||||
color: Colors.transparent,
|
||||
shadowColor: Colors.black,
|
||||
child: albumIcon,
|
||||
);
|
||||
}
|
||||
header = TitleSectionHeader(
|
||||
leading: albumIcon,
|
||||
title: collection.getUniqueAlbumName(sectionKey as String, sections.keys.cast<String>()),
|
||||
);
|
||||
break;
|
||||
case GroupFactor.month:
|
||||
header = MonthSectionHeader(date: sectionKey as DateTime);
|
||||
break;
|
||||
case GroupFactor.day:
|
||||
header = DaySectionHeader(date: sectionKey as DateTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return IgnorePointer(
|
||||
child: header,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/thumbnail_collection.dart';
|
||||
import 'package:aves/widgets/common/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FilteredCollectionPage extends StatelessWidget {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/thumbnail_collection.dart';
|
||||
import 'package:aves/widgets/common/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ImageSearchDelegate extends SearchDelegate<ImageEntry> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:aves/utils/time_utils.dart';
|
||||
import 'package:aves/widgets/common/outlined_text.dart';
|
||||
import 'package:aves/widgets/common/fx/outlined_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/sections.dart';
|
||||
import 'package:aves/widgets/album/thumbnail.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
|
||||
import 'package:aves/widgets/album/collection_section.dart';
|
||||
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ThumbnailCollection extends AnimatedWidget {
|
||||
|
@ -103,126 +99,3 @@ class ThumbnailCollectionContent extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionSliver extends StatelessWidget {
|
||||
final ImageCollection collection;
|
||||
final Map<dynamic, List<ImageEntry>> sections;
|
||||
final dynamic sectionKey;
|
||||
final double screenWidth;
|
||||
|
||||
const SectionSliver({
|
||||
Key key,
|
||||
@required this.collection,
|
||||
@required this.sections,
|
||||
@required this.sectionKey,
|
||||
@required this.screenWidth,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const columnCount = 4;
|
||||
return SliverStickyHeader(
|
||||
header: SectionHeader(
|
||||
collection: collection,
|
||||
sections: sections,
|
||||
sectionKey: sectionKey,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
// TODO TLAD find out why thumbnails are rebuilt (with `initState`) when:
|
||||
// - config change (show/hide status bar)
|
||||
// - navigating away/back
|
||||
(sliverContext, index) {
|
||||
final sectionEntries = sections[sectionKey];
|
||||
if (index >= sectionEntries.length) return null;
|
||||
final entry = sectionEntries[index];
|
||||
return GestureDetector(
|
||||
onTap: () => _showFullscreen(sliverContext, entry),
|
||||
child: Thumbnail(
|
||||
entry: entry,
|
||||
extent: screenWidth / columnCount,
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: sections[sectionKey].length,
|
||||
addAutomaticKeepAlives: false,
|
||||
addRepaintBoundaries: true,
|
||||
),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: columnCount,
|
||||
),
|
||||
),
|
||||
overlapsContent: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _showFullscreen(BuildContext context, ImageEntry entry) {
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => FullscreenPage(
|
||||
// collection: collection,
|
||||
// initialUri: entry.uri,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// TODO TLAD consider the following to have transparency while popping fullscreen by drag down
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
opaque: false,
|
||||
pageBuilder: (BuildContext context, _, __) => FullscreenPage(
|
||||
collection: collection,
|
||||
initialUri: entry.uri,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionHeader extends StatelessWidget {
|
||||
final ImageCollection collection;
|
||||
final Map<dynamic, List<ImageEntry>> sections;
|
||||
final dynamic sectionKey;
|
||||
|
||||
const SectionHeader({
|
||||
Key key,
|
||||
@required this.collection,
|
||||
@required this.sections,
|
||||
@required this.sectionKey,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget header = const SizedBox.shrink();
|
||||
if (collection.sortFactor == SortFactor.date) {
|
||||
switch (collection.groupFactor) {
|
||||
case GroupFactor.album:
|
||||
Widget albumIcon = IconUtils.getAlbumIcon(context, sectionKey as String);
|
||||
if (albumIcon != null) {
|
||||
albumIcon = Material(
|
||||
type: MaterialType.circle,
|
||||
elevation: 3,
|
||||
color: Colors.transparent,
|
||||
shadowColor: Colors.black,
|
||||
child: albumIcon,
|
||||
);
|
||||
}
|
||||
header = TitleSectionHeader(
|
||||
leading: albumIcon,
|
||||
title: collection.getUniqueAlbumName(sectionKey as String, sections.keys.cast<String>()),
|
||||
);
|
||||
break;
|
||||
case GroupFactor.month:
|
||||
header = MonthSectionHeader(date: sectionKey as DateTime);
|
||||
break;
|
||||
case GroupFactor.day:
|
||||
header = DaySectionHeader(date: sectionKey as DateTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return IgnorePointer(
|
||||
child: header,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
16
lib/widgets/album/transparent_material_page_route.dart
Normal file
16
lib/widgets/album/transparent_material_page_route.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class TransparentMaterialPageRoute<T> extends PageRouteBuilder<T> {
|
||||
TransparentMaterialPageRoute({
|
||||
@required RoutePageBuilder pageBuilder,
|
||||
}) : super(pageBuilder: pageBuilder);
|
||||
|
||||
@override
|
||||
bool get opaque => false;
|
||||
|
||||
@override
|
||||
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
|
||||
return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ class ImagePreviewState extends State<ImagePreview> with AfterInitMixin {
|
|||
super.initState();
|
||||
_entryChangeNotifier = Listenable.merge([
|
||||
entry.imageChangeNotifier,
|
||||
entry.metadataChangeNotifier
|
||||
entry.metadataChangeNotifier,
|
||||
]);
|
||||
_entryChangeNotifier.addListener(_onEntryChange);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:aves/model/image_metadata.dart';
|
|||
import 'package:aves/model/metadata_db.dart';
|
||||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/widgets/common/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/common/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_action_delegate.dart';
|
||||
import 'package:aves/widgets/fullscreen/image_page.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/info_page.dart';
|
||||
|
@ -298,7 +298,7 @@ class FullscreenVerticalPageView extends StatefulWidget {
|
|||
|
||||
class _FullscreenVerticalPageViewState extends State<FullscreenVerticalPageView> {
|
||||
bool _isInitialScale = true;
|
||||
Color _background = Colors.black;
|
||||
ValueNotifier<Color> _backgroundColorNotifier = ValueNotifier(Colors.black);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -328,24 +328,25 @@ class _FullscreenVerticalPageViewState extends State<FullscreenVerticalPageView>
|
|||
}
|
||||
|
||||
void _onVerticalPageControllerChange() {
|
||||
setState(() => _background = _background.withOpacity(min(1.0, widget.verticalPager.page)));
|
||||
_backgroundColorNotifier.value = _backgroundColorNotifier.value.withOpacity(min(1.0, widget.verticalPager.page));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PageView(
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: widget.verticalPager,
|
||||
physics: _isInitialScale ? const PageScrollPhysics() : const NeverScrollableScrollPhysics(),
|
||||
onPageChanged: widget.onVerticalPageChanged,
|
||||
children: [
|
||||
Container(
|
||||
color: _background,
|
||||
child: const SizedBox(),
|
||||
),
|
||||
Container(
|
||||
color: _background,
|
||||
child: ImagePage(
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _backgroundColorNotifier,
|
||||
builder: (context, backgroundColor, child) => Container(
|
||||
color: backgroundColor,
|
||||
child: child,
|
||||
),
|
||||
child: PageView(
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: widget.verticalPager,
|
||||
physics: _isInitialScale ? const PageScrollPhysics() : const NeverScrollableScrollPhysics(),
|
||||
onPageChanged: widget.onVerticalPageChanged,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
ImagePage(
|
||||
collection: widget.collection,
|
||||
pageController: widget.horizontalPager,
|
||||
onTap: widget.onImageTap,
|
||||
|
@ -353,15 +354,15 @@ class _FullscreenVerticalPageViewState extends State<FullscreenVerticalPageView>
|
|||
onScaleChanged: (state) => setState(() => _isInitialScale = state == PhotoViewScaleState.initial),
|
||||
videoControllers: widget.videoControllers,
|
||||
),
|
||||
),
|
||||
NotificationListener(
|
||||
onNotification: (notification) {
|
||||
if (notification is BackUpNotification) widget.onImagePageRequested();
|
||||
return false;
|
||||
},
|
||||
child: InfoPage(collection: widget.collection, entry: widget.entry),
|
||||
),
|
||||
],
|
||||
NotificationListener(
|
||||
onNotification: (notification) {
|
||||
if (notification is BackUpNotification) widget.onImagePageRequested();
|
||||
return false;
|
||||
},
|
||||
child: InfoPage(collection: widget.collection, entry: widget.entry),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class BasicSection extends StatelessWidget {
|
|||
if (rotation != null) InfoRow('Rotation', '$rotation°');
|
||||
return [
|
||||
InfoRow('Duration', entry.durationText),
|
||||
if (rotation != null) InfoRow('Rotation', '$rotation°')
|
||||
if (rotation != null) InfoRow('Rotation', '$rotation°'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/common/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/basic_section.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/location_section.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/metadata_section.dart';
|
||||
|
|
|
@ -17,7 +17,7 @@ class LocationSection extends AnimatedWidget {
|
|||
key: key,
|
||||
listenable: Listenable.merge([
|
||||
entry.metadataChangeNotifier,
|
||||
entry.addressChangeNotifier
|
||||
entry.addressChangeNotifier,
|
||||
]));
|
||||
|
||||
@override
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:aves/model/image_metadata.dart';
|
|||
import 'package:aves/model/metadata_service.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/utils/geo_utils.dart';
|
||||
import 'package:aves/widgets/common/blurred.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:aves/widgets/common/blurred.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OverlayButton extends StatelessWidget {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/utils/android_app_service.dart';
|
||||
import 'package:aves/utils/time_utils.dart';
|
||||
import 'package:aves/widgets/common/blurred.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:aves/widgets/fullscreen/overlay/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
|
Loading…
Reference in a new issue