scrolling to entry with custom sliver list

This commit is contained in:
Thibault Deckers 2020-04-14 13:20:51 +09:00
parent 4edc2bf5d4
commit 6a5603a116
3 changed files with 54 additions and 21 deletions

View file

@ -1,18 +1,18 @@
import 'dart:math';
import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/album/grid/header_generic.dart';
import 'package:aves/widgets/album/grid/list_sliver.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SectionedListLayoutProvider extends StatelessWidget {
final Widget child;
final CollectionLens collection;
final int columnCount;
final double scrollableWidth;
final double tileExtent;
final Widget child;
SectionedListLayoutProvider({
@required this.collection,
@ -64,7 +64,12 @@ class SectionedListLayoutProvider extends StatelessWidget {
),
);
});
return SectionedListLayout(sectionLayouts);
return SectionedListLayout(
collection: collection,
columnCount: columnCount,
tileExtent: tileExtent,
sectionLayouts: sectionLayouts,
);
}
Widget _buildInSection(int sectionChildIndex, CollectionLens collection, dynamic sectionKey) {
@ -100,9 +105,37 @@ class SectionedListLayoutProvider extends StatelessWidget {
}
class SectionedListLayout {
final CollectionLens collection;
final int columnCount;
final double tileExtent;
final List<SectionLayout> sectionLayouts;
const SectionedListLayout(this.sectionLayouts);
const SectionedListLayout({
@required this.collection,
@required this.columnCount,
@required this.tileExtent,
@required this.sectionLayouts,
});
Rect getTileRect(ImageEntry entry) {
final section = collection.sections.entries.firstWhere((kv) => kv.value.contains(entry), orElse: () => null);
if (section == null) return null;
final sectionKey = section.key;
final sectionLayout = sectionLayouts.firstWhere((sl) => sl.sectionKey == sectionKey, orElse: () => null);
if (sectionLayout == null) return null;
final showHeaders = collection.showHeaders;
final sectionEntryIndex = section.value.indexOf(entry);
final column = sectionEntryIndex % columnCount;
final row = (sectionEntryIndex / columnCount).floor();
final listIndex = sectionLayout.firstIndex + (showHeaders ? 1 : 0) + row;
final left = tileExtent * column;
final top = sectionLayout.indexToLayoutOffset(listIndex);
debugPrint('TLAD getTileRect sectionKey=$sectionKey sectionOffset=${sectionLayout.minOffset} top=$top row=$row column=$column for title=${entry.bestTitle}');
return Rect.fromLTWH(left, top, tileExtent, tileExtent);
}
}
class SectionLayout {

View file

@ -2,12 +2,14 @@ import 'dart:math';
import 'dart:ui' as ui;
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/album/grid/list_section_layout.dart';
import 'package:aves/widgets/album/grid/list_sliver.dart';
import 'package:aves/widgets/album/grid/tile_extent_manager.dart';
import 'package:aves/widgets/album/thumbnail.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:provider/provider.dart';
class GridScaleGestureDetector extends StatefulWidget {
final GlobalKey scrollableKey;
@ -33,7 +35,6 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
ValueNotifier<double> _scaledExtentNotifier;
OverlayEntry _overlayEntry;
ThumbnailMetadata _metadata;
RenderSliver _renderSliver;
RenderViewport _renderViewport;
ValueNotifier<double> get tileExtentNotifier => widget.extentNotifier;
@ -56,7 +57,6 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
final renderMetaData = firstOf<RenderMetaData>(result);
// abort if we cannot find an image to show on overlay
if (renderMetaData == null) return;
_renderSliver = firstOf<RenderSliver>(result);
_renderViewport = firstOf<RenderViewport>(result);
_metadata = renderMetaData.metaData;
_startExtent = tileExtentNotifier.value;
@ -105,18 +105,18 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
// TODO TLAD fix scroll to specific thumbnail with custom SliverList
// scroll to show the focal point thumbnail at its new position
final sliverClosure = _renderSliver;
final viewportClosure = _renderViewport;
final index = _metadata.index;
WidgetsBinding.instance.addPostFrameCallback((_) {
final scrollableContext = widget.scrollableKey.currentContext;
final gridSize = (scrollableContext.findRenderObject() as RenderBox).size;
final newColumnCount = gridSize.width / newExtent;
final row = index ~/ newColumnCount;
final sectionLayout = Provider.of<SectionedListLayout>(context, listen: false);
final tileRect = sectionLayout.getTileRect(_metadata.entry);
final scrollOffset = (tileRect?.top ?? 0) - gridSize.height / 2;
viewportClosure.offset.jumpTo(scrollOffset.clamp(.0, double.infinity));
// about scrolling & offset retrieval:
// `Scrollable.ensureVisible` only works on already rendered objects
// `RenderViewport.showOnScreen` can find any `RenderSliver`, but not always a `RenderMetadata`
final scrollOffset = viewportClosure.scrollOffsetOf(sliverClosure, (row + 1) * newExtent - gridSize.height / 2);
viewportClosure.offset.jumpTo(scrollOffset.clamp(.0, double.infinity));
// `RenderViewport.scrollOffsetOf` is a good alternative
});
},
child: widget.child,

View file

@ -44,23 +44,23 @@ class ThumbnailCollection extends StatelessWidget {
builder: (context, collection, child) {
final scrollView = _buildScrollView(collection);
final draggable = _buildDraggableScrollView(scrollView);
final scaler = GridScaleGestureDetector(
scrollableKey: _scrollableKey,
extentNotifier: _tileExtentNotifier,
mqSize: mqSize,
mqHorizontalPadding: mqHorizontalPadding,
child: draggable,
);
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
valueListenable: _tileExtentNotifier,
builder: (context, tileExtent, child) => SectionedListLayoutProvider(
collection: collection,
scrollableWidth: mqSize.width - mqHorizontalPadding,
tileExtent: tileExtent,
child: draggable,
child: scaler,
),
);
final scaler = GridScaleGestureDetector(
scrollableKey: _scrollableKey,
extentNotifier: _tileExtentNotifier,
mqSize: mqSize,
mqHorizontalPadding: mqHorizontalPadding,
child: sectionedListLayoutProvider,
);
return scaler;
return sectionedListLayoutProvider;
},
);
},