Files
ai_chat_assistant/lib/widgets/chat/chat_window_content.dart

187 lines
5.4 KiB
Dart

import 'package:flutter/material.dart';
import 'dart:ui';
import '../../widgets/chat_box.dart';
import 'package:provider/provider.dart';
import '../../services/message_service.dart';
import '../../widgets/gradient_background.dart';
import '../../utils/assets_util.dart';
import '../../pages/full_screen.dart';
class ChatWindowContent extends StatefulWidget {
final AnimationController? animationController;
final double? minHeight;
final double? maxHeight;
final double? chatWidth;
final VoidCallback? onCloseWindow;
const ChatWindowContent({
super.key,
this.onCloseWindow,
this.animationController,
this.minHeight,
this.maxHeight,
this.chatWidth,
});
@override
State<ChatWindowContent> createState() => _ChatWindowContentState();
}
class _ChatWindowContentState extends State<ChatWindowContent> {
final ScrollController _scrollController = ScrollController();
int _lastMessageCount = 0;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final messageService = Provider.of<MessageService>(context, listen: false);
messageService.removeNonListeningMessages();
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _scrollToBottom() {
if (_scrollController.hasClients) {
_scrollController.animateTo(
0.0,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
);
}
}
void _openFullScreen() async {
if (widget.onCloseWindow != null) {
widget.onCloseWindow!();
}
final messageService = context.read<MessageService>();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChangeNotifierProvider.value(
value: messageService, // 传递同一个单例实例
child: const FullScreenPage(),
),
),
);
}
@override
Widget build(BuildContext context) {
final minHeight = widget.minHeight ?? 0;
final maxHeight = widget.maxHeight ?? double.infinity;
final chatWidth = widget.chatWidth ?? double.infinity;
final animationController = widget.animationController;
return Consumer<MessageService>(
builder: (context, messageService, child) {
final messageCount = messageService.messages.length;
if (messageCount > _lastMessageCount) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
});
}
_lastMessageCount = messageCount;
var chatContent = _buildChatContent(messageService);
// 包裹弹窗动画
if (animationController != null) {
Widget content = FadeTransition(
opacity: animationController,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.3),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animationController,
curve: Curves.easeOutQuart,
)),
child: TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0.8, end: 1.0),
duration: const Duration(milliseconds: 200),
curve: Curves.easeOutBack,
builder: (context, scale, child) {
return Transform.scale(
scale: scale,
child: Container(
width: chatWidth,
constraints: BoxConstraints(
minHeight: minHeight,
maxHeight: maxHeight,
),
child: child,
),
);
},
child: chatContent,
),
),
);
}
Widget content = Container(
width: chatWidth,
constraints: BoxConstraints(
minHeight: minHeight,
maxHeight: maxHeight,
),
child: chatContent,
);
return content;
},
);
}
Widget _buildChatContent(MessageService messageService) {
Widget content = ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
child: GradientBackground(
colors: const [
Color(0XBF3B0A3F),
Color(0xBF0E0E24),
Color(0xBF0C0B33),
],
borderRadius: BorderRadius.circular(6),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.only(top: 50, left: 6, right: 6, bottom: 30),
child: LayoutBuilder(
builder: (context, boxConstraints) {
return ChatBox(
scrollController: _scrollController,
messages: messageService.messages,
);
},
),
),
Positioned(
top: 6,
right: 6,
child: IconButton(
icon: AssetsUtil.getImageWidget(
'open_in_full.png',
width: 24,
height: 24,
),
onPressed: _openFullScreen,
padding: EdgeInsets.zero,
),
),
],
),
),
),
);
return content;
}
}