moved scroll function out of scaling gesture detector
This commit is contained in:
parent
81f72d8322
commit
8b06e6c86c
3 changed files with 76 additions and 38 deletions
|
@ -89,11 +89,14 @@ class SectionedListLayoutProvider extends StatelessWidget {
|
|||
|
||||
final minEntryIndex = sectionChildIndex * columnCount;
|
||||
final maxEntryIndex = min(sectionEntryCount, minEntryIndex + columnCount);
|
||||
final ids = <int>[];
|
||||
final children = <Widget>[];
|
||||
for (var i = minEntryIndex; i < maxEntryIndex; i++) {
|
||||
final entry = section[i];
|
||||
final id = entry.contentId;
|
||||
ids.add(id);
|
||||
children.add(GridThumbnail(
|
||||
key: ValueKey(entry.contentId),
|
||||
key: ValueKey(id),
|
||||
collection: collection,
|
||||
index: i,
|
||||
entry: entry,
|
||||
|
@ -101,6 +104,7 @@ class SectionedListLayoutProvider extends StatelessWidget {
|
|||
));
|
||||
}
|
||||
return Row(
|
||||
key: ValueKey(ids.join('-')),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: children,
|
||||
);
|
||||
|
@ -136,7 +140,6 @@ class SectionedListLayout {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,29 +2,27 @@ 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/decorated.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;
|
||||
final ValueNotifier<double> appBarHeightNotifier;
|
||||
final ValueNotifier<double> extentNotifier;
|
||||
final Size mqSize;
|
||||
final double mqHorizontalPadding;
|
||||
final void Function(ImageEntry entry) onScaled;
|
||||
final Widget child;
|
||||
|
||||
const GridScaleGestureDetector({
|
||||
this.scrollableKey,
|
||||
@required this.appBarHeightNotifier,
|
||||
@required this.extentNotifier,
|
||||
@required this.mqSize,
|
||||
@required this.mqHorizontalPadding,
|
||||
@required this.onScaled,
|
||||
@required this.child,
|
||||
});
|
||||
|
||||
|
@ -38,7 +36,6 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
|||
ValueNotifier<double> _scaledExtentNotifier;
|
||||
OverlayEntry _overlayEntry;
|
||||
ThumbnailMetadata _metadata;
|
||||
RenderViewport _renderViewport;
|
||||
|
||||
ValueNotifier<double> get tileExtentNotifier => widget.extentNotifier;
|
||||
|
||||
|
@ -64,7 +61,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;
|
||||
_renderViewport = firstOf<RenderViewport>(result);
|
||||
_metadata = renderMetaData.metaData;
|
||||
_startExtent = tileExtentNotifier.value;
|
||||
_scaledExtentNotifier = ValueNotifier(_startExtent);
|
||||
|
@ -113,21 +109,8 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
|||
_applyingScale = false;
|
||||
} else {
|
||||
// scroll to show the focal point thumbnail at its new position
|
||||
final viewportClosure = _renderViewport;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// about scrolling & offset retrieval:
|
||||
// `Scrollable.ensureVisible` only works on already rendered objects
|
||||
// `RenderViewport.showOnScreen` can find any `RenderSliver`, but not always a `RenderMetadata`
|
||||
// `RenderViewport.scrollOffsetOf` is a good alternative
|
||||
final scrollableContext = widget.scrollableKey.currentContext;
|
||||
final gridSize = (scrollableContext.findRenderObject() as RenderBox).size;
|
||||
final sectionLayout = Provider.of<SectionedListLayout>(context, listen: false);
|
||||
final tileRect = sectionLayout.getTileRect(_metadata.entry) ?? Rect.zero;
|
||||
// most of the time the app bar will be scrolled away after scaling,
|
||||
// so we compensate for it to center the focal point thumbnail
|
||||
final appBarHeight = widget.appBarHeightNotifier.value;
|
||||
final scrollOffset = tileRect.top + (tileRect.height - gridSize.height) / 2 + appBarHeight;
|
||||
viewportClosure.offset.jumpTo(max(.0, scrollOffset));
|
||||
widget.onScaled(_metadata.entry);
|
||||
_applyingScale = false;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/collection_lens.dart';
|
||||
import 'package:aves/model/filters/favourite.dart';
|
||||
import 'package:aves/model/filters/mime.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/model/mime_types.dart';
|
||||
import 'package:aves/widgets/album/app_bar.dart';
|
||||
import 'package:aves/widgets/album/empty.dart';
|
||||
|
@ -18,7 +21,6 @@ import 'package:tuple/tuple.dart';
|
|||
class ThumbnailCollection extends StatelessWidget {
|
||||
final ValueNotifier<double> _appBarHeightNotifier = ValueNotifier(0);
|
||||
final ValueNotifier<double> _tileExtentNotifier = ValueNotifier(0);
|
||||
final GlobalKey _scrollableKey = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -34,23 +36,25 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
// so that view updates on collection filter changes
|
||||
return Consumer<CollectionLens>(
|
||||
builder: (context, collection, child) {
|
||||
final scrollView = _buildScrollView(collection);
|
||||
final draggable = _buildDraggableScrollView(scrollView);
|
||||
final scaler = GridScaleGestureDetector(
|
||||
scrollableKey: _scrollableKey,
|
||||
final appBar = CollectionAppBar(
|
||||
appBarHeightNotifier: _appBarHeightNotifier,
|
||||
extentNotifier: _tileExtentNotifier,
|
||||
mqSize: mqSize,
|
||||
mqHorizontalPadding: mqHorizontalPadding,
|
||||
child: draggable,
|
||||
collection: collection,
|
||||
);
|
||||
|
||||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||
valueListenable: _tileExtentNotifier,
|
||||
builder: (context, tileExtent, child) => SectionedListLayoutProvider(
|
||||
collection: collection,
|
||||
scrollableWidth: mqSize.width - mqHorizontalPadding,
|
||||
tileExtent: tileExtent,
|
||||
child: scaler,
|
||||
child: _ScalableThumbnailCollection(
|
||||
appBarHeightNotifier: _appBarHeightNotifier,
|
||||
tileExtentNotifier: _tileExtentNotifier,
|
||||
collection: collection,
|
||||
mqSize: mqSize,
|
||||
mqHorizontalPadding: mqHorizontalPadding,
|
||||
appBar: appBar,
|
||||
),
|
||||
),
|
||||
);
|
||||
return sectionedListLayoutProvider;
|
||||
|
@ -60,8 +64,42 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView _buildScrollView(CollectionLens collection) {
|
||||
class _ScalableThumbnailCollection extends StatelessWidget {
|
||||
final CollectionLens collection;
|
||||
final ValueNotifier<double> appBarHeightNotifier;
|
||||
final ValueNotifier<double> tileExtentNotifier;
|
||||
final Size mqSize;
|
||||
final double mqHorizontalPadding;
|
||||
final Widget appBar;
|
||||
|
||||
final GlobalKey _scrollableKey = GlobalKey();
|
||||
|
||||
_ScalableThumbnailCollection({
|
||||
@required this.appBarHeightNotifier,
|
||||
@required this.tileExtentNotifier,
|
||||
@required this.collection,
|
||||
@required this.mqSize,
|
||||
@required this.mqHorizontalPadding,
|
||||
@required this.appBar,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scrollView = _buildScrollView(appBar, collection);
|
||||
final draggable = _buildDraggableScrollView(scrollView);
|
||||
return GridScaleGestureDetector(
|
||||
scrollableKey: _scrollableKey,
|
||||
extentNotifier: tileExtentNotifier,
|
||||
mqSize: mqSize,
|
||||
mqHorizontalPadding: mqHorizontalPadding,
|
||||
onScaled: (entry) => _scrollToEntry(context, entry),
|
||||
child: draggable,
|
||||
);
|
||||
}
|
||||
|
||||
ScrollView _buildScrollView(Widget appBar, CollectionLens collection) {
|
||||
return CustomScrollView(
|
||||
key: _scrollableKey,
|
||||
primary: true,
|
||||
|
@ -69,10 +107,7 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
// when there is no content and we use `SliverFillRemaining`
|
||||
physics: collection.isEmpty ? const NeverScrollableScrollPhysics() : null,
|
||||
slivers: [
|
||||
CollectionAppBar(
|
||||
appBarHeightNotifier: _appBarHeightNotifier,
|
||||
collection: collection,
|
||||
),
|
||||
appBar,
|
||||
collection.isEmpty
|
||||
? SliverFillRemaining(
|
||||
child: _buildEmptyCollectionPlaceholder(collection),
|
||||
|
@ -93,7 +128,7 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
|
||||
Widget _buildDraggableScrollView(ScrollView scrollView) {
|
||||
return ValueListenableBuilder<double>(
|
||||
valueListenable: _appBarHeightNotifier,
|
||||
valueListenable: appBarHeightNotifier,
|
||||
builder: (context, appBarHeight, child) => Selector<MediaQueryData, double>(
|
||||
selector: (context, mq) => mq.viewInsets.bottom,
|
||||
builder: (context, mqViewInsetsBottom, child) => DraggableScrollbar(
|
||||
|
@ -128,4 +163,21 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
)
|
||||
: const EmptyContent();
|
||||
}
|
||||
|
||||
// about scrolling & offset retrieval:
|
||||
// `Scrollable.ensureVisible` only works on already rendered objects
|
||||
// `RenderViewport.showOnScreen` can find any `RenderSliver`, but not always a `RenderMetadata`
|
||||
// `RenderViewport.scrollOffsetOf` is a good alternative
|
||||
void _scrollToEntry(BuildContext context, ImageEntry entry) {
|
||||
final scrollableContext = _scrollableKey.currentContext;
|
||||
final scrollableHeight = (scrollableContext.findRenderObject() as RenderBox).size.height;
|
||||
final sectionLayout = Provider.of<SectionedListLayout>(context, listen: false);
|
||||
final tileRect = sectionLayout.getTileRect(entry) ?? Rect.zero;
|
||||
// most of the time the app bar will be scrolled away after scaling,
|
||||
// so we compensate for it to center the focal point thumbnail
|
||||
final appBarHeight = appBarHeightNotifier.value;
|
||||
final scrollOffset = tileRect.top + (tileRect.height - scrollableHeight) / 2 + appBarHeight;
|
||||
|
||||
PrimaryScrollController.of(context)?.jumpTo(max(.0, scrollOffset));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue