use SliverList instead of multiple SliverGrid + SliverStickyHeader
This commit is contained in:
parent
802efda6be
commit
0cec7af363
5 changed files with 106 additions and 70 deletions
105
lib/widgets/album/collection_list_sliver.dart
Normal file
105
lib/widgets/album/collection_list_sliver.dart
Normal file
|
@ -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 = <Widget>[];
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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:aves/widgets/common/data_providers/media_query_data_provider.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
|
||||||
|
|
||||||
class GridScaleGestureDetector extends StatefulWidget {
|
class GridScaleGestureDetector extends StatefulWidget {
|
||||||
final GlobalKey scrollableKey;
|
final GlobalKey scrollableKey;
|
||||||
|
@ -53,7 +52,7 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
||||||
final renderMetaData = firstOf<RenderMetaData>(result);
|
final renderMetaData = firstOf<RenderMetaData>(result);
|
||||||
// abort if we cannot find an image to show on overlay
|
// abort if we cannot find an image to show on overlay
|
||||||
if (renderMetaData == null) return;
|
if (renderMetaData == null) return;
|
||||||
_renderSliver = firstOf<RenderSliverStickyHeader>(result) ?? firstOf<RenderSliverGrid>(result);
|
_renderSliver = firstOf<RenderSliverList>(result);
|
||||||
_renderViewport = firstOf<RenderViewport>(result);
|
_renderViewport = firstOf<RenderViewport>(result);
|
||||||
_metadata = renderMetaData.metaData;
|
_metadata = renderMetaData.metaData;
|
||||||
_startExtent = tileExtentNotifier.value;
|
_startExtent = tileExtentNotifier.value;
|
||||||
|
|
|
@ -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/common/icons.dart';
|
||||||
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
|
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
|
||||||
import 'package:flutter/material.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 {
|
class GridThumbnail extends StatelessWidget {
|
||||||
final CollectionLens collection;
|
final CollectionLens collection;
|
||||||
|
|
|
@ -136,15 +136,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
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:
|
flutter_svg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -30,9 +30,6 @@ dependencies:
|
||||||
url: https://github.com/AndreHaueisen/flushbar.git
|
url: https://github.com/AndreHaueisen/flushbar.git
|
||||||
ref: 13c55a8
|
ref: 13c55a8
|
||||||
flutter_native_timezone:
|
flutter_native_timezone:
|
||||||
flutter_sticky_header:
|
|
||||||
git:
|
|
||||||
url: git://github.com/deckerst/flutter_sticky_header.git
|
|
||||||
flutter_svg:
|
flutter_svg:
|
||||||
geocoder:
|
geocoder:
|
||||||
google_maps_flutter:
|
google_maps_flutter:
|
||||||
|
|
Loading…
Reference in a new issue