diff --git a/lib/widgets/album/all_collection_drawer.dart b/lib/widgets/album/all_collection_drawer.dart index b8ebc6c5e..2fc52323e 100644 --- a/lib/widgets/album/all_collection_drawer.dart +++ b/lib/widgets/album/all_collection_drawer.dart @@ -12,14 +12,102 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:outline_material_icons/outline_material_icons.dart'; import 'package:provider/provider.dart'; -class AllCollectionDrawer extends StatelessWidget { +class AllCollectionDrawer extends StatefulWidget { const AllCollectionDrawer(); + @override + _AllCollectionDrawerState createState() => _AllCollectionDrawerState(); +} + +class _AllCollectionDrawerState extends State { + bool _regularAlbumsExpanded = false, _tagsExpanded = false; + @override Widget build(BuildContext context) { final collection = Provider.of(context); final source = collection.source; - final tags = source.sortedTags; + + final header = DrawerHeader( + decoration: BoxDecoration( + color: Theme.of(context).accentColor, + ), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CircleAvatar( + backgroundColor: Colors.white, + radius: 44, + child: Padding( + padding: const EdgeInsets.only(top: 6.0), + child: SvgPicture.asset( + 'assets/aves_logo.svg', + width: 64, + ), + ), + ), + const SizedBox(width: 16), + const Text( + 'Aves', + style: TextStyle( + fontSize: 44, + fontFamily: 'Concourse Caps', + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + const Icon(OMIcons.photoLibrary), + const SizedBox(width: 4), + Text('${collection.imageCount}'), + ]), + Row(children: [ + const Icon(OMIcons.videoLibrary), + const SizedBox(width: 4), + Text('${collection.videoCount}'), + ]), + Row(children: [ + const Icon(OMIcons.photoAlbum), + const SizedBox(width: 4), + Text('${source.albumCount}'), + ]), + ], + ), + ], + ), + ), + ); + + final videoEntry = _FilteredCollectionNavTile( + collection: collection, + leading: const Icon(OMIcons.videoLibrary), + title: 'Videos', + filter: VideoFilter(), + ); + final buildAlbumEntry = (album) => _FilteredCollectionNavTile( + collection: collection, + leading: IconUtils.getAlbumIcon(context, album), + title: CollectionSource.getUniqueAlbumName(album, source.sortedAlbums), + dense: true, + filter: AlbumFilter(album), + ); + final buildTagEntry = (tag) => _FilteredCollectionNavTile( + collection: collection, + leading: Icon( + OMIcons.label, + color: stringToColor(tag), + ), + title: tag, + dense: true, + filter: TagFilter(tag), + ); + final regularAlbums = [], appAlbums = [], specialAlbums = []; for (var album in source.sortedAlbums) { switch (androidFileUtils.getAlbumType(album)) { @@ -35,114 +123,80 @@ class AllCollectionDrawer extends StatelessWidget { } } - final videoEntry = _FilteredCollectionNavTile( - collection: collection, - leading: const Icon(OMIcons.videoLibrary), - title: 'Videos', - filter: VideoFilter(), - ); - final buildAlbumEntry = (album) => _FilteredCollectionNavTile( - collection: collection, - leading: IconUtils.getAlbumIcon(context, album) ?? const Icon(OMIcons.photoAlbum), - title: CollectionSource.getUniqueAlbumName(album, source.sortedAlbums), - filter: AlbumFilter(album), - ); - final buildTagEntry = (tag) => _FilteredCollectionNavTile( - collection: collection, - leading: Icon( - OMIcons.label, - color: stringToColor(tag), - ), - title: tag, - filter: TagFilter(tag), - ); + final tags = source.sortedTags; return Drawer( child: Selector( selector: (c, mq) => mq.viewInsets.bottom, - builder: (c, mqViewInsetsBottom, child) => ListView( - padding: EdgeInsets.only(bottom: mqViewInsetsBottom), - children: [ - DrawerHeader( - decoration: BoxDecoration( - color: Theme.of(context).accentColor, + builder: (c, mqViewInsetsBottom, child) { + return SingleChildScrollView( + padding: EdgeInsets.only(bottom: mqViewInsetsBottom), + child: Theme( + data: Theme.of(context).copyWith( + // color used by `ExpansionTile` for leading icon + unselectedWidgetColor: Colors.white, ), - child: SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - CircleAvatar( - backgroundColor: Colors.white, - radius: 44, - child: Padding( - padding: const EdgeInsets.only(top: 6.0), - child: SvgPicture.asset( - 'assets/aves_logo.svg', - width: 64, - ), - ), - ), - const SizedBox(width: 16), - const Text( - 'Aves', - style: TextStyle( - fontSize: 44, - fontFamily: 'Concourse Caps', - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - const Icon(OMIcons.photoLibrary), - const SizedBox(width: 4), - Text('${collection.imageCount}'), - ]), - Row(children: [ - const Icon(OMIcons.videoLibrary), - const SizedBox(width: 4), - Text('${collection.videoCount}'), - ]), - Row(children: [ - const Icon(OMIcons.photoAlbum), - const SizedBox(width: 4), - Text('${source.albumCount}'), - ]), - Row(children: [ - const Icon(OMIcons.label), - const SizedBox(width: 4), - Text('${source.tagCount}'), - ]), - ], - ), + child: Column( + children: [ + header, + videoEntry, + if (specialAlbums.isNotEmpty) ...[ + const Divider(), + ...specialAlbums.map(buildAlbumEntry), ], - ), + if (appAlbums.isNotEmpty) ...[ + const Divider(), + ...appAlbums.map(buildAlbumEntry), + ], + if (regularAlbums.isNotEmpty) + SafeArea( + top: false, + bottom: false, + child: ExpansionTile( + leading: const Icon(OMIcons.photoAlbum), + title: Row( + children: [ + const Text('Albums'), + const Spacer(), + Text( + '${regularAlbums.length}', + style: TextStyle( + color: (_regularAlbumsExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6), + ), + ), + ], + ), + onExpansionChanged: (expanded) => setState(() => _regularAlbumsExpanded = expanded), + children: regularAlbums.map(buildAlbumEntry).toList(), + ), + ), + if (tags.isNotEmpty) + SafeArea( + top: false, + bottom: false, + child: ExpansionTile( + leading: const Icon(OMIcons.label), + title: Row( + children: [ + const Text('Tags'), + const Spacer(), + Text( + '${tags.length}', + style: TextStyle( + color: (_tagsExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6), + ), + ), + ], + ), + onExpansionChanged: (expanded) => setState(() => _tagsExpanded = expanded), + children: tags.map(buildTagEntry).toList(), + ), + ), + ], ), ), - videoEntry, - if (specialAlbums.isNotEmpty) ...[ - const Divider(), - ...specialAlbums.map(buildAlbumEntry), - ], - if (appAlbums.isNotEmpty) ...[ - const Divider(), - ...appAlbums.map(buildAlbumEntry), - ], - if (regularAlbums.isNotEmpty) ...[ - const Divider(), - ...regularAlbums.map(buildAlbumEntry), - ], - if (tags.isNotEmpty) ...[ - const Divider(), - ...tags.map(buildTagEntry), - ], - ], - ), + ); + }, ), ); } @@ -152,14 +206,16 @@ class _FilteredCollectionNavTile extends StatelessWidget { final CollectionLens collection; final Widget leading; final String title; + final bool dense; final CollectionFilter filter; const _FilteredCollectionNavTile({ @required this.collection, @required this.leading, @required this.title, + bool dense, @required this.filter, - }); + }) : this.dense = dense ?? false; @override Widget build(BuildContext context) { @@ -169,7 +225,7 @@ class _FilteredCollectionNavTile extends StatelessWidget { child: ListTile( leading: leading, title: Text(title), - dense: true, + dense: dense, onTap: () { Navigator.pop(context); Navigator.push( diff --git a/lib/widgets/fullscreen/info/xmp_section.dart b/lib/widgets/fullscreen/info/xmp_section.dart index 72d992aad..98e2469c7 100644 --- a/lib/widgets/fullscreen/info/xmp_section.dart +++ b/lib/widgets/fullscreen/info/xmp_section.dart @@ -10,8 +10,6 @@ class XmpTagSectionSliver extends AnimatedWidget { final CollectionLens collection; final ImageEntry entry; - static const double buttonBorderWidth = 2; - XmpTagSectionSliver({ Key key, @required this.collection, @@ -28,20 +26,13 @@ class XmpTagSectionSliver extends AnimatedWidget { : [ const SectionRow('XMP Tags'), Padding( - padding: const EdgeInsets.symmetric(horizontal: buttonBorderWidth / 2), + padding: const EdgeInsets.symmetric(horizontal: TagButton.buttonBorderWidth / 2), child: Wrap( spacing: 8, children: tags - .map((tag) => OutlineButton( + .map((tag) => TagButton( + tag: tag, onPressed: () => _goToTag(context, tag), - borderSide: BorderSide( - color: stringToColor(tag), - width: buttonBorderWidth, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(42), - ), - child: Text(tag), )) .toList(), ), @@ -64,3 +55,30 @@ class XmpTagSectionSliver extends AnimatedWidget { ); } } + +class TagButton extends StatelessWidget { + final String tag; + final VoidCallback onPressed; + + const TagButton({ + @required this.tag, + @required this.onPressed, + }); + + static const double buttonBorderWidth = 2; + + @override + Widget build(BuildContext context) { + return OutlineButton( + onPressed: onPressed, + borderSide: BorderSide( + color: stringToColor(tag), + width: buttonBorderWidth, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(42), + ), + child: Text(tag), + ); + } +}