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(); } }