webdav存在自引用问题,现版本修复完成
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 磁吸进度状态
|
||||
class MagneticSnapState {
|
||||
/// 吸附进度 0.0 ~ 1.0
|
||||
final double progress;
|
||||
|
||||
/// 媒体库透明度
|
||||
double get mediaOpacity {
|
||||
// 0% ~ 70%: 1.0 → 0.45
|
||||
// 70% ~ 100%: 0.45 → 0
|
||||
if (progress <= 0.7) {
|
||||
return 1.0 - (progress / 0.7) * 0.55;
|
||||
} else {
|
||||
final t = (progress - 0.7) / 0.3;
|
||||
return 0.45 * (1 - t);
|
||||
}
|
||||
}
|
||||
|
||||
/// 收藏标题位移(从 +12px → 0)
|
||||
double get favoriteTranslateY {
|
||||
return (1 - progress) * 12;
|
||||
}
|
||||
|
||||
/// 收藏列表展开程度 0.0 ~ 1.0
|
||||
double get listReveal {
|
||||
if (progress <= 0.7) return 0.0;
|
||||
return (progress - 0.7) / 0.3;
|
||||
}
|
||||
|
||||
/// 是否完全吸附
|
||||
bool get isSnapped => progress >= 1.0;
|
||||
|
||||
/// 是否在磁吸区(进度 > 70%)
|
||||
bool get isInMagneticZone => progress > 0.7;
|
||||
|
||||
const MagneticSnapState(this.progress);
|
||||
|
||||
factory MagneticSnapState.initial() => const MagneticSnapState(0.0);
|
||||
|
||||
MagneticSnapState copyWith({double? progress}) {
|
||||
return MagneticSnapState(progress ?? this.progress);
|
||||
}
|
||||
}
|
||||
|
||||
/// 磁吸控制器
|
||||
class MagneticScrollController extends ChangeNotifier {
|
||||
/// 吸附进度
|
||||
double _progress = 0.0;
|
||||
double get progress => _progress;
|
||||
|
||||
/// 是否已吸附
|
||||
bool _isSnapped = false;
|
||||
bool get isSnapped => _isSnapped;
|
||||
|
||||
/// 动画控制器(用于吸附动画)
|
||||
AnimationController? _animationController;
|
||||
TickerProvider? _tickerProvider;
|
||||
|
||||
/// 状态
|
||||
MagneticSnapState get state => MagneticSnapState(_progress);
|
||||
|
||||
void init(TickerProvider vsync) {
|
||||
_animationController = AnimationController(
|
||||
vsync: vsync,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// 更新滚动进度(在滚动时调用)
|
||||
void updateProgress(double newProgress) {
|
||||
if (_isSnapped) return;
|
||||
_progress = newProgress.clamp(0.0, 1.0);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 处理松手事件
|
||||
void onDragEnd(double velocity) {
|
||||
if (_isSnapped) return;
|
||||
|
||||
final threshold = 0.45; // 45% 触发吸附
|
||||
final shouldSnap = _progress > threshold || velocity > 800;
|
||||
|
||||
if (shouldSnap) {
|
||||
snapOpen();
|
||||
} else {
|
||||
snapBack();
|
||||
}
|
||||
}
|
||||
|
||||
/// 吸附展开
|
||||
void snapOpen() {
|
||||
if (_isSnapped) return;
|
||||
_isSnapped = true;
|
||||
|
||||
_animationController?.reset();
|
||||
_animationController
|
||||
?.animateTo(
|
||||
1.0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOutBack,
|
||||
)
|
||||
.then((_) {
|
||||
_progress = 1.0;
|
||||
notifyListeners();
|
||||
});
|
||||
|
||||
// 实时更新进度
|
||||
_animationController?.addListener(() {
|
||||
_progress = _animationController!.value;
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
/// 回弹
|
||||
void snapBack() {
|
||||
if (_isSnapped) return;
|
||||
|
||||
_animationController?.reset();
|
||||
_animationController
|
||||
?.animateTo(
|
||||
0.0,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeOut,
|
||||
)
|
||||
.then((_) {
|
||||
_progress = 0.0;
|
||||
notifyListeners();
|
||||
});
|
||||
|
||||
_animationController?.addListener(() {
|
||||
_progress = _animationController!.value;
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
/// 重置状态(退出页面时)
|
||||
void reset() {
|
||||
_isSnapped = false;
|
||||
_progress = 0.0;
|
||||
_animationController?.reset();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user