Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies
150 lines
4.9 KiB
Dart
150 lines
4.9 KiB
Dart
import 'dart:async';
|
|
import 'dart:collection';
|
|
import 'dart:math';
|
|
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/models/models.dart';
|
|
import 'package:fl_clash/widgets/fade_box.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MessageManager extends StatefulWidget {
|
|
final Widget child;
|
|
|
|
const MessageManager({super.key, required this.child});
|
|
|
|
@override
|
|
State<MessageManager> createState() => MessageManagerState();
|
|
}
|
|
|
|
class MessageManagerState extends State<MessageManager> {
|
|
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
|
|
final _bufferMessages = Queue<CommonMessage>();
|
|
final _activeTimers = <String, Timer>{};
|
|
bool _isDisplayingMessage = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_messagesNotifier.dispose();
|
|
for (final timer in _activeTimers.values) {
|
|
timer.cancel();
|
|
}
|
|
_activeTimers.clear();
|
|
_bufferMessages.clear();
|
|
super.dispose();
|
|
}
|
|
|
|
void message(String text) {
|
|
final commonMessage = CommonMessage(id: utils.uuidV4, text: text);
|
|
_bufferMessages.add(commonMessage);
|
|
commonPrint.log('message: $text');
|
|
_processQueue();
|
|
}
|
|
|
|
void _cancelMessage(String id) {
|
|
_bufferMessages.removeWhere((msg) => msg.id == id);
|
|
if (_activeTimers.containsKey(id)) {
|
|
_removeMessage(id);
|
|
}
|
|
}
|
|
|
|
void _processQueue() {
|
|
if (_isDisplayingMessage || _bufferMessages.isEmpty) {
|
|
return;
|
|
}
|
|
_isDisplayingMessage = true;
|
|
final message = _bufferMessages.removeFirst();
|
|
_messagesNotifier.value = List.from(_messagesNotifier.value)..add(message);
|
|
final timer = Timer(message.duration, () {
|
|
_removeMessage(message.id);
|
|
});
|
|
_activeTimers[message.id] = timer;
|
|
}
|
|
|
|
void _removeMessage(String id) {
|
|
_activeTimers.remove(id)?.cancel();
|
|
final currentMessages = List<CommonMessage>.from(_messagesNotifier.value);
|
|
currentMessages.removeWhere((msg) => msg.id == id);
|
|
_messagesNotifier.value = currentMessages;
|
|
_isDisplayingMessage = false;
|
|
_processQueue();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Stack(
|
|
children: [
|
|
widget.child,
|
|
ValueListenableBuilder(
|
|
valueListenable: _messagesNotifier,
|
|
builder: (_, messages, _) {
|
|
return Container(
|
|
margin: EdgeInsets.only(
|
|
top: kToolbarHeight + 12,
|
|
left: 12,
|
|
right: 12,
|
|
),
|
|
child: FadeThroughBox(
|
|
alignment: Alignment.topRight,
|
|
child: messages.isEmpty
|
|
? SizedBox()
|
|
: LayoutBuilder(
|
|
key: Key(messages.last.id),
|
|
builder: (_, constraints) {
|
|
return Dismissible(
|
|
key: ValueKey(messages.last.id),
|
|
onDismissed: (_) {
|
|
_cancelMessage(messages.last.id);
|
|
},
|
|
child: Card(
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(14),
|
|
),
|
|
),
|
|
elevation: 10,
|
|
color: context.colorScheme.surfaceContainerHigh,
|
|
child: Container(
|
|
width: min(constraints.maxWidth, 500),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 12,
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Flexible(
|
|
child: Text(
|
|
messages.last.text,
|
|
maxLines: 3,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
SizedBox(width: 16),
|
|
TextButton(
|
|
onPressed: () {
|
|
_cancelMessage(messages.last.id);
|
|
},
|
|
child: Text(appLocalizations.cancel),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|