You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
5.0 KiB
198 lines
5.0 KiB
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,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|