import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/message_service.dart'; import '../screens/full_screen.dart'; import '../screens/part_screen.dart'; import 'floating_icon_with_wave.dart'; import 'dart:async'; import 'package:basic_intl/intl.dart'; import '../utils/assets_util.dart'; class FloatingIcon extends StatefulWidget { const FloatingIcon({super.key}); @override State createState() => _FloatingIconState(); } class _FloatingIconState extends State with TickerProviderStateMixin { Offset _position = const Offset(10, 120); final iconSize = 80.0; bool _isShowPartScreen = false; bool _isDragging = false; bool _isAnimating = false; Offset _targetPosition = const Offset(10, 120); // 水波纹动画控制器 late AnimationController _waveAnimationController; // PartScreen 显示动画控制器 late AnimationController _partScreenAnimationController; // 图片切换定时器 Timer? _imageTimer; // 添加长按状态标记 bool _isLongPressing = false; // 图片切换相关 int _imageIndex = 0; late final List _iconImages = [ 'ai1_hd.png', 'ai0_hd.png', ]; late final List _iconImagesEn = [ 'ai1_hd_en.png', 'ai0_hd_en.png', ]; @override void initState() { super.initState(); _waveAnimationController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); // PartScreen动画控制器 _partScreenAnimationController = AnimationController( duration: const Duration(milliseconds: 250), vsync: this, ); // 图片切换定时器 _imageTimer = Timer.periodic(const Duration(seconds: 3), (timer) { if (mounted && !_isDragging) { setState(() { _imageIndex = (_imageIndex + 1) % _iconImages.length; }); } }); } @override void dispose() { _waveAnimationController.dispose(); _partScreenAnimationController.dispose(); _imageTimer?.cancel(); super.dispose(); } void _showPartScreen() { setState(() { _isShowPartScreen = true; }); _partScreenAnimationController.forward(); } void _hidePartScreen() { _partScreenAnimationController.reverse().then((_) { if (mounted) { setState(() { _isShowPartScreen = false; }); } }); } // 显示全屏界面 void _showFullScreen() async { _hidePartScreen(); final messageService = MessageService.instance; await Navigator.of(context).push( MaterialPageRoute( builder: (context) => ChangeNotifierProvider.value( value: messageService, // 传递同一个单例实例 child: const FullScreen(), ), ), ); } // 更新位置 void _updatePosition(Offset delta) { if (!_isDragging) return; setState(() { final screenSize = MediaQuery.of(context).size; double newX = (_position.dx - delta.dx).clamp(0.0, screenSize.width - iconSize); double newY = (_position.dy - delta.dy).clamp(0.0, screenSize.height - iconSize); _position = Offset(newX, newY); }); } // 吸附到屏幕边缘 void _snapToEdge() { final screenSize = MediaQuery.of(context).size; final centerX = screenSize.width / 2; // 判断应该吸到左边还是右边 final shouldSnapToLeft = _position.dx < centerX - iconSize / 2; final targetX = shouldSnapToLeft ? 0.0 : screenSize.width - iconSize; setState(() { _targetPosition = Offset(targetX, _position.dy); _isAnimating = true; }); // 使用定时器在动画完成后更新状态 Timer(const Duration(milliseconds: 100), () { if (mounted) { setState(() { _position = _targetPosition; _isAnimating = false; }); } }); } void _onPanStart(DragStartDetails details) { _isDragging = true; _waveAnimationController.stop(); } void _onPanEnd(DragEndDetails details) { _isDragging = false; _snapToEdge(); } @override Widget build(BuildContext context) { // 奇怪的代码,不应该放在build里面 // WidgetsBinding.instance.addPostFrameCallback((_) { // if (messageService.isRecording) { // if (!_waveAnimationController.isAnimating) { // _waveAnimationController.repeat(); // } // } else { // if (_waveAnimationController.isAnimating) { // _waveAnimationController.stop(); // } // } // }); return ChangeNotifierProvider.value( value: MessageService.instance, child: _buildFloatingIcon(), ); } Widget _buildFloatingIcon() { return Stack( children: [ if (_isShowPartScreen) FadeTransition( opacity: _partScreenAnimationController, child: SlideTransition( position: Tween( begin: const Offset(0, 0.3), end: Offset.zero, ).animate(CurvedAnimation( parent: _partScreenAnimationController, curve: Curves.easeOutQuart, )), child: PartScreen( onHide: _hidePartScreen, floatingIconPosition: _position, iconSize: iconSize, )), ), AnimatedPositioned( duration: const Duration(milliseconds: 250), curve: Curves.easeOutBack, bottom: _isAnimating ? _targetPosition.dy : _position.dy, right: _isAnimating ? _targetPosition.dx : _position.dx, child: Consumer( builder: (context, messageService, child) { return GestureDetector( onTap: _showFullScreen, onLongPress: () async { debugPrint('Long press'); _isLongPressing = true; // 标记开始长按 _showPartScreen(); _waveAnimationController.repeat(); await messageService.startVoiceInput(); }, onLongPressUp: () async { debugPrint('Long press up'); _isLongPressing = false; // 清除长按标记 _waveAnimationController.stop(); await messageService.stopAndProcessVoiceInput(); final hasMessage = messageService.messages.isNotEmpty; if (!hasMessage) { _hidePartScreen(); } }, onLongPressEnd: (d) { debugPrint('Long press end $d'); _isLongPressing = false; // 清除长按标记 }, onPanStart: (details) { // 只有在非长按状态下才允许拖拽 if (!_isLongPressing) { _onPanStart(details); } }, onPanUpdate: (details) { // 只有在拖拽状态下才更新位置 if (_isDragging && !_isLongPressing) { _updatePosition(details.delta); } }, onPanEnd: (details) { if (!_isLongPressing) { _onPanEnd(details); } }, child: AnimatedScale( scale: _isDragging ? 1.1 : 1.0, duration: const Duration(milliseconds: 150), child: messageService.isRecording ? FloatingIconWithWave( animationController: _waveAnimationController, iconSize: iconSize, waveColor: Colors.white, ) : AssetsUtil.getImageWidget( Intl.getCurrentLocale().startsWith('zh') ? _iconImages[_imageIndex] : _iconImagesEn[_imageIndex], width: iconSize, height: iconSize, ), ), ); }, ), ) ], ); } }