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/providers/providers.dart'; import 'package:fl_clash/widgets/fade_box.dart'; import 'package:fl_clash/widgets/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class StatusManager extends StatefulWidget { final Widget child; const StatusManager({super.key, required this.child}); @override State createState() => StatusManagerState(); } class StatusManagerState extends State { final _messagesNotifier = ValueNotifier>([]); final _bufferMessages = Queue(); final _activeTimers = {}; 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, {MessageActionState? actionState}) { final commonMessage = CommonMessage( id: utils.uuidV4, text: text, actionState: actionState, ); _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.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, Consumer( builder: (_, ref, child) { final top = ref.watch(overlayTopOffsetProvider); return Container( margin: EdgeInsets.only( top: top + MediaQuery.of(context).viewPadding.top + 8, ), child: child, ); }, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: AnimatedSize( duration: animateDuration, child: ValueListenableBuilder( valueListenable: _messagesNotifier, builder: (_, messages, _) { return FadeThroughBox( alignment: Alignment.centerRight, 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 RoundedSuperellipseBorder( borderRadius: BorderRadius.all( Radius.circular(14), ), ), elevation: 10, color: context .colorScheme .surfaceContainerHigh, child: Container( width: min(constraints.maxWidth, 500), constraints: BoxConstraints( minHeight: 54, ), padding: EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Text( messages.last.text, maxLines: 3, style: context .textTheme .labelLarge ?.copyWith( color: context .colorScheme .onSurfaceVariant, ), overflow: TextOverflow.ellipsis, ), ), SizedBox(width: 16), if (messages.last.actionState != null) CommonMinFilledButtonTheme( child: FilledButton.tonal( onPressed: () async { _cancelMessage( messages.last.id, ); messages.last.actionState! .action(); }, child: Text( messages .last .actionState! .actionText, ), ), ), ], ), ), ), ); }, ), ); }, ), ), ), // LoadingIndicator(), ], ), ), ], ); } } // class LoadingIndicator extends ConsumerWidget { // const LoadingIndicator({super.key}); // // @override // Widget build(BuildContext context, ref) { // final loading = ref.watch(loadingProvider); // final isMobileView = ref.watch(isMobileViewProvider); // return AnimatedSwitcher( // switchInCurve: Curves.easeIn, // switchOutCurve: Curves.easeOut, // duration: midDuration, // transitionBuilder: (Widget child, Animation animation) { // return SlideTransition( // position: Tween( // begin: const Offset(1, 0), // end: Offset.zero, // ).animate(animation), // child: child, // ); // }, // child: loading && isMobileView // ? Container( // height: 54, // margin: EdgeInsets.only(top: 8, left: 14, right: 14), // child: Material( // elevation: 3, // color: context.colorScheme.surfaceContainer, // surfaceTintColor: context.colorScheme.surfaceTint, // shape: const RoundedSuperellipseBorder( // borderRadius: BorderRadius.all(Radius.circular(14)), // ), // child: Padding( // padding: EdgeInsets.symmetric(horizontal: 16), // child: Row( // mainAxisSize: MainAxisSize.min, // spacing: 12, // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // Flexible( // child: Text( // context.appLocalizations.loading, // style: context.textTheme.labelLarge?.copyWith( // color: context.colorScheme.onSurfaceVariant, // ), // ), // ), // SizedBox( // height: 32, // width: 32, // child: CommonCircleLoading(), // ), // ], // ), // ), // ), // ) // : SizedBox(), // ); // } // }