Files
PiliPlus/lib/common/widgets/scroll_physics.dart
2026-07-31 20:21:09 +08:00

125 lines
3.6 KiB
Dart

import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/material.dart';
Widget tabBarView({
required List<Widget> children,
TabController? controller,
HitTestBehavior hitTestBehavior = .opaque,
}) => TabBarView(
controller: controller,
physics: tabBarScrollPhysics,
hitTestBehavior: hitTestBehavior,
horizontalDragGestureRecognizer: CustomHorizontalDragGestureRecognizer.new,
children: children,
);
SpringDescription kSpringDescription = _customSpringDescription();
SpringDescription _customSpringDescription() {
final List<double> springDescription = Pref.springDescription;
return SpringDescription(
mass: springDescription[0],
stiffness: springDescription[1],
damping: springDescription[2],
);
}
const tabBarScrollPhysics = _TabBarViewScrollPhysics();
class _TabBarViewScrollPhysics extends ClampingScrollPhysics {
const _TabBarViewScrollPhysics({super.parent});
@override
_TabBarViewScrollPhysics applyTo(ScrollPhysics? ancestor) {
return _TabBarViewScrollPhysics(parent: buildParent(ancestor));
}
@override
SpringDescription get spring => kSpringDescription;
}
mixin ReloadMixin {
late bool reload = false;
}
class ReloadScrollPhysics extends AlwaysScrollableScrollPhysics {
const ReloadScrollPhysics({super.parent, required this.controller});
final ReloadMixin controller;
@override
ReloadScrollPhysics applyTo(ScrollPhysics? ancestor) {
return ReloadScrollPhysics(
parent: buildParent(ancestor),
controller: controller,
);
}
@override
double adjustPositionForNewDimensions({
required ScrollMetrics oldPosition,
required ScrollMetrics newPosition,
required bool isScrolling,
required double velocity,
}) {
if (controller.reload) {
controller.reload = false;
return 0;
}
return super.adjustPositionForNewDimensions(
oldPosition: oldPosition,
newPosition: newPosition,
isScrolling: isScrolling,
velocity: velocity,
);
}
}
final platformClampingPhysics = PlatformUtils.isDarwin
? const BouncingScrollPhysicsExt()
: const ClampingScrollPhysics();
final platformAlwaysClampingPhysics = PlatformUtils.isDarwin
? const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysicsExt())
: const AlwaysScrollableScrollPhysics(parent: ClampingScrollPhysics());
class BouncingScrollPhysicsExt extends BouncingScrollPhysics
with ClampingBoundaryMixin {
const BouncingScrollPhysicsExt({super.parent});
@override
BouncingScrollPhysicsExt applyTo(ScrollPhysics? ancestor) {
return BouncingScrollPhysicsExt(parent: buildParent(ancestor));
}
}
/// [ClampingScrollPhysics.applyBoundaryConditions]
mixin ClampingBoundaryMixin on ScrollPhysics {
@override
double applyBoundaryConditions(ScrollMetrics position, double value) {
if (value < position.pixels &&
position.pixels <= position.minScrollExtent) {
// Underscroll.
return value - position.pixels;
}
if (position.maxScrollExtent <= position.pixels &&
position.pixels < value) {
// Overscroll.
return value - position.pixels;
}
if (value < position.minScrollExtent &&
position.minScrollExtent < position.pixels) {
// Hit top edge.
return value - position.minScrollExtent;
}
if (position.pixels < position.maxScrollExtent &&
position.maxScrollExtent < value) {
// Hit bottom edge.
return value - position.maxScrollExtent;
}
return 0.0;
}
}