From 5f3d4e5946221321e3bb225863ef55dbb6787e58 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sat, 1 Aug 2020 21:34:06 +0900 Subject: [PATCH 1/7] collection: added option to not group --- lib/model/source/collection_lens.dart | 9 +++++++- lib/widgets/album/app_bar.dart | 9 ++++++++ lib/widgets/album/grid/header_generic.dart | 24 +++++++++++----------- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index 7ba050181..0b1b1e2cf 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -85,6 +85,8 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel bool get showHeaders { if (sortFactor == SortFactor.size) return false; + if (sortFactor == SortFactor.date && groupFactor == GroupFactor.none) return false; + final albumSections = sortFactor == SortFactor.name || (sortFactor == SortFactor.date && groupFactor == GroupFactor.album); final filterByAlbum = filters.any((f) => f is AlbumFilter); if (albumSections && filterByAlbum) return false; @@ -160,6 +162,11 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel case GroupFactor.day: sections = groupBy(_filteredEntries, (entry) => entry.dayTaken); break; + case GroupFactor.none: + sections = Map.fromEntries([ + MapEntry(null, _filteredEntries), + ]); + break; } break; case SortFactor.size: @@ -209,7 +216,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel enum SortFactor { date, size, name } -enum GroupFactor { album, month, day } +enum GroupFactor { none, album, month, day } enum Activity { browse, select } diff --git a/lib/widgets/album/app_bar.dart b/lib/widgets/album/app_bar.dart index a2086dee7..0ce2ccf64 100644 --- a/lib/widgets/album/app_bar.dart +++ b/lib/widgets/album/app_bar.dart @@ -271,6 +271,10 @@ class _CollectionAppBarState extends State with SingleTickerPr value: CollectionAction.groupByDay, child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day), ), + PopupMenuItem( + value: CollectionAction.groupByNone, + child: MenuRow(text: 'Do not group', checked: collection.groupFactor == GroupFactor.none), + ), PopupMenuDivider(), ] : []; @@ -329,6 +333,10 @@ class _CollectionAppBarState extends State with SingleTickerPr settings.collectionGroupFactor = GroupFactor.day; collection.group(GroupFactor.day); break; + case CollectionAction.groupByNone: + settings.collectionGroupFactor = GroupFactor.none; + collection.group(GroupFactor.none); + break; case CollectionAction.sortByDate: settings.collectionSortFactor = SortFactor.date; collection.sort(SortFactor.date); @@ -375,6 +383,7 @@ enum CollectionAction { groupByAlbum, groupByMonth, groupByDay, + groupByNone, sortByDate, sortBySize, sortByName, diff --git a/lib/widgets/album/grid/header_generic.dart b/lib/widgets/album/grid/header_generic.dart index e10d7b1ea..59bbc8163 100644 --- a/lib/widgets/album/grid/header_generic.dart +++ b/lib/widgets/album/grid/header_generic.dart @@ -29,18 +29,18 @@ class SectionHeader extends StatelessWidget { Widget header; switch (collection.sortFactor) { case SortFactor.date: - if (collection.sortFactor == SortFactor.date) { - switch (collection.groupFactor) { - case GroupFactor.album: - header = _buildAlbumSectionHeader(); - break; - case GroupFactor.month: - header = MonthSectionHeader(key: ValueKey(sectionKey), date: sectionKey as DateTime); - break; - case GroupFactor.day: - header = DaySectionHeader(key: ValueKey(sectionKey), date: sectionKey as DateTime); - break; - } + switch (collection.groupFactor) { + case GroupFactor.album: + header = _buildAlbumSectionHeader(); + break; + case GroupFactor.month: + header = MonthSectionHeader(key: ValueKey(sectionKey), date: sectionKey as DateTime); + break; + case GroupFactor.day: + header = DaySectionHeader(key: ValueKey(sectionKey), date: sectionKey as DateTime); + break; + case GroupFactor.none: + break; } break; case SortFactor.size: From 5b338ba025116a4189c74217ee7b8c0655fd36ac Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 2 Aug 2020 15:43:23 +0900 Subject: [PATCH 2/7] album creation dialog: use radio buttons instead of dropdown --- .../action_delegates/create_album_dialog.dart | 89 ++++++++++--------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/lib/widgets/common/action_delegates/create_album_dialog.dart b/lib/widgets/common/action_delegates/create_album_dialog.dart index 51baf33a5..46a440077 100644 --- a/lib/widgets/common/action_delegates/create_album_dialog.dart +++ b/lib/widgets/common/action_delegates/create_album_dialog.dart @@ -16,6 +16,8 @@ class _CreateAlbumDialogState extends State { Set _allVolumes; StorageVolume _primaryVolume, _selectedVolume; + static const EdgeInsets hPadding = EdgeInsets.symmetric(horizontal: 24); + @override void initState() { super.initState(); @@ -35,56 +37,55 @@ class _CreateAlbumDialogState extends State { Widget build(BuildContext context) { return AlertDialog( title: Text('New Album'), - content: Column( - mainAxisSize: MainAxisSize.min, + content: ListView( + shrinkWrap: true, children: [ if (_allVolumes.length > 1) ...[ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Storage:'), - SizedBox(width: 8), - Expanded( - child: DropdownButton( - isExpanded: true, - items: _allVolumes - .map((volume) => DropdownMenuItem( - value: volume, - child: Text( - volume.description, - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 1, - ), - )) - .toList(), - value: _selectedVolume, - onChanged: (volume) { - _selectedVolume = volume; - _checkAlbumExists(); - setState(() {}); - }, - ), - ), - ], + Padding( + padding: hPadding, + child: Text('Storage:'), ), - SizedBox(height: 16), - ], - ValueListenableBuilder( - valueListenable: _existsNotifier, - builder: (context, exists, child) { - return TextField( - controller: _nameController, - decoration: InputDecoration( - helperText: exists ? 'Album already exists' : '', + ..._allVolumes.map((volume) => RadioListTile( + value: volume, + groupValue: _selectedVolume, + onChanged: (volume) { + _selectedVolume = volume; + _checkAlbumExists(); + setState(() {}); + }, + title: Text( + volume.description, + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, ), - onChanged: (_) => _checkAlbumExists(), - onSubmitted: (_) => _submit(context), - ); - }), + subtitle: Text( + volume.path, + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, + ), + )), + SizedBox(height: 8), + ], + Padding( + padding: hPadding, + child: ValueListenableBuilder( + valueListenable: _existsNotifier, + builder: (context, exists, child) { + return TextField( + controller: _nameController, + decoration: InputDecoration( + helperText: exists ? 'Album already exists' : '', + ), + onChanged: (_) => _checkAlbumExists(), + onSubmitted: (_) => _submit(context), + ); + }), + ), ], ), - contentPadding: EdgeInsets.fromLTRB(24, 20, 24, 0), + contentPadding: EdgeInsets.only(top: 20), actions: [ FlatButton( onPressed: () => Navigator.pop(context), From 93ebd25f70a494d063ed46747a089b348a3ffb9f Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 2 Aug 2020 15:57:18 +0900 Subject: [PATCH 3/7] changed dialog style --- lib/utils/constants.dart | 8 ++++++++ .../common/action_delegates/create_album_dialog.dart | 9 +++++---- .../common/action_delegates/entry_action_delegate.dart | 3 +++ .../common/action_delegates/permission_aware.dart | 3 +++ .../common/action_delegates/rename_entry_dialog.dart | 3 +++ .../action_delegates/selection_action_delegate.dart | 3 +++ 6 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 902979c8e..58381d2f7 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -22,6 +22,14 @@ class Constants { static const svgBackground = Colors.white; static const svgColorFilter = ColorFilter.mode(svgBackground, BlendMode.dstOver); + static const dialogContentHorizontalPadding = EdgeInsets.symmetric(horizontal: 24); + static const dialogActionsPadding = EdgeInsets.symmetric(horizontal: 8); + static const dialogShape = RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(24), + ), + ); + static const List androidDependencies = [ Dependency( name: 'CWAC-Document', diff --git a/lib/widgets/common/action_delegates/create_album_dialog.dart b/lib/widgets/common/action_delegates/create_album_dialog.dart index 46a440077..a9ebf26c1 100644 --- a/lib/widgets/common/action_delegates/create_album_dialog.dart +++ b/lib/widgets/common/action_delegates/create_album_dialog.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:aves/utils/android_file_utils.dart'; +import 'package:aves/utils/constants.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:path/path.dart'; @@ -16,8 +17,6 @@ class _CreateAlbumDialogState extends State { Set _allVolumes; StorageVolume _primaryVolume, _selectedVolume; - static const EdgeInsets hPadding = EdgeInsets.symmetric(horizontal: 24); - @override void initState() { super.initState(); @@ -42,7 +41,7 @@ class _CreateAlbumDialogState extends State { children: [ if (_allVolumes.length > 1) ...[ Padding( - padding: hPadding, + padding: Constants.dialogContentHorizontalPadding, child: Text('Storage:'), ), ..._allVolumes.map((volume) => RadioListTile( @@ -69,7 +68,7 @@ class _CreateAlbumDialogState extends State { SizedBox(height: 8), ], Padding( - padding: hPadding, + padding: Constants.dialogContentHorizontalPadding, child: ValueListenableBuilder( valueListenable: _existsNotifier, builder: (context, exists, child) { @@ -96,6 +95,8 @@ class _CreateAlbumDialogState extends State { child: Text('Create'.toUpperCase()), ), ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, ); } diff --git a/lib/widgets/common/action_delegates/entry_action_delegate.dart b/lib/widgets/common/action_delegates/entry_action_delegate.dart index b54a6ba50..36c36b235 100644 --- a/lib/widgets/common/action_delegates/entry_action_delegate.dart +++ b/lib/widgets/common/action_delegates/entry_action_delegate.dart @@ -4,6 +4,7 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/services/android_app_service.dart'; import 'package:aves/services/image_file_service.dart'; +import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/action_delegates/feedback.dart'; import 'package:aves/widgets/common/action_delegates/permission_aware.dart'; import 'package:aves/widgets/common/action_delegates/rename_entry_dialog.dart'; @@ -131,6 +132,8 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin { child: Text('Delete'.toUpperCase()), ), ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, ); }, ); diff --git a/lib/widgets/common/action_delegates/permission_aware.dart b/lib/widgets/common/action_delegates/permission_aware.dart index 59702f516..cddbfa92b 100644 --- a/lib/widgets/common/action_delegates/permission_aware.dart +++ b/lib/widgets/common/action_delegates/permission_aware.dart @@ -1,5 +1,6 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/services/android_file_service.dart'; +import 'package:aves/utils/constants.dart'; import 'package:flutter/material.dart'; mixin PermissionAwareMixin { @@ -35,6 +36,8 @@ mixin PermissionAwareMixin { child: Text('OK'.toUpperCase()), ), ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, ); }, ); diff --git a/lib/widgets/common/action_delegates/rename_entry_dialog.dart b/lib/widgets/common/action_delegates/rename_entry_dialog.dart index 2aeeb5291..d7fb28535 100644 --- a/lib/widgets/common/action_delegates/rename_entry_dialog.dart +++ b/lib/widgets/common/action_delegates/rename_entry_dialog.dart @@ -1,4 +1,5 @@ import 'package:aves/model/image_entry.dart'; +import 'package:aves/utils/constants.dart'; import 'package:flutter/material.dart'; class RenameEntryDialog extends StatefulWidget { @@ -43,6 +44,8 @@ class _RenameEntryDialogState extends State { child: Text('Apply'.toUpperCase()), ), ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, ); } } diff --git a/lib/widgets/common/action_delegates/selection_action_delegate.dart b/lib/widgets/common/action_delegates/selection_action_delegate.dart index 29bbe53da..67cb50b20 100644 --- a/lib/widgets/common/action_delegates/selection_action_delegate.dart +++ b/lib/widgets/common/action_delegates/selection_action_delegate.dart @@ -8,6 +8,7 @@ import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/services/android_app_service.dart'; import 'package:aves/services/image_file_service.dart'; +import 'package:aves/utils/constants.dart'; import 'package:aves/utils/durations.dart'; import 'package:aves/widgets/album/app_bar.dart'; import 'package:aves/widgets/album/empty.dart'; @@ -200,6 +201,8 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin { child: Text('Delete'.toUpperCase()), ), ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, ); }, ); From d3e0dd93757195214ed0c836e7da3960d73422b9 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 2 Aug 2020 16:42:44 +0900 Subject: [PATCH 4/7] collection: replaced sort/group menu items by selection dialogs --- lib/widgets/album/app_bar.dart | 109 +++++------------- .../group_collection_dialog.dart | 61 ++++++++++ .../sort_collection_dialog.dart | 60 ++++++++++ lib/widgets/common/icons.dart | 1 + 4 files changed, 154 insertions(+), 77 deletions(-) create mode 100644 lib/widgets/common/action_delegates/group_collection_dialog.dart create mode 100644 lib/widgets/common/action_delegates/sort_collection_dialog.dart diff --git a/lib/widgets/album/app_bar.dart b/lib/widgets/album/app_bar.dart index 0ce2ccf64..2ac5de3c6 100644 --- a/lib/widgets/album/app_bar.dart +++ b/lib/widgets/album/app_bar.dart @@ -6,7 +6,9 @@ import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/utils/durations.dart'; import 'package:aves/widgets/album/filter_bar.dart'; import 'package:aves/widgets/album/search/search_delegate.dart'; +import 'package:aves/widgets/common/action_delegates/group_collection_dialog.dart'; import 'package:aves/widgets/common/action_delegates/selection_action_delegate.dart'; +import 'package:aves/widgets/common/action_delegates/sort_collection_dialog.dart'; import 'package:aves/widgets/common/app_bar_subtitle.dart'; import 'package:aves/widgets/common/data_providers/media_store_collection_provider.dart'; import 'package:aves/widgets/common/entry_actions.dart'; @@ -185,8 +187,15 @@ class _CollectionAppBarState extends State with SingleTickerPr itemBuilder: (context) { final hasSelection = collection.selection.isNotEmpty; return [ - ..._buildSortMenuItems(), - ..._buildGroupMenuItems(), + PopupMenuItem( + value: CollectionAction.sort, + child: MenuRow(text: 'Sort...', icon: AIcons.sort), + ), + if (collection.sortFactor == SortFactor.date) + PopupMenuItem( + value: CollectionAction.group, + child: MenuRow(text: 'Group...', icon: AIcons.group), + ), if (collection.isBrowsing) ...[ if (AvesApp.mode == AppMode.main) if (kDebugMode) @@ -204,6 +213,7 @@ class _CollectionAppBarState extends State with SingleTickerPr ), ], if (collection.isSelecting) ...[ + PopupMenuDivider(), PopupMenuItem( value: CollectionAction.copy, enabled: hasSelection, @@ -238,48 +248,6 @@ class _CollectionAppBarState extends State with SingleTickerPr ]; } - List> _buildSortMenuItems() { - return [ - PopupMenuItem( - value: CollectionAction.sortByDate, - child: MenuRow(text: 'Sort by date', checked: collection.sortFactor == SortFactor.date), - ), - PopupMenuItem( - value: CollectionAction.sortBySize, - child: MenuRow(text: 'Sort by size', checked: collection.sortFactor == SortFactor.size), - ), - PopupMenuItem( - value: CollectionAction.sortByName, - child: MenuRow(text: 'Sort by name', checked: collection.sortFactor == SortFactor.name), - ), - PopupMenuDivider(), - ]; - } - - List> _buildGroupMenuItems() { - return collection.sortFactor == SortFactor.date - ? [ - PopupMenuItem( - value: CollectionAction.groupByAlbum, - child: MenuRow(text: 'Group by album', checked: collection.groupFactor == GroupFactor.album), - ), - PopupMenuItem( - value: CollectionAction.groupByMonth, - child: MenuRow(text: 'Group by month', checked: collection.groupFactor == GroupFactor.month), - ), - PopupMenuItem( - value: CollectionAction.groupByDay, - child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day), - ), - PopupMenuItem( - value: CollectionAction.groupByNone, - child: MenuRow(text: 'Do not group', checked: collection.groupFactor == GroupFactor.none), - ), - PopupMenuDivider(), - ] - : []; - } - void _onActivityChange() { if (collection.isSelecting) { _browseToSelectAnimation.forward(); @@ -321,33 +289,25 @@ class _CollectionAppBarState extends State with SingleTickerPr case CollectionAction.stats: unawaited(_goToStats()); break; - case CollectionAction.groupByAlbum: - settings.collectionGroupFactor = GroupFactor.album; - collection.group(GroupFactor.album); + case CollectionAction.group: + final factor = await showDialog( + context: context, + builder: (context) => GroupCollectionDialog(), + ); + if (factor != null) { + settings.collectionGroupFactor = factor; + collection.group(factor); + } break; - case CollectionAction.groupByMonth: - settings.collectionGroupFactor = GroupFactor.month; - collection.group(GroupFactor.month); - break; - case CollectionAction.groupByDay: - settings.collectionGroupFactor = GroupFactor.day; - collection.group(GroupFactor.day); - break; - case CollectionAction.groupByNone: - settings.collectionGroupFactor = GroupFactor.none; - collection.group(GroupFactor.none); - break; - case CollectionAction.sortByDate: - settings.collectionSortFactor = SortFactor.date; - collection.sort(SortFactor.date); - break; - case CollectionAction.sortBySize: - settings.collectionSortFactor = SortFactor.size; - collection.sort(SortFactor.size); - break; - case CollectionAction.sortByName: - settings.collectionSortFactor = SortFactor.name; - collection.sort(SortFactor.name); + case CollectionAction.sort: + final factor = await showDialog( + context: context, + builder: (context) => SortCollectionDialog(), + ); + if (factor != null) { + settings.collectionSortFactor = factor; + collection.sort(factor); + } break; } } @@ -373,18 +333,13 @@ class _CollectionAppBarState extends State with SingleTickerPr enum CollectionAction { copy, + group, move, refresh, refreshMetadata, select, selectAll, selectNone, + sort, stats, - groupByAlbum, - groupByMonth, - groupByDay, - groupByNone, - sortByDate, - sortBySize, - sortByName, } diff --git a/lib/widgets/common/action_delegates/group_collection_dialog.dart b/lib/widgets/common/action_delegates/group_collection_dialog.dart new file mode 100644 index 000000000..744242af3 --- /dev/null +++ b/lib/widgets/common/action_delegates/group_collection_dialog.dart @@ -0,0 +1,61 @@ +import 'package:aves/model/settings.dart'; +import 'package:aves/model/source/collection_lens.dart'; +import 'package:aves/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class GroupCollectionDialog extends StatefulWidget { + @override + _GroupCollectionDialogState createState() => _GroupCollectionDialogState(); +} + +class _GroupCollectionDialogState extends State { + GroupFactor _selectedGroup; + + @override + void initState() { + super.initState(); + _selectedGroup = settings.collectionGroupFactor; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text('Group'), + content: ListView( + shrinkWrap: true, + children: [ + _buildRadioListTile(GroupFactor.album, 'By album'), + _buildRadioListTile(GroupFactor.month, 'By month'), + _buildRadioListTile(GroupFactor.day, 'By day'), + _buildRadioListTile(GroupFactor.none, 'Do not group'), + ], + ), + contentPadding: EdgeInsets.only(top: 20), + actions: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text('Cancel'.toUpperCase()), + ), + FlatButton( + onPressed: () => Navigator.pop(context, _selectedGroup), + child: Text('Apply'.toUpperCase()), + ), + ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, + ); + } + + Widget _buildRadioListTile(GroupFactor group, String title) => RadioListTile( + value: group, + groupValue: _selectedGroup, + onChanged: (group) => setState(() => _selectedGroup = group), + title: Text( + title, + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, + ), + ); +} diff --git a/lib/widgets/common/action_delegates/sort_collection_dialog.dart b/lib/widgets/common/action_delegates/sort_collection_dialog.dart new file mode 100644 index 000000000..b00143c0a --- /dev/null +++ b/lib/widgets/common/action_delegates/sort_collection_dialog.dart @@ -0,0 +1,60 @@ +import 'package:aves/model/settings.dart'; +import 'package:aves/model/source/collection_lens.dart'; +import 'package:aves/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class SortCollectionDialog extends StatefulWidget { + @override + _SortCollectionDialogState createState() => _SortCollectionDialogState(); +} + +class _SortCollectionDialogState extends State { + SortFactor _selectedSort; + + @override + void initState() { + super.initState(); + _selectedSort = settings.collectionSortFactor; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text('Sort'), + content: ListView( + shrinkWrap: true, + children: [ + _buildRadioListTile(SortFactor.date, 'By date'), + _buildRadioListTile(SortFactor.size, 'By size'), + _buildRadioListTile(SortFactor.name, 'By album & file name'), + ], + ), + contentPadding: EdgeInsets.only(top: 20), + actions: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text('Cancel'.toUpperCase()), + ), + FlatButton( + onPressed: () => Navigator.pop(context, _selectedSort), + child: Text('Apply'.toUpperCase()), + ), + ], + actionsPadding: Constants.dialogActionsPadding, + shape: Constants.dialogShape, + ); + } + + Widget _buildRadioListTile(SortFactor sort, String title) => RadioListTile( + value: sort, + groupValue: _selectedSort, + onChanged: (sort) => setState(() => _selectedSort = sort), + title: Text( + title, + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, + ), + ); +} diff --git a/lib/widgets/common/icons.dart b/lib/widgets/common/icons.dart index ef354b4bf..1c10cdcaf 100644 --- a/lib/widgets/common/icons.dart +++ b/lib/widgets/common/icons.dart @@ -32,6 +32,7 @@ class AIcons { static const IconData favourite = OMIcons.favoriteBorder; static const IconData favouriteActive = OMIcons.favorite; static const IconData goUp = OMIcons.arrowUpward; + static const IconData group = OMIcons.groupWork; static const IconData info = OMIcons.info; static const IconData openInNew = OMIcons.openInNew; static const IconData print = OMIcons.print; From 408afd4c9ddd3126b6740310e77e10ee3ab8f98b Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 2 Aug 2020 17:37:36 +0900 Subject: [PATCH 5/7] search: submitting query animate query chip like it was tapped --- .../album/search/expandable_filter_row.dart | 24 +++++++++---------- lib/widgets/album/search/search_delegate.dart | 14 +++++++++-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/widgets/album/search/expandable_filter_row.dart b/lib/widgets/album/search/expandable_filter_row.dart index f5ca689bf..fe57ffc53 100644 --- a/lib/widgets/album/search/expandable_filter_row.dart +++ b/lib/widgets/album/search/expandable_filter_row.dart @@ -9,12 +9,14 @@ class ExpandableFilterRow extends StatelessWidget { final String title; final Iterable filters; final ValueNotifier expandedNotifier; + final HeroType Function(CollectionFilter filter) heroTypeBuilder; final FilterCallback onPressed; const ExpandableFilterRow({ this.title, @required this.filters, this.expandedNotifier, + this.heroTypeBuilder, @required this.onPressed, }); @@ -59,12 +61,7 @@ class ExpandableFilterRow extends StatelessWidget { child: Wrap( spacing: horizontalPadding, runSpacing: verticalPadding, - children: filtersList - .map((filter) => AvesFilterChip( - filter: filter, - onPressed: onPressed, - )) - .toList(), + children: filtersList.map(_buildFilterChip).toList(), ), ); final list = Container( @@ -78,12 +75,7 @@ class ExpandableFilterRow extends StatelessWidget { physics: BouncingScrollPhysics(), padding: EdgeInsets.symmetric(horizontal: horizontalPadding), itemBuilder: (context, index) { - if (index >= filtersList.length) return null; - final filter = filtersList[index]; - return AvesFilterChip( - filter: filter, - onPressed: onPressed, - ); + return index < filtersList.length ? _buildFilterChip(filtersList[index]) : null; }, separatorBuilder: (context, index) => SizedBox(width: 8), itemCount: filtersList.length, @@ -109,4 +101,12 @@ class ExpandableFilterRow extends StatelessWidget { ) : filterChips; } + + Widget _buildFilterChip(CollectionFilter filter) { + return AvesFilterChip( + filter: filter, + heroType: heroTypeBuilder?.call(filter) ?? HeroType.onTap, + onPressed: onPressed, + ); + } } diff --git a/lib/widgets/album/search/search_delegate.dart b/lib/widgets/album/search/search_delegate.dart index 3618c8822..28cc29f7a 100644 --- a/lib/widgets/album/search/search_delegate.dart +++ b/lib/widgets/album/search/search_delegate.dart @@ -62,19 +62,23 @@ class ImageSearchDelegate extends SearchDelegate { child: ValueListenableBuilder( valueListenable: expandedSectionNotifier, builder: (context, expandedSection, child) { + var queryFilter = _buildQueryFilter(false); return ListView( padding: EdgeInsets.only(top: 8), children: [ _buildFilterRow( context: context, filters: [ - _buildQueryFilter(false), + queryFilter, FavouriteFilter(), MimeFilter(MimeTypes.anyImage), MimeFilter(MimeTypes.anyVideo), MimeFilter(MimeFilter.animated), MimeFilter(MimeTypes.svg), ].where((f) => f != null && containQuery(f.label)), + // usually perform hero animation only on tapped chips, + // but we also need to animate the query chip when it is selected by submitting the search query + heroTypeBuilder: (filter) => filter == queryFilter ? HeroType.always : HeroType.onTap, ), StreamBuilder( stream: source.eventBus.on(), @@ -118,11 +122,17 @@ class ImageSearchDelegate extends SearchDelegate { ); } - Widget _buildFilterRow({@required BuildContext context, String title, @required Iterable filters}) { + Widget _buildFilterRow({ + @required BuildContext context, + String title, + @required Iterable filters, + HeroType Function(CollectionFilter filter) heroTypeBuilder, + }) { return ExpandableFilterRow( title: title, filters: filters, expandedNotifier: expandedSectionNotifier, + heroTypeBuilder: heroTypeBuilder, onPressed: (filter) => _select(context, filter is QueryFilter ? QueryFilter(filter.query) : filter), ); } From 05917e08cc0f1cbfed1dcc87f5cab1147f255229 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 2 Aug 2020 18:05:49 +0900 Subject: [PATCH 6/7] filter bar: apply curve to removal animation --- lib/utils/durations.dart | 2 +- lib/widgets/album/filter_bar.dart | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/utils/durations.dart b/lib/utils/durations.dart index b397a0c2d..3bb1e1a4d 100644 --- a/lib/utils/durations.dart +++ b/lib/utils/durations.dart @@ -11,7 +11,7 @@ class Durations { // collection animations static const appBarTitleAnimation = Duration(milliseconds: 300); - static const filterBarRemovalAnimation = Duration(milliseconds: 200); + static const filterBarRemovalAnimation = Duration(milliseconds: 400); static const collectionOpOverlayAnimation = Duration(milliseconds: 300); static const collectionScalingBackgroundAnimation = Duration(milliseconds: 200); static const sectionHeaderAnimation = Duration(milliseconds: 200); diff --git a/lib/widgets/album/filter_bar.dart b/lib/widgets/album/filter_bar.dart index 53417a632..cfe71641c 100644 --- a/lib/widgets/album/filter_bar.dart +++ b/lib/widgets/album/filter_bar.dart @@ -45,7 +45,9 @@ class _FilterBarState extends State { listState.removeItem( index, animate - ? (context, animation) => FadeTransition( + ? (context, animation) { + animation = animation.drive(CurveTween(curve: Curves.easeInOutBack)); + return FadeTransition( opacity: animation, child: SizeTransition( axis: Axis.horizontal, @@ -55,7 +57,8 @@ class _FilterBarState extends State { child: _buildChip(filter), ), ), - ) + ); + } : (context, animation) => _buildChip(filter), duration: animate ? Durations.filterBarRemovalAnimation : Duration.zero, ); From fd6951e92e0e5eaa581cd6aa5284b5aa168c0a96 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 2 Aug 2020 19:29:49 +0900 Subject: [PATCH 7/7] version bump --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 0548ffb1b..3047fd531 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: A new Flutter application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1+13 +version: 1.1.2+14 # video_player (as of v0.10.8+2, backed by ExoPlayer): # - does not support content URIs (by default, but trivial by fork)