improve Dyn/Topic DraggableScrollableSheet

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-04-21 13:23:44 +08:00
parent dd5ccd11b8
commit d7b4ceabbf
9 changed files with 183 additions and 1285 deletions

View File

@@ -0,0 +1,91 @@
part of 'package:PiliPlus/common/widgets/flutter/draggable_scrollable_sheet.dart';
class DynDraggableScrollableSheet extends DraggableScrollableSheet {
const DynDraggableScrollableSheet({
super.key,
super.initialChildSize,
super.minChildSize,
super.maxChildSize,
super.expand,
super.snap,
super.snapSizes,
super.snapAnimationDuration,
super.controller,
super.shouldCloseOnMinExtent,
required super.builder,
});
@override
State<DraggableScrollableSheet> createState() =>
_DynDraggableScrollableSheetState();
}
class _DynDraggableScrollableSheetState extends _DraggableScrollableSheetState {
@override
void initState() {
super.initState();
_extent = _DraggableSheetExtent(
minSize: widget.minChildSize,
maxSize: widget.maxChildSize,
snap: widget.snap,
snapSizes: _impliedSnapSizes(),
snapAnimationDuration: widget.snapAnimationDuration,
initialSize: widget.initialChildSize,
shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent,
);
_scrollController = _DynDraggableScrollableSheetScrollController(
extent: _extent,
);
widget.controller?._attach(_scrollController);
}
}
class _DynDraggableScrollableSheetScrollController
extends _DraggableScrollableSheetScrollController {
_DynDraggableScrollableSheetScrollController({
required super.extent,
});
@override
_DraggableScrollableSheetScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition? oldPosition,
) {
return _DynDraggableScrollableSheetScrollPosition(
physics: physics.applyTo(const AlwaysScrollableScrollPhysics()),
context: context,
oldPosition: oldPosition,
getExtent: () => extent,
);
}
}
class _DynDraggableScrollableSheetScrollPosition
extends _DraggableScrollableSheetScrollPosition {
_DynDraggableScrollableSheetScrollPosition({
required super.physics,
required super.context,
super.oldPosition,
required super.getExtent,
});
bool _isAtTop = true;
@override
bool get listShouldScroll => !_isAtTop || super.listShouldScroll;
@override
void applyUserOffset(double delta) {
if (_isAtTop && pixels > 0) {
_isAtTop = false;
}
super.applyUserOffset(delta);
}
@override
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
_isAtTop = pixels == 0;
return super.drag(details, dragCancelCallback);
}
}

View File

@@ -0,0 +1,74 @@
part of 'package:PiliPlus/common/widgets/flutter/draggable_scrollable_sheet.dart';
class TopicDraggableScrollableSheet extends DraggableScrollableSheet {
const TopicDraggableScrollableSheet({
super.key,
super.initialChildSize,
super.minChildSize,
super.maxChildSize,
super.expand,
super.snap,
super.snapSizes,
super.snapAnimationDuration,
super.controller,
super.shouldCloseOnMinExtent,
required super.builder,
this.initialScrollOffset = 0.0,
});
final double initialScrollOffset;
@override
State<DraggableScrollableSheet> createState() =>
_TopicDraggableScrollableSheetState();
}
class _TopicDraggableScrollableSheetState
extends _DraggableScrollableSheetState {
@override
void initState() {
super.initState();
_extent = _DraggableSheetExtent(
minSize: widget.minChildSize,
maxSize: widget.maxChildSize,
snap: widget.snap,
snapSizes: _impliedSnapSizes(),
snapAnimationDuration: widget.snapAnimationDuration,
initialSize: widget.initialChildSize,
shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent,
);
_scrollController = _TopicDraggableScrollableSheetScrollController(
extent: _extent,
initialScrollOffset:
(widget as TopicDraggableScrollableSheet).initialScrollOffset,
);
widget.controller?._attach(_scrollController);
}
}
class _TopicDraggableScrollableSheetScrollController
extends _DraggableScrollableSheetScrollController {
_TopicDraggableScrollableSheetScrollController({
required super.extent,
double initialScrollOffset = 0.0,
}) : _initialScrollOffset = initialScrollOffset;
@override
double get initialScrollOffset => _initialScrollOffset;
final double _initialScrollOffset;
@override
_DraggableScrollableSheetScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition? oldPosition,
) {
return _DraggableScrollableSheetScrollPosition(
physics: physics.applyTo(const AlwaysScrollableScrollPhysics()),
context: context,
oldPosition: oldPosition,
getExtent: () => extent,
initialPixels: _initialScrollOffset,
);
}
}

View File

