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

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart';
@@ -52,3 +53,41 @@ class CustomTabBarViewClampingScrollPhysics extends ClampingScrollPhysics {
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 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/spring_physics.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:get/get.dart';
import 'package:PiliPlus/common/constants.dart';
@@ -67,12 +68,12 @@ class _RcmdPageState extends State<RcmdPage>
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
),
child: refreshIndicator(
child: RefreshIndicator(
onRefresh: () async {
await _controller.onRefresh();
},
child: CustomScrollView(
controller: _controller.scrollController,
child: (onCancelDrag) => CustomScrollView(
controller: CustomScrollController(onCancelDrag),
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
SliverPadding(