|
|
import 'dart:async';
import 'package:flutter/material.dart';
enum Toast { short, long, }
enum ToastGravity { top, bottom, center, topLeft, topRight, bottomLeft, bottomRight, centerLEft, centerRight, snackBar, }
typedef PositionedToastBuilder = Widget Function(BuildContext context, Widget child);
class FToast { late BuildContext context;
static final FToast _instance = FToast._internal();
factory FToast() { return _instance; }
FToast init(BuildContext context) { _instance.context = context; return _instance; }
FToast._internal();
OverlayEntry? _entry; final List<_ToastEntry> _overlayQueue = []; Timer? _timer;
void _showOverlay() { if (_overlayQueue.isEmpty) { _entry = null; return; } _ToastEntry toastEntry = _overlayQueue.removeAt(0); _entry = toastEntry.entry; Overlay.of(context).insert(_entry!);
_timer = Timer(toastEntry.duration, () { Future.delayed(const Duration(milliseconds: 360), () { removeCustomToast(); }); }); }
void removeCustomToast() { _timer!.cancel(); _timer = null; if (_entry != null) _entry!.remove(); _entry = null; _showOverlay(); }
void removeQueuedCustomToasts() { _timer?.cancel(); _timer = null; _overlayQueue.clear(); if (_entry != null) _entry!.remove(); _entry = null; }
void showToast({ required Widget child, PositionedToastBuilder? positionedToastBuilder, required Duration? toastDuration, required ToastGravity gravity, int fadeDuration = 350, }) { Widget newChild = _ToastStateFul(child, toastDuration ?? const Duration(seconds: 2), fadeDuration: fadeDuration); if (gravity == ToastGravity.bottom) { if (MediaQuery.of(context).viewInsets.bottom != 0) { gravity = ToastGravity.center; } } OverlayEntry newEntry = OverlayEntry(builder: (context) { if (positionedToastBuilder != null) return positionedToastBuilder(context, newChild); return _getPositionWidgetBasedOnGravity(newChild, gravity); });
_overlayQueue.add(_ToastEntry(entry: newEntry, duration: toastDuration ?? const Duration(seconds: 2))); if (_timer == null) _showOverlay(); }
Positioned _getPositionWidgetBasedOnGravity(Widget child, ToastGravity gravity) { switch (gravity) { case ToastGravity.top: return Positioned(top: 100.0, left: 24.0, right: 24.0, child: child); case ToastGravity.topLeft: return Positioned(top: 100.0, left: 24.0, child: child); case ToastGravity.topRight: return Positioned(top: 100.0, right: 24.0, child: child); case ToastGravity.center: return Positioned(top: 50.0, bottom: 50.0, left: 24.0, right: 24.0, child: child); case ToastGravity.centerLEft: return Positioned(top: 50.0, bottom: 50.0, left: 24.0, child: child); case ToastGravity.centerRight: return Positioned(top: 50.0, bottom: 50.0, right: 24.0, child: child); case ToastGravity.bottomLeft: return Positioned(bottom: 50.0, left: 24.0, child: child); case ToastGravity.bottomRight: return Positioned(bottom: 50.0, right: 24.0, child: child); case ToastGravity.snackBar: return Positioned(bottom: MediaQuery.of(context).viewInsets.bottom, left: 0, right: 0, child: child); case ToastGravity.bottom: default: return Positioned(bottom: 50.0, left: 24.0, right: 24.0, child: child); } } }
class _ToastEntry { final OverlayEntry entry; final Duration duration;
_ToastEntry({required this.entry, required this.duration}); }
class _ToastStateFul extends StatefulWidget { const _ToastStateFul(this.child, this.duration, {Key? key, this.fadeDuration = 350}) : super(key: key);
final Widget child; final Duration duration; final int fadeDuration;
@override ToastStateFulState createState() => ToastStateFulState(); }
class ToastStateFulState extends State<_ToastStateFul> with SingleTickerProviderStateMixin { void showIt() { _animationController.forward(); }
void hideIt() { _animationController.reverse(); _timer.cancel(); }
late AnimationController _animationController; late Animation _fadeAnimation; late Timer _timer;
@override void initState() { _animationController = AnimationController( vsync: this, duration: Duration(milliseconds: widget.fadeDuration), ); _fadeAnimation = CurvedAnimation(parent: _animationController, curve: Curves.easeIn); super.initState(); showIt(); _timer = Timer(widget.duration, () { hideIt(); }); }
@override void deactivate() { _timer.cancel(); _animationController.stop(); super.deactivate(); }
@override void dispose() { _timer.cancel(); _animationController.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return FadeTransition( opacity: _fadeAnimation as Animation<double>, child: Center( child: Material( color: Colors.transparent, child: widget.child, ), ), ); } }
|