@@ -2,27 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: uri_does_not_exist_in_doc_import, depend_on_referenced_packages
/// @docImport 'package:flutter/material.dart';
/// @docImport 'package:flutter_test/flutter_test.dart';
///
/// @docImport 'primary_scroll_controller.dart';
/// @docImport 'scroll_configuration.dart';
/// @docImport 'scroll_view.dart';
/// @docImport 'scrollable.dart';
/// @docImport 'single_child_scroll_view.dart';
/// @docImport 'viewport.dart';
library;
import 'dart:math' as math;
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'
hide DraggableScrollableSheet, LayoutBuilder;
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
part 'package:PiliPlus/common/widgets/draggable_sheet/dyn.dart';
part 'package:PiliPlus/common/widgets/draggable_sheet/topic.dart';
/// Controls a [DraggableScrollableSheet].
///
@@ -730,9 +718,7 @@ class _DraggableScrollableSheetState extends State<DraggableScrollableSheet> {
/// [_DraggableScrollableSheetScrollController] as the primary controller for
/// descendants.
class _DraggableScrollableSheetScrollController extends ScrollController {
_DraggableScrollableSheetScrollController({
required this.extent,
});
_DraggableScrollableSheetScrollController({required this.extent});
_DraggableSheetExtent extent;
VoidCallback? onPositionDetached;
@@ -807,6 +793,7 @@ class _DraggableScrollableSheetScrollPosition
required super.context,
super.oldPosition,
required this.getExtent,
super.initialPixels,
});
VoidCallback? _dragCancelCallback;
@@ -817,8 +804,6 @@ class _DraggableScrollableSheetScrollPosition
_DraggableSheetExtent get extent => getExtent();
bool _isAtTop = true;
@override
void absorb(ScrollPosition other) {
super.absorb(other);
@@ -846,9 +831,7 @@ class _DraggableScrollableSheetScrollPosition
@override
void applyUserOffset(double delta) {
if (!_isAtTop) {
super.applyUserOffset(delta);
} else if (!listShouldScroll &&
if (!listShouldScroll &&
(!(extent.isAtMin || extent.isAtMax) ||
(extent.isAtMin && delta < 0) ||
(extent.isAtMax && delta > 0))) {
@@ -883,10 +866,6 @@ class _DraggableScrollableSheetScrollPosition
@override
void goBallistic(double velocity) {
if (!_isAtTop) {
super.goBallistic(velocity);
return;
}
if ((velocity == 0.0 && !_shouldSnap()) ||
(velocity < 0.0 && listShouldScroll) ||
(velocity > 0.0 && extent.isAtMax)) {
@@ -964,71 +943,12 @@ class _DraggableScrollableSheetScrollPosition
@override
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
_isAtTop = pixels == 0;
// Save this so we can call it later if we have to [goBallistic] on our own.
_dragCancelCallback = dragCancelCallback;
return super.drag(details, dragCancelCallback);
}
}
/// A widget that can notify a descendent [DraggableScrollableSheet] that it
/// should reset its position to the initial state.
///
/// The [Scaffold] uses this widget to notify a persistent bottom sheet that
/// the user has tapped back if the sheet has started to cover more of the body
/// than when at its initial position. This is important for users of assistive
/// technology, where dragging may be difficult to communicate.
///
/// This is just a wrapper on top of [DraggableScrollableController]. It is
/// primarily useful for controlling a sheet in a part of the widget tree that
/// the current code does not control (e.g. library code trying to affect a sheet
/// in library users' code). Generally, it's easier to control the sheet
/// directly by creating a controller and passing the controller to the sheet in
/// its constructor (see [DraggableScrollableSheet.controller]).
class DraggableScrollableActuator extends StatefulWidget {
/// Creates a widget that can notify descendent [DraggableScrollableSheet]s
/// to reset to their initial position.
///
/// The [child] parameter is required.
const DraggableScrollableActuator({super.key, required this.child});
/// This child's [DraggableScrollableSheet] descendant will be reset when the
/// [reset] method is applied to a context that includes it.
final Widget child;
/// Notifies any descendant [DraggableScrollableSheet] that it should reset
/// to its initial position.
///
/// Returns `true` if a [DraggableScrollableActuator] is available and
/// some [DraggableScrollableSheet] is listening for updates, `false`
/// otherwise.
static bool reset(BuildContext context) {
final _InheritedResetNotifier? notifier = context
.dependOnInheritedWidgetOfExactType<_InheritedResetNotifier>();
return notifier?._sendReset() ?? false;
}
@override
State<DraggableScrollableActuator> createState() =>
_DraggableScrollableActuatorState();
}
class _DraggableScrollableActuatorState
extends State<DraggableScrollableActuator> {
final _ResetNotifier _notifier = _ResetNotifier();
@override
Widget build(BuildContext context) {
return _InheritedResetNotifier(notifier: _notifier, child: widget.child);
}
@override
void dispose() {
_notifier.dispose();
super.dispose();
}
}
/// A [ChangeNotifier] to use with [_InheritedResetNotifier] to notify
/// descendants that they should reset to initial state.
class _ResetNotifier extends ChangeNotifier {