Files
ai_chat_assistant/lib/widgets/floating_icon.dart

153 lines
4.4 KiB
Dart
Raw Normal View History

2025-08-12 13:36:42 +08:00
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'; // 添加此行
2025-08-20 16:29:50 +08:00
import 'package:basic_intl/intl.dart';
2025-08-12 13:36:42 +08:00
class FloatingIcon extends StatefulWidget {
const FloatingIcon({super.key});
@override
State<FloatingIcon> createState() => _FloatingIconState();
}
class _FloatingIconState extends State<FloatingIcon>
with SingleTickerProviderStateMixin {
2025-08-12 13:36:42 +08:00
Offset _position = const Offset(10, 120);
final iconSize = 80.0;
bool _isShowPartScreen = false;
late AnimationController _waveAnimationController;
2025-08-12 13:36:42 +08:00
// 新增:图片切换相关
int _imageIndex = 0;
late final List<String> _iconImages = [
'assets/images/ai1_hd.png',
'assets/images/ai0_hd.png',
];
2025-08-20 16:29:50 +08:00
late final List<String> _iconImagesEn = [
'assets/images/ai1_hd_en.png',
'assets/images/ai0_hd_en.png',
];
Timer? _imageTimer; // 用于定时切换图片
2025-08-12 13:36:42 +08:00
@override
void initState() {
super.initState();
_waveAnimationController = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
);
// 使用Timer.periodic定时切换图片
_imageTimer = Timer.periodic(const Duration(seconds: 3), (timer) {
setState(() {
_imageIndex = (_imageIndex + 1) % _iconImages.length;
});
});
}
@override
void dispose() {
_waveAnimationController.dispose();
_imageTimer?.cancel(); // 释放Timer
super.dispose();
2025-08-12 13:36:42 +08:00
}
void _showPartScreen() {
setState(() {
_isShowPartScreen = true;
});
}
void _hidePartScreen() {
setState(() {
_isShowPartScreen = false;
});
}
void _showFullScreen() async {
2025-08-22 17:35:02 +08:00
_hidePartScreen();
2025-08-12 13:36:42 +08:00
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const FullScreen(),
),
);
}
void _updatePosition(Offset delta) {
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);
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
if (_isShowPartScreen)
PartScreen(
onHide: _hidePartScreen,
),
Positioned(
bottom: _position.dy,
right: _position.dx,
child: Consumer<MessageService>(
builder: (context, messageService, child) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (messageService.isRecording) {
if (!_waveAnimationController.isAnimating) {
_waveAnimationController.repeat();
}
} else {
if (_waveAnimationController.isAnimating) {
_waveAnimationController.stop();
}
}
});
2025-08-12 13:36:42 +08:00
return GestureDetector(
onTap: _showFullScreen,
onLongPress: () async {
_showPartScreen();
_waveAnimationController.repeat();
2025-08-12 13:36:42 +08:00
await messageService.startVoiceInput();
},
onLongPressUp: () async {
_waveAnimationController.stop();
2025-08-12 13:36:42 +08:00
await messageService.stopAndProcessVoiceInput();
final hasMessage = messageService.messages.isNotEmpty;
if (!hasMessage) {
_hidePartScreen();
}
},
onPanUpdate: (details) => _updatePosition(details.delta),
child: messageService.isRecording
? FloatingIconWithWave(
animationController: _waveAnimationController,
iconSize: iconSize,
waveColor: Colors.white,
)
: Image.asset(
2025-08-20 16:29:50 +08:00
Intl.getCurrentLocale().startsWith('zh')
? _iconImages[_imageIndex]:_iconImagesEn[_imageIndex],
width: iconSize,
height: iconSize,package: 'ai_chat_assistant',
),
2025-08-12 13:36:42 +08:00
);
},
),
),
],
);
}
}