Files
PiliPlus/lib/common/widgets/scroll_physics.dart
2026-07-22 13:02:40 +08:00

126 lines
3.7 KiB
Dart

import 'package:PiliPlus/common/widgets/flutter/page/tabs.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' hide TabBarView;
Widget tabBarView({
required List<Widget> children,
TabController? controller,
}) => TabBarView<CustomHorizontalDragGestureRecognizer>(
controller: controller,
physics: clampingScrollPhysics,
horizontalDragGestureRecognizer: CustomHorizontalDragGestureRecognizer.new,
children: children,
);
final _springDescription = _customSpringDescription();
SpringDescription _customSpringDescription() {
final List<double> springDescription = Pref.springDescription;
return SpringDescription(
mass: springDescription[0],
stiffness: springDescription[1],
damping: springDescription[2],
);
}
const clampingScrollPhysics = CustomTabBarViewScrollPhysics(
parent: ClampingScrollPhysics(),
);
class CustomTabBarViewScrollPhysics extends ScrollPhysics {
const CustomTabBarViewScrollPhysics({super.parent});
@override
CustomTabBarViewScrollPhysics applyTo(ScrollPhysics? ancestor) {
return CustomTabBarViewScrollPhysics(parent: buildParent(ancestor));
}
@override
SpringDescription get spring => _springDescription;
}
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;
}
}