scrolling to entry with custom sliver list
This commit is contained in:
parent
4edc2bf5d4
commit
6a5603a116
3 changed files with 54 additions and 21 deletions
|
@ -1,18 +1,18 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/collection_lens.dart';
|
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/header_generic.dart';
|
||||||
import 'package:aves/widgets/album/grid/list_sliver.dart';
|
import 'package:aves/widgets/album/grid/list_sliver.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class SectionedListLayoutProvider extends StatelessWidget {
|
class SectionedListLayoutProvider extends StatelessWidget {
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
final CollectionLens collection;
|
final CollectionLens collection;
|
||||||
final int columnCount;
|
final int columnCount;
|
||||||
final double scrollableWidth;
|
final double scrollableWidth;
|
||||||
final double tileExtent;
|
final double tileExtent;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
SectionedListLayoutProvider({
|
SectionedListLayoutProvider({
|
||||||
@required this.collection,
|
@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) {
|
Widget _buildInSection(int sectionChildIndex, CollectionLens collection, dynamic sectionKey) {
|
||||||
|
@ -100,9 +105,37 @@ class SectionedListLayoutProvider extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SectionedListLayout {
|
class SectionedListLayout {
|
||||||
|
final CollectionLens collection;
|
||||||
|
final int columnCount;
|
||||||
|
final double tileExtent;
|
||||||
final List<SectionLayout> sectionLayouts;
|
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 {
|
class SectionLayout {
|
||||||
|
|
|
@ -2,12 +2,14 @@ import 'dart:math';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:aves/model/image_entry.dart';
|
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/list_sliver.dart';
|
||||||
import 'package:aves/widgets/album/grid/tile_extent_manager.dart';
|
import 'package:aves/widgets/album/grid/tile_extent_manager.dart';
|
||||||
import 'package:aves/widgets/album/thumbnail.dart';
|
import 'package:aves/widgets/album/thumbnail.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:provider/provider.dart';
|
||||||
|
|
||||||
class GridScaleGestureDetector extends StatefulWidget {
|
class GridScaleGestureDetector extends StatefulWidget {
|
||||||
final GlobalKey scrollableKey;
|
final GlobalKey scrollableKey;
|
||||||
|
@ -33,7 +35,6 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
||||||
ValueNotifier<double> _scaledExtentNotifier;
|
ValueNotifier<double> _scaledExtentNotifier;
|
||||||
OverlayEntry _overlayEntry;
|
OverlayEntry _overlayEntry;
|
||||||
ThumbnailMetadata _metadata;
|
ThumbnailMetadata _metadata;
|
||||||
RenderSliver _renderSliver;
|
|
||||||
RenderViewport _renderViewport;
|
RenderViewport _renderViewport;
|
||||||
|
|
||||||
ValueNotifier<double> get tileExtentNotifier => widget.extentNotifier;
|
ValueNotifier<double> get tileExtentNotifier => widget.extentNotifier;
|
||||||
|
@ -56,7 +57,6 @@ 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<RenderSliver>(result);
|
|
||||||
_renderViewport = firstOf<RenderViewport>(result);
|
_renderViewport = firstOf<RenderViewport>(result);
|
||||||
_metadata = renderMetaData.metaData;
|
_metadata = renderMetaData.metaData;
|
||||||
_startExtent = tileExtentNotifier.value;
|
_startExtent = tileExtentNotifier.value;
|
||||||
|
@ -105,18 +105,18 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
||||||
|
|
||||||
// TODO TLAD fix scroll to specific thumbnail with custom SliverList
|
// TODO TLAD fix scroll to specific thumbnail with custom SliverList
|
||||||
// scroll to show the focal point thumbnail at its new position
|
// scroll to show the focal point thumbnail at its new position
|
||||||
final sliverClosure = _renderSliver;
|
|
||||||
final viewportClosure = _renderViewport;
|
final viewportClosure = _renderViewport;
|
||||||
final index = _metadata.index;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
final scrollableContext = widget.scrollableKey.currentContext;
|
final scrollableContext = widget.scrollableKey.currentContext;
|
||||||
final gridSize = (scrollableContext.findRenderObject() as RenderBox).size;
|
final gridSize = (scrollableContext.findRenderObject() as RenderBox).size;
|
||||||
final newColumnCount = gridSize.width / newExtent;
|
final sectionLayout = Provider.of<SectionedListLayout>(context, listen: false);
|
||||||
final row = index ~/ newColumnCount;
|
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
|
// `Scrollable.ensureVisible` only works on already rendered objects
|
||||||
// `RenderViewport.showOnScreen` can find any `RenderSliver`, but not always a `RenderMetadata`
|
// `RenderViewport.showOnScreen` can find any `RenderSliver`, but not always a `RenderMetadata`
|
||||||
final scrollOffset = viewportClosure.scrollOffsetOf(sliverClosure, (row + 1) * newExtent - gridSize.height / 2);
|
// `RenderViewport.scrollOffsetOf` is a good alternative
|
||||||
viewportClosure.offset.jumpTo(scrollOffset.clamp(.0, double.infinity));
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
|
|
|
@ -44,23 +44,23 @@ class ThumbnailCollection extends StatelessWidget {
|
||||||
builder: (context, collection, child) {
|
builder: (context, collection, child) {
|
||||||
final scrollView = _buildScrollView(collection);
|
final scrollView = _buildScrollView(collection);
|
||||||
final draggable = _buildDraggableScrollView(scrollView);
|
final draggable = _buildDraggableScrollView(scrollView);
|
||||||
|
final scaler = GridScaleGestureDetector(
|
||||||
|
scrollableKey: _scrollableKey,
|
||||||
|
extentNotifier: _tileExtentNotifier,
|
||||||
|
mqSize: mqSize,
|
||||||
|
mqHorizontalPadding: mqHorizontalPadding,
|
||||||
|
child: draggable,
|
||||||
|
);
|
||||||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||||
valueListenable: _tileExtentNotifier,
|
valueListenable: _tileExtentNotifier,
|
||||||
builder: (context, tileExtent, child) => SectionedListLayoutProvider(
|
builder: (context, tileExtent, child) => SectionedListLayoutProvider(
|
||||||
collection: collection,
|
collection: collection,
|
||||||
scrollableWidth: mqSize.width - mqHorizontalPadding,
|
scrollableWidth: mqSize.width - mqHorizontalPadding,
|
||||||
tileExtent: tileExtent,
|
tileExtent: tileExtent,
|
||||||
child: draggable,
|
child: scaler,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final scaler = GridScaleGestureDetector(
|
return sectionedListLayoutProvider;
|
||||||
scrollableKey: _scrollableKey,
|
|
||||||
extentNotifier: _tileExtentNotifier,
|
|
||||||
mqSize: mqSize,
|
|
||||||
mqHorizontalPadding: mqHorizontalPadding,
|
|
||||||
child: sectionedListLayoutProvider,
|
|
||||||
);
|
|
||||||
return scaler;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue