diff --git a/lib/widgets/album/all_collection_page.dart b/lib/widgets/album/all_collection_page.dart index 2ae28300f..98338c846 100644 --- a/lib/widgets/album/all_collection_page.dart +++ b/lib/widgets/album/all_collection_page.dart @@ -14,55 +14,61 @@ class AllCollectionPage extends StatelessWidget { @override Widget build(BuildContext context) { debugPrint('$runtimeType build'); - final collection = Provider.of(context); return ThumbnailCollection( - collection: collection, - appBar: SliverAppBar( - title: const Text('All'), - actions: [ - IconButton( - icon: Icon(OMIcons.search), - onPressed: () => showSearch( - context: context, - delegate: ImageSearchDelegate(collection), - ), + appBar: _AllCollectionAppBar(), + ); + } +} + +class _AllCollectionAppBar extends StatelessWidget { + @override + Widget build(BuildContext context) { + final collection = Provider.of(context); + return SliverAppBar( + title: const Text('All'), + actions: [ + IconButton( + icon: Icon(OMIcons.search), + onPressed: () => showSearch( + context: context, + delegate: ImageSearchDelegate(collection), ), - PopupMenuButton( - itemBuilder: (context) => [ + ), + PopupMenuButton( + itemBuilder: (context) => [ + PopupMenuItem( + value: AlbumAction.sortByDate, + child: MenuRow(text: 'Sort by date', checked: collection.sortFactor == SortFactor.date), + ), + PopupMenuItem( + value: AlbumAction.sortBySize, + child: MenuRow(text: 'Sort by size', checked: collection.sortFactor == SortFactor.size), + ), + const PopupMenuDivider(), + if (collection.sortFactor == SortFactor.date) ...[ PopupMenuItem( - value: AlbumAction.sortByDate, - child: MenuRow(text: 'Sort by date', checked: collection.sortFactor == SortFactor.date), + value: AlbumAction.groupByAlbum, + child: MenuRow(text: 'Group by album', checked: collection.groupFactor == GroupFactor.album), ), PopupMenuItem( - value: AlbumAction.sortBySize, - child: MenuRow(text: 'Sort by size', checked: collection.sortFactor == SortFactor.size), + value: AlbumAction.groupByMonth, + child: MenuRow(text: 'Group by month', checked: collection.groupFactor == GroupFactor.month), + ), + PopupMenuItem( + value: AlbumAction.groupByDay, + child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day), ), const PopupMenuDivider(), - if (collection.sortFactor == SortFactor.date) ...[ - PopupMenuItem( - value: AlbumAction.groupByAlbum, - child: MenuRow(text: 'Group by album', checked: collection.groupFactor == GroupFactor.album), - ), - PopupMenuItem( - value: AlbumAction.groupByMonth, - child: MenuRow(text: 'Group by month', checked: collection.groupFactor == GroupFactor.month), - ), - PopupMenuItem( - value: AlbumAction.groupByDay, - child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day), - ), - const PopupMenuDivider(), - ], - PopupMenuItem( - value: AlbumAction.debug, - child: MenuRow(text: 'Debug', icon: OMIcons.whatshot), - ), ], - onSelected: (action) => _onActionSelected(context, collection, action), - ), - ], - floating: true, - ), + PopupMenuItem( + value: AlbumAction.debug, + child: MenuRow(text: 'Debug', icon: OMIcons.whatshot), + ), + ], + onSelected: (action) => _onActionSelected(context, collection, action), + ), + ], + floating: true, ); } diff --git a/lib/widgets/album/filtered_collection_page.dart b/lib/widgets/album/filtered_collection_page.dart index 4b519e36e..15d7b7cf5 100644 --- a/lib/widgets/album/filtered_collection_page.dart +++ b/lib/widgets/album/filtered_collection_page.dart @@ -3,6 +3,7 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/album/thumbnail_collection.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class FilteredCollectionPage extends StatelessWidget { final ImageCollection collection; @@ -17,11 +18,13 @@ class FilteredCollectionPage extends StatelessWidget { Widget build(BuildContext context) { return MediaQueryDataProvider( child: Scaffold( - body: ThumbnailCollection( - collection: collection, - appBar: SliverAppBar( - title: Text(title), - floating: true, + body: ChangeNotifierProvider.value( + value: collection, + child: ThumbnailCollection( + appBar: SliverAppBar( + title: Text(title), + floating: true, + ), ), ), resizeToAvoidBottomInset: false, diff --git a/lib/widgets/album/search_delegate.dart b/lib/widgets/album/search_delegate.dart index d9d5dc8bb..d584fa495 100644 --- a/lib/widgets/album/search_delegate.dart +++ b/lib/widgets/album/search_delegate.dart @@ -4,6 +4,7 @@ import 'package:aves/widgets/album/thumbnail_collection.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:flutter/material.dart'; import 'package:outline_material_icons/outline_material_icons.dart'; +import 'package:provider/provider.dart'; class ImageSearchDelegate extends SearchDelegate { final ImageCollection collection; @@ -59,12 +60,13 @@ class ImageSearchDelegate extends SearchDelegate { return _EmptyContent(); } return MediaQueryDataProvider( - child: ThumbnailCollection( - collection: ImageCollection( + child: ChangeNotifierProvider.value( + value: ImageCollection( entries: matches, groupFactor: collection.groupFactor, sortFactor: collection.sortFactor, ), + child: ThumbnailCollection(), ), ); } diff --git a/lib/widgets/album/thumbnail_collection.dart b/lib/widgets/album/thumbnail_collection.dart index 65d5cf9f4..41c851e5a 100644 --- a/lib/widgets/album/thumbnail_collection.dart +++ b/lib/widgets/album/thumbnail_collection.dart @@ -1,46 +1,24 @@ import 'package:aves/model/image_collection.dart'; -import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/album/collection_section.dart'; import 'package:draggable_scrollbar/draggable_scrollbar.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class ThumbnailCollection extends AnimatedWidget { - final ImageCollection collection; +class ThumbnailCollection extends StatelessWidget { final Widget appBar; - - const ThumbnailCollection({ - Key key, - this.collection, - this.appBar, - }) : super(key: key, listenable: collection); - - @override - Widget build(BuildContext context) { - return ThumbnailCollectionContent( - collection: collection, - appBar: appBar, - ); - } -} - -class ThumbnailCollectionContent extends StatelessWidget { - final ImageCollection collection; - final Widget appBar; - - final Map> _sections; final ScrollController _scrollController = ScrollController(); - ThumbnailCollectionContent({ + ThumbnailCollection({ Key key, - @required this.collection, - @required this.appBar, - }) : _sections = collection.sections, - super(key: key); + this.appBar, + }) : super(key: key); @override Widget build(BuildContext context) { - final sectionKeys = _sections.keys.toList(); + final collection = Provider.of(context); + final sections = collection.sections; + final sectionKeys = sections.keys.toList(); + double topPadding = 0; if (appBar != null) { final topWidget = appBar; @@ -51,40 +29,43 @@ class ThumbnailCollectionContent extends StatelessWidget { } } + final scrollView = CustomScrollView( + controller: _scrollController, + slivers: [ + if (appBar != null) appBar, + ...sectionKeys.map((sectionKey) => SectionSliver( + // need key to prevent section header mismatch + // but it should not be unique key, otherwise sections are rebuilt when changing page + key: ValueKey(sectionKey), + collection: collection, + sections: sections, + sectionKey: sectionKey, + )), + SliverToBoxAdapter( + child: Selector( + selector: (c, mq) => mq.viewInsets.bottom, + builder: (c, mqViewInsetsBottom, child) { + return SizedBox(height: mqViewInsetsBottom); + }, + ), + ), + ], + ); + return SafeArea( child: Selector( selector: (c, mq) => mq.viewInsets.bottom, - builder: (c, mqViewInsetsBottom, child) => DraggableScrollbar.arrows( - controller: _scrollController, - padding: EdgeInsets.only( - // top padding to adjust scroll thumb - top: topPadding, - bottom: mqViewInsetsBottom, - ), - child: CustomScrollView( + builder: (c, mqViewInsetsBottom, child) { + return DraggableScrollbar.arrows( controller: _scrollController, - slivers: [ - if (appBar != null) appBar, - ...sectionKeys.map((sectionKey) { - Widget sliver = SectionSliver( - // need key to prevent section header mismatch - // but it should not be unique key, otherwise sections are rebuilt when changing page - key: ValueKey(sectionKey), - collection: collection, - sections: _sections, - sectionKey: sectionKey, - ); - if (sectionKey == sectionKeys.last) { - sliver = SliverPadding( - padding: EdgeInsets.only(bottom: mqViewInsetsBottom), - sliver: sliver, - ); - } - return sliver; - }), - ], - ), - ), + padding: EdgeInsets.only( + // top padding to adjust scroll thumb + top: topPadding, + bottom: mqViewInsetsBottom, + ), + child: scrollView, + ); + }, ), ); } diff --git a/lib/widgets/common/image_preview.dart b/lib/widgets/common/image_preview.dart index b1180cbf0..2a8a66467 100644 --- a/lib/widgets/common/image_preview.dart +++ b/lib/widgets/common/image_preview.dart @@ -36,7 +36,7 @@ class ImagePreviewState extends State with AfterInitMixin { @override void initState() { - debugPrint('$runtimeType initState path=${entry.path}'); +// debugPrint('$runtimeType initState path=${entry.path}'); super.initState(); _entryChangeNotifier = Listenable.merge([ entry.imageChangeNotifier, diff --git a/lib/widgets/common/providers/media_store_collection_provider.dart b/lib/widgets/common/providers/media_store_collection_provider.dart index 56a9396d9..951972954 100644 --- a/lib/widgets/common/providers/media_store_collection_provider.dart +++ b/lib/widgets/common/providers/media_store_collection_provider.dart @@ -10,12 +10,25 @@ import 'package:provider/provider.dart'; final _stopwatch = Stopwatch()..start(); -class MediaStoreCollectionProvider extends StatelessWidget { +class MediaStoreCollectionProvider extends StatefulWidget { final Widget child; + const MediaStoreCollectionProvider({@required this.child}); + + @override + _MediaStoreCollectionProviderState createState() => _MediaStoreCollectionProviderState(); +} + +class _MediaStoreCollectionProviderState extends State { + Future collectionFuture; + static const EventChannel eventChannel = EventChannel('deckers.thibault/aves/mediastore'); - const MediaStoreCollectionProvider({@required this.child}); + @override + void initState() { + super.initState(); + collectionFuture = _create(); + } Future _create() async { debugPrint('$runtimeType _create, elapsed=${_stopwatch.elapsed}'); @@ -57,10 +70,15 @@ class MediaStoreCollectionProvider extends StatelessWidget { @override Widget build(BuildContext context) { - return FutureProvider( - create: (context) => _create(), - initialData: ImageCollection(entries: []), - child: child, + return FutureBuilder( + future: collectionFuture, + builder: (futureContext, AsyncSnapshot snapshot) { + final collection = (snapshot.connectionState == ConnectionState.done && !snapshot.hasError) ? snapshot.data : ImageCollection(entries: []); + return ChangeNotifierProvider.value( + value: collection, + child: widget.child, + ); + }, ); } } diff --git a/lib/widgets/fullscreen/info/metadata_section.dart b/lib/widgets/fullscreen/info/metadata_section.dart index 7c2af764a..e63603758 100644 --- a/lib/widgets/fullscreen/info/metadata_section.dart +++ b/lib/widgets/fullscreen/info/metadata_section.dart @@ -18,8 +18,6 @@ class MetadataSection extends StatefulWidget { class MetadataSectionState extends State { Future _metadataLoader; - static const int maxValueLength = 140; - @override void initState() { super.initState(); @@ -46,49 +44,71 @@ class MetadataSectionState extends State { if (snapshot.hasError) return Text(snapshot.error.toString()); if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink(); final metadataMap = snapshot.data.cast(); - final directoryNames = metadataMap.keys.toList()..sort(); - - Widget content; - if (mqWidth > 400) { - final first = [], second = []; - var firstItemCount = 0, secondItemCount = 0; - var firstIndex = 0, secondIndex = directoryNames.length - 1; - while (firstIndex <= secondIndex) { - if (firstItemCount <= secondItemCount) { - final directoryName = directoryNames[firstIndex++]; - first.add(directoryName); - firstItemCount += 2 + metadataMap[directoryName].length; - } else { - final directoryName = directoryNames[secondIndex--]; - second.insert(0, directoryName); - secondItemCount += 2 + metadataMap[directoryName].length; - } - } - content = Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(child: getMetadataColumn(metadataMap, first)), - const SizedBox(width: 8), - Expanded(child: getMetadataColumn(metadataMap, second)), - ], - ); - } else { - content = getMetadataColumn(metadataMap, directoryNames); - } return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SectionRow('Metadata'), - content, + _MetadataSectionContent( + metadataMap: metadataMap, + split: mqWidth > 400, + ), ], ); }, ), ); } +} - Widget getMetadataColumn(Map metadataMap, Iterable directoryNames) { +class _MetadataSectionContent extends StatelessWidget { + final Map metadataMap; + final List directoryNames; + final bool split; + + _MetadataSectionContent({@required this.metadataMap, @required this.split}) : directoryNames = metadataMap.keys.toList()..sort(); + + @override + Widget build(BuildContext context) { + if (split) { + final first = [], second = []; + var firstItemCount = 0, secondItemCount = 0; + var firstIndex = 0, secondIndex = directoryNames.length - 1; + while (firstIndex <= secondIndex) { + if (firstItemCount <= secondItemCount) { + final directoryName = directoryNames[firstIndex++]; + first.add(directoryName); + firstItemCount += 2 + metadataMap[directoryName].length; + } else { + final directoryName = directoryNames[secondIndex--]; + second.insert(0, directoryName); + secondItemCount += 2 + metadataMap[directoryName].length; + } + } + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _MetadataColumn(metadataMap: metadataMap, directoryNames: first)), + const SizedBox(width: 8), + Expanded(child: _MetadataColumn(metadataMap: metadataMap, directoryNames: second)), + ], + ); + } else { + return _MetadataColumn(metadataMap: metadataMap, directoryNames: directoryNames); + } + } +} + +class _MetadataColumn extends StatelessWidget { + final Map metadataMap; + final List directoryNames; + + const _MetadataColumn({@required this.metadataMap, @required this.directoryNames}); + + static const int maxValueLength = 140; + + @override + Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [