elysia/lib/plugin/GlobalMessage.dart
2025-11-04 09:53:47 +08:00

216 lines
6.5 KiB
Dart

import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'CacheAavatar.dart';
import 'StringTool.dart';
// 全局消息显示时长(可修改)
Duration GlobalMessage_BakDuration = const Duration(seconds: 5);
class GlobalData {
final String? image;
final String title;
final String content;
final dynamic payload;
GlobalData({
this.image,
required this.title,
required this.content,
this.payload,
});
}
class GlobalMessage_Bak {
static final GlobalMessage_Bak _instance = GlobalMessage_Bak._internal();
static BuildContext? _context;
// 消息队列
static final Queue<_MessageItem> _queue = Queue();
factory GlobalMessage_Bak() => _instance;
GlobalMessage_Bak._internal();
static void init(BuildContext context) {
_context = context;
}
static void push({
required GlobalData data,
required Function(BuildContext) onTap,
BuildContext? context,
Duration? duration,
}) {
final useContext = context ?? _context;
if (useContext == null) {
throw Exception('GlobalMessage_Bak not initialized. Call GlobalMessage_Bak.init() first.');
}
final controller = AnimationController(
vsync: Navigator.of(useContext),
duration: const Duration(milliseconds: 300),
);
final slideAnimation = Tween<Offset>(
begin: const Offset(0, -1),
end: Offset.zero,
).animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
final entry = OverlayEntry(
builder: (_) => Positioned(
top: MediaQuery.of(useContext).padding.top,
left: 0,
right: 0,
child: SlideTransition(
position: slideAnimation,
child: _MessageContent(
data: data,
onTap: () {
GlobalMessage_Bak.hide(); // 统一通过 GlobalMessage_Bak.hide() 隐藏
onTap(useContext);
},
),
),
),
);
// 添加到队列
_queue.add(_MessageItem(entry: entry, controller: controller, duration: duration ?? GlobalMessage_BakDuration));
// 如果队列只有这一条,立即显示
if (_queue.length == 1) {
_showNext();
}
}
static void _showNext() {
if (_queue.isEmpty) return;
final item = _queue.first;
Overlay.of(_context!)!.insert(item.entry);
item.controller.forward();
// 自动隐藏
Future.delayed(item.duration, () => hideCurrent());
}
static void hideCurrent() {
if (_queue.isEmpty) return;
final item = _queue.first;
item.controller.reverse().then((_) {
item.entry.remove();
item.controller.dispose();
_queue.removeFirst();
_showNext(); // 显示队列中的下一条消息
});
}
// 手动隐藏当前显示的消息
static void hide() => hideCurrent();
}
// 队列中的消息项
class _MessageItem {
final OverlayEntry entry;
final AnimationController controller;
final Duration duration;
_MessageItem({required this.entry, required this.controller, required this.duration});
}
class _MessageContent extends StatefulWidget {
final GlobalData data;
final VoidCallback onTap;
const _MessageContent({Key? key, required this.data, required this.onTap}) : super(key: key);
@override
__MessageContentState createState() => __MessageContentState();
}
class __MessageContentState extends State<_MessageContent> {
late Offset _dragStartOffset;
@override
Widget build(BuildContext context) {
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(now);
return GestureDetector(
onTap: widget.onTap,
onVerticalDragStart: (details) {
_dragStartOffset = details.globalPosition;
},
onVerticalDragUpdate: (details) {
if (details.globalPosition.dy < _dragStartOffset.dy - 50) {
GlobalMessage_Bak.hide();
}
},
child: Dismissible(
key: Key(DateTime.now().millisecondsSinceEpoch.toString()),
direction: DismissDirection.horizontal,
onDismissed: (direction) => GlobalMessage_Bak.hide(),
background: Container(color: Colors.transparent),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 8, offset: Offset(0, 4))],
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.data.image != null)
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: CacheAvatar(url: widget.data.image!,),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
widget.data.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black87,
decoration: TextDecoration.none),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
formattedTime,
style: const TextStyle(fontSize: 12, color: Colors.grey,decoration: TextDecoration.none),
),
],
),
const SizedBox(height: 6),
Text(
StringTool.stripMarkdown(widget.data.content),
style: TextStyle(
color: Colors.grey[500],
fontSize: 12,
decoration: TextDecoration.none),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
),
),
);
}
}