collection: snap to header/row when fast scrolling long content
This commit is contained in:
parent
5677dab9fb
commit
954853643a
2 changed files with 30 additions and 3 deletions
|
@ -45,6 +45,7 @@ import 'package:aves/widgets/common/thumbnail/notifications.dart';
|
|||
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
||||
import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
|
@ -500,6 +501,8 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
|
|||
return Selector<SectionedListLayout<AvesEntry>, List<SectionLayout>>(
|
||||
selector: (context, layout) => layout.sectionLayouts,
|
||||
builder: (context, sectionLayouts, child) {
|
||||
final scrollController = widget.scrollController;
|
||||
final offsetIncrementSnapThreshold = context.select<TileExtentController, double>((v) => (v.extentNotifier.value + v.spacing) / 4);
|
||||
return DraggableScrollbar(
|
||||
backgroundColor: Colors.white,
|
||||
scrollThumbSize: Size(avesScrollThumbWidth, avesScrollThumbHeight),
|
||||
|
@ -507,7 +510,23 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
|
|||
height: avesScrollThumbHeight,
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
controller: widget.scrollController,
|
||||
controller: scrollController,
|
||||
dragOffsetSnapper: (scrollOffset, offsetIncrement) {
|
||||
if (offsetIncrement > offsetIncrementSnapThreshold && scrollOffset < scrollController.position.maxScrollExtent) {
|
||||
final section = sectionLayouts.firstWhereOrNull((section) => section.hasChildAtOffset(scrollOffset));
|
||||
if (section != null) {
|
||||
if (section.maxOffset - section.minOffset < scrollController.position.viewportDimension) {
|
||||
// snap to section header
|
||||
return section.minOffset;
|
||||
} else {
|
||||
// snap to content row
|
||||
final index = section.getMinChildIndexForScrollOffset(scrollOffset);
|
||||
return section.indexToLayoutOffset(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return scrollOffset;
|
||||
},
|
||||
crumbsBuilder: () => _getCrumbs(sectionLayouts),
|
||||
padding: EdgeInsets.only(
|
||||
// padding to keep scroll thumb between app bar above and nav bar below
|
||||
|
|
|
@ -59,6 +59,8 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
/// The ScrollController for the BoxScrollView
|
||||
final ScrollController controller;
|
||||
|
||||
final double Function(double scrollOffset, double offsetIncrement)? dragOffsetSnapper;
|
||||
|
||||
/// The view that will be scrolled with the scroll thumb
|
||||
final ScrollView child;
|
||||
|
||||
|
@ -68,6 +70,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
required this.scrollThumbSize,
|
||||
required this.scrollThumbBuilder,
|
||||
required this.controller,
|
||||
this.dragOffsetSnapper,
|
||||
this.crumbsBuilder,
|
||||
this.padding = EdgeInsets.zero,
|
||||
this.scrollbarAnimationDuration = const Duration(milliseconds: 300),
|
||||
|
@ -114,6 +117,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProviderStateMixin {
|
||||
final ValueNotifier<double> _thumbOffsetNotifier = ValueNotifier(0), _viewOffsetNotifier = ValueNotifier(0);
|
||||
bool _isDragInProcess = false;
|
||||
double _boundlessThumbOffset = 0, _offsetIncrement = 0;
|
||||
late Offset _longPressLastGlobalPosition;
|
||||
|
||||
late AnimationController _thumbAnimationController;
|
||||
|
@ -281,6 +285,8 @@ class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProv
|
|||
|
||||
void _onVerticalDragStart() {
|
||||
const DraggableScrollbarNotification(DraggableScrollbarEvent.dragStart).dispatch(context);
|
||||
_boundlessThumbOffset = _thumbOffsetNotifier.value;
|
||||
_offsetIncrement = 1 / thumbMaxScrollExtent * controller.position.maxScrollExtent;
|
||||
_labelAnimationController.forward();
|
||||
_fadeoutTimer?.cancel();
|
||||
_showThumb();
|
||||
|
@ -292,12 +298,14 @@ class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProv
|
|||
_showThumb();
|
||||
if (_isDragInProcess) {
|
||||
// thumb offset
|
||||
_thumbOffsetNotifier.value = (_thumbOffsetNotifier.value + deltaY).clamp(thumbMinScrollExtent, thumbMaxScrollExtent);
|
||||
_boundlessThumbOffset += deltaY;
|
||||
_thumbOffsetNotifier.value = _boundlessThumbOffset.clamp(thumbMinScrollExtent, thumbMaxScrollExtent);
|
||||
|
||||
// scroll offset
|
||||
final min = controller.position.minScrollExtent;
|
||||
final max = controller.position.maxScrollExtent;
|
||||
controller.jumpTo((_thumbOffsetNotifier.value / thumbMaxScrollExtent * max).clamp(min, max));
|
||||
final scrollOffset = _thumbOffsetNotifier.value / thumbMaxScrollExtent * max;
|
||||
controller.jumpTo((widget.dragOffsetSnapper?.call(scrollOffset, _offsetIncrement) ?? scrollOffset).clamp(min, max));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue