diff --git a/lib/services/chat_text_scale_service.dart b/lib/services/chat_text_scale_service.dart index 0257a56..21d6a5f 100644 --- a/lib/services/chat_text_scale_service.dart +++ b/lib/services/chat_text_scale_service.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; import '../storage/prefs_manager.dart'; @@ -21,6 +23,7 @@ class ChatTextScaleService extends ChangeNotifier { static const double _maxScale = 1.8; double _scale = 1.0; + Timer? _saveTimer; double get scale => _scale; @@ -31,15 +34,39 @@ class ChatTextScaleService extends ChangeNotifier { } } - void setScale(double value) { + void setScale(double value, {bool persistImmediately = false}) { final next = _clamp(value); if (next == _scale) return; _scale = next; - PrefsManager.instance.setDouble(_prefKey, _scale); notifyListeners(); + if (persistImmediately) { + _commitScale(); + } else { + _scheduleSave(); + } } - void reset() => setScale(1.0); + void reset() { + setScale(1.0, persistImmediately: true); + } + + void persist() => _commitScale(); + + @override + void dispose() { + _saveTimer?.cancel(); + super.dispose(); + } + + void _scheduleSave() { + _saveTimer?.cancel(); + _saveTimer = Timer(const Duration(milliseconds: 250), _commitScale); + } + + void _commitScale() { + _saveTimer?.cancel(); + PrefsManager.instance.setDouble(_prefKey, _scale); + } double _clamp(double value) => value.clamp(_minScale, _maxScale).toDouble(); } diff --git a/lib/widgets/chat_zoom_wrapper.dart b/lib/widgets/chat_zoom_wrapper.dart index e18662d..f0c6815 100644 --- a/lib/widgets/chat_zoom_wrapper.dart +++ b/lib/widgets/chat_zoom_wrapper.dart @@ -6,12 +6,18 @@ import '../services/chat_text_scale_service.dart'; /// Gesture wrapper that exposes two-finger pinch-to-zoom for chat scrollables. /// Double-tap resets the scale. Only the wrapper itself listens to gestures; /// child scrollables keep their normal touch handling. -class ChatZoomWrapper extends StatelessWidget { - ChatZoomWrapper({super.key, required this.child, this.onDoubleTap}); +class ChatZoomWrapper extends StatefulWidget { + const ChatZoomWrapper({super.key, required this.child, this.onDoubleTap}); final Widget child; final VoidCallback? onDoubleTap; - final _ZoomGestureState _state = _ZoomGestureState(); + + @override + State createState() => _ChatZoomWrapperState(); +} + +class _ChatZoomWrapperState extends State { + double? _startScale; @override Widget build(BuildContext context) { @@ -21,25 +27,23 @@ class ChatZoomWrapper extends StatelessWidget { behavior: HitTestBehavior.translucent, onDoubleTap: () { service.reset(); - onDoubleTap?.call(); + service.persist(); + widget.onDoubleTap?.call(); }, onScaleStart: (details) { if (details.pointerCount != 2) return; - _state.startScale = service.scale; + _startScale = service.scale; }, onScaleUpdate: (details) { if (details.pointerCount != 2) return; - final baseScale = _state.startScale ?? service.scale; + final baseScale = _startScale ?? service.scale; service.setScale(baseScale * details.scale); }, onScaleEnd: (_) { - _state.startScale = null; + _startScale = null; + service.persist(); }, - child: child, + child: widget.child, ); } } - -class _ZoomGestureState { - double? startScale; -}