diff --git a/lib/widgets/album/collection_list_sliver.dart b/lib/widgets/album/collection_list_sliver.dart new file mode 100644 index 000000000..6d623d12d --- /dev/null +++ b/lib/widgets/album/collection_list_sliver.dart @@ -0,0 +1,105 @@ +import 'dart:math'; + +import 'package:aves/model/collection_lens.dart'; +import 'package:aves/widgets/album/collection_section.dart'; +import 'package:flutter/material.dart'; + +// use a `SliverList` instead of multiple `SliverGrid` because having one `SliverGrid` per section does not scale up +// with the multiple `SliverGrid` solution, thumbnails at the beginning of each sections are built even though they are offscreen +// because of `RenderSliverMultiBoxAdaptor.addInitialChild` called by `RenderSliverGrid.performLayout` (line 547), as of Flutter v1.17.0 +class CollectionListSliver extends StatelessWidget { + final CollectionLens collection; + final bool showHeader; + final int columnCount; + final double tileExtent; + + const CollectionListSliver({ + Key key, + @required this.collection, + @required this.showHeader, + @required this.columnCount, + @required this.tileExtent, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final sectionLayouts = <_SectionLayout>[]; + final sectionKeys = collection.sections.keys.toList(); + var firstIndex = 0; + sectionKeys.forEach((sectionKey) { + final sectionEntryCount = collection.sections[sectionKey].length; + final rowCount = (sectionEntryCount / columnCount).ceil(); + final widgetCount = rowCount + (showHeader ? 1 : 0); + // closure of `firstIndex` on `sectionFirstIndex` + final sectionFirstIndex = firstIndex; + sectionLayouts.add( + _SectionLayout( + sectionKey: sectionKey, + widgetCount: widgetCount, + firstIndex: sectionFirstIndex, + builder: (context, listIndex) { + listIndex -= sectionFirstIndex; + if (showHeader) { + if (listIndex == 0) { + return SectionHeader( + collection: collection, + sections: collection.sections, + sectionKey: sectionKey, + ); + } + listIndex--; + } + + final section = collection.sections[sectionKey]; + final minEntryIndex = listIndex * columnCount; + final maxEntryIndex = min(sectionEntryCount, minEntryIndex + columnCount); + final children = []; + for (var i = minEntryIndex; i < maxEntryIndex; i++) { + children.add(GridThumbnail( + collection: collection, + index: i, + entry: section[i], + tileExtent: tileExtent, + )); + } + return Row( + mainAxisSize: MainAxisSize.min, + children: children, + ); + }, + ), + ); + firstIndex += widgetCount; + }); + final childCount = firstIndex; + + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index >= childCount) return null; + final sectionLayout = sectionLayouts.firstWhere((section) => section.hasChild(index)); + return sectionLayout.builder(context, index); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + ), + ); + } +} + +class _SectionLayout { + final dynamic sectionKey; + final int widgetCount; + final int firstIndex; + final int lastIndex; + final IndexedWidgetBuilder builder; + + const _SectionLayout({ + @required this.sectionKey, + @required this.widgetCount, + @required this.firstIndex, + @required this.builder, + }) : lastIndex = firstIndex + widgetCount - 1; + + bool hasChild(int index) => firstIndex <= index && index <= lastIndex; +} diff --git a/lib/widgets/album/collection_scaling.dart b/lib/widgets/album/collection_scaling.dart index 4b755b213..33101d3f4 100644 --- a/lib/widgets/album/collection_scaling.dart +++ b/lib/widgets/album/collection_scaling.dart @@ -8,7 +8,6 @@ import 'package:aves/widgets/album/tile_extent_manager.dart'; import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter_sticky_header/flutter_sticky_header.dart'; class GridScaleGestureDetector extends StatefulWidget { final GlobalKey scrollableKey; @@ -53,7 +52,7 @@ class _GridScaleGestureDetectorState extends State { final renderMetaData = firstOf(result); // abort if we cannot find an image to show on overlay if (renderMetaData == null) return; - _renderSliver = firstOf(result) ?? firstOf(result); + _renderSliver = firstOf(result); _renderViewport = firstOf(result); _metadata = renderMetaData.metaData; _startExtent = tileExtentNotifier.value; diff --git a/lib/widgets/album/collection_section.dart b/lib/widgets/album/collection_section.dart index db079e153..6a5ddc4a4 100644 --- a/lib/widgets/album/collection_section.dart +++ b/lib/widgets/album/collection_section.dart @@ -7,62 +7,6 @@ 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 CollectionLens collection; - final dynamic sectionKey; - final double tileExtent; - final bool showHeader; - - const SectionSliver({ - Key key, - @required this.collection, - @required this.sectionKey, - @required this.tileExtent, - @required this.showHeader, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final sections = collection.sections; - final sectionEntries = sections[sectionKey]; - final childCount = sectionEntries.length; - - final sliver = SliverGrid( - delegate: SliverChildBuilderDelegate( - // TODO TLAD thumbnails at the beginning of each sections are built even though they are offscreen - // because of `RenderSliverMultiBoxAdaptor.addInitialChild` - // called by `RenderSliverGrid.performLayout` (line 547) - (context, index) => index < childCount - ? GridThumbnail( - collection: collection, - index: index, - entry: sectionEntries[index], - tileExtent: tileExtent, - ) - : null, - childCount: childCount, - addAutomaticKeepAlives: false, - ), - gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: tileExtent, - ), - ); - - return showHeader - ? SliverStickyHeader( - header: SectionHeader( - collection: collection, - sections: sections, - sectionKey: sectionKey, - ), - sliver: sliver, - overlapsContent: false, - ) - : sliver; - } -} class GridThumbnail extends StatelessWidget { final CollectionLens collection; diff --git a/lib/widgets/album/thumbnail_collection.dart b/lib/widgets/album/thumbnail_collection.dart index bc88cb0e3..73b07634a 100644 --- a/lib/widgets/album/thumbnail_collection.dart +++ b/lib/widgets/album/thumbnail_collection.dart @@ -3,9 +3,9 @@ import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/mime_types.dart'; import 'package:aves/widgets/album/collection_app_bar.dart'; +import 'package:aves/widgets/album/collection_list_sliver.dart'; import 'package:aves/widgets/album/collection_page.dart'; import 'package:aves/widgets/album/collection_scaling.dart'; -import 'package:aves/widgets/album/collection_section.dart'; import 'package:aves/widgets/album/empty.dart'; import 'package:aves/widgets/album/tile_extent_manager.dart'; import 'package:aves/widgets/common/scroll_thumb.dart'; @@ -63,17 +63,17 @@ class ThumbnailCollection extends StatelessWidget { appBarHeightNotifier: _appBarHeightNotifier, collection: collection, ), - if (collection.isEmpty) - SliverFillRemaining( - child: _buildEmptyCollectionPlaceholder(collection), - hasScrollBody: false, - ), - ...sectionKeys.map((sectionKey) => SectionSliver( - collection: collection, - sectionKey: sectionKey, - tileExtent: tileExtent, - showHeader: showHeaders, - )), + collection.isEmpty + ? SliverFillRemaining( + child: _buildEmptyCollectionPlaceholder(collection), + hasScrollBody: false, + ) + : CollectionListSliver( + collection: collection, + showHeader: showHeaders, + columnCount: (mqSize.width / tileExtent).round(), + tileExtent: tileExtent, + ), SliverToBoxAdapter( child: Selector( selector: (c, mq) => mq.viewInsets.bottom, diff --git a/pubspec.lock b/pubspec.lock index 6b0d20d2f..a132cabff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -136,15 +136,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.6" - flutter_sticky_header: - dependency: "direct main" - description: - path: "." - ref: HEAD - resolved-ref: "14be154f50f5d14e88cc05b93b12377012b8905a" - url: "git://github.com/deckerst/flutter_sticky_header.git" - source: git - version: "0.4.2" flutter_svg: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 204324c85..1f2600bf9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,9 +30,6 @@ dependencies: url: https://github.com/AndreHaueisen/flushbar.git ref: 13c55a8 flutter_native_timezone: - flutter_sticky_header: - git: - url: git://github.com/deckerst/flutter_sticky_header.git flutter_svg: geocoder: google_maps_flutter: