// lib/widgets/magnetic_scroll_physics.dart import 'package:flutter/material.dart'; class MagneticScrollPhysics extends ClampingScrollPhysics { final double snapPoint; final double magneticZoneStart; const MagneticScrollPhysics({ required this.snapPoint, this.magneticZoneStart = 0.20, super.parent, }); @override MagneticScrollPhysics applyTo(ScrollPhysics? ancestor) { return MagneticScrollPhysics( snapPoint: snapPoint, magneticZoneStart: magneticZoneStart, parent: buildParent(ancestor), ); } @override Simulation? createBallisticSimulation( ScrollMetrics position, double velocity, ) { final offset = position.pixels; if (offset <= 0 || offset >= snapPoint) { return super.createBallisticSimulation(position, velocity); } final shouldSnap = _shouldSnap(offset, velocity); final target = shouldSnap ? snapPoint : 0.0; if ((offset - target).abs() < 1.0) { return null; } return ScrollSpringSimulation( SpringDescription( mass: 1.0, stiffness: 320.0, damping: 26.0, ), offset, target, velocity, tolerance: const Tolerance( velocity: 0.01, distance: 0.5, ), ); } bool _shouldSnap(double offset, double velocity) { final zoneStart = snapPoint * magneticZoneStart; // 向上滑 → 吸附展开 if (velocity > 20) { return true; } // 向下滑 → 回去 if (velocity < -20) { return false; } // 松手速度很小,用当前位置决定 return offset >= zoneStart; } }