opt: refresh indicator

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-03-09 23:36:11 +08:00
parent 2efa6f4ace
commit c4bf8cbda2
3 changed files with 57 additions and 7 deletions

View File

@@ -12,7 +12,7 @@ Widget refreshIndicator({
return RefreshIndicator( return RefreshIndicator(
displacement: displacement, displacement: displacement,
onRefresh: onRefresh, onRefresh: onRefresh,
child: child, child: (onCancelDrag) => child,
); );
} }
@@ -188,7 +188,7 @@ class RefreshIndicator extends StatefulWidget {
/// will appear when child's Scrollable descendant is over-scrolled. /// will appear when child's Scrollable descendant is over-scrolled.
/// ///
/// Typically a [ListView] or [CustomScrollView]. /// Typically a [ListView] or [CustomScrollView].
final Widget child; final Widget Function(ValueChanged<double> onCancelDrag) child;
/// The distance from the child's top or bottom [edgeOffset] where /// The distance from the child's top or bottom [edgeOffset] where
/// the refresh indicator will settle. During the drag that exposes the refresh /// the refresh indicator will settle. During the drag that exposes the refresh
@@ -270,6 +270,8 @@ class RefreshIndicator extends StatefulWidget {
RefreshIndicatorState createState() => RefreshIndicatorState(); RefreshIndicatorState createState() => RefreshIndicatorState();
} }
bool isRefreshing = false;
/// Contains the state for a [RefreshIndicator]. This class can be used to /// Contains the state for a [RefreshIndicator]. This class can be used to
/// programmatically show the refresh indicator, see the [show] method. /// programmatically show the refresh indicator, see the [show] method.
class RefreshIndicatorState extends State<RefreshIndicator> class RefreshIndicatorState extends State<RefreshIndicator>
@@ -368,6 +370,8 @@ class RefreshIndicatorState extends State<RefreshIndicator>
_start(notification.metrics.axisDirection); _start(notification.metrics.axisDirection);
} }
double? containerExtent;
bool _handleScrollNotification(ScrollNotification notification) { bool _handleScrollNotification(ScrollNotification notification) {
if (!widget.notificationPredicate(notification)) { if (!widget.notificationPredicate(notification)) {
return false; return false;
@@ -443,6 +447,7 @@ class RefreshIndicatorState extends State<RefreshIndicator>
return false; return false;
} }
if (_mode == _RefreshIndicatorMode.drag) { if (_mode == _RefreshIndicatorMode.drag) {
isRefreshing = true;
notification.disallowIndicator(); notification.disallowIndicator();
return true; return true;
} }
@@ -470,6 +475,7 @@ class RefreshIndicatorState extends State<RefreshIndicator>
} }
void _checkDragOffset(double containerExtent) { void _checkDragOffset(double containerExtent) {
this.containerExtent ??= containerExtent;
assert(_mode == _RefreshIndicatorMode.drag || assert(_mode == _RefreshIndicatorMode.drag ||
_mode == _RefreshIndicatorMode.armed); _mode == _RefreshIndicatorMode.armed);
double newValue = double newValue =
@@ -487,6 +493,7 @@ class RefreshIndicatorState extends State<RefreshIndicator>
// Stop showing the refresh indicator. // Stop showing the refresh indicator.
Future<void> _dismiss(_RefreshIndicatorMode newMode) async { Future<void> _dismiss(_RefreshIndicatorMode newMode) async {
isRefreshing = false;
await Future<void>.value(); await Future<void>.value();
// This can only be called from _show() when refreshing and // This can only be called from _show() when refreshing and
// _handleScrollNotification in response to a ScrollEndNotification or // _handleScrollNotification in response to a ScrollEndNotification or
@@ -579,7 +586,10 @@ class RefreshIndicatorState extends State<RefreshIndicator>
onNotification: _handleScrollNotification, onNotification: _handleScrollNotification,
child: NotificationListener<OverscrollIndicatorNotification>( child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: _handleIndicatorNotification, onNotification: _handleIndicatorNotification,
child: widget.child, child: widget.child((delta) {
_dragOffset = _dragOffset! + delta;
_checkDragOffset(containerExtent!);
}),
), ),
); );
assert(() { assert(() {

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -52,3 +53,41 @@ class CustomTabBarViewClampingScrollPhysics extends ClampingScrollPhysics {
damping: GStorage.springDescription[2], damping: GStorage.springDescription[2],
); );
} }
class CustomScrollPosition extends ScrollPositionWithSingleContext {
CustomScrollPosition({
required super.physics,
required super.context,
required this.onCancelDrag,
});
final ValueChanged<double> onCancelDrag;
@override
void applyUserOffset(double delta) {
if (isRefreshing && delta < 0) {
onCancelDrag(delta);
return;
}
super.applyUserOffset(delta);
}
}
class CustomScrollController extends ScrollController {
CustomScrollController(this.onCancelDrag);
final ValueChanged<double> onCancelDrag;
@override
CustomScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition? oldPosition,
) {
return CustomScrollPosition(
physics: physics.applyTo(const AlwaysScrollableScrollPhysics()),
context: context,
onCancelDrag: onCancelDrag,
);
}
}

View File

@@ -1,8 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/spring_physics.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart' hide RefreshIndicator;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/constants.dart';
@@ -67,12 +68,12 @@ class _RcmdPageState extends State<RcmdPage>
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: StyleString.mdRadius, borderRadius: StyleString.mdRadius,
), ),
child: refreshIndicator( child: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
await _controller.onRefresh(); await _controller.onRefresh();
}, },
child: CustomScrollView( child: (onCancelDrag) => CustomScrollView(
controller: _controller.scrollController, controller: CustomScrollController(onCancelDrag),
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
slivers: [ slivers: [
SliverPadding( SliverPadding(