Skip to content

Instantly share code, notes, and snippets.

@hugeorange
Created June 3, 2023 08:44
Show Gist options
  • Save hugeorange/828f54e13e5c954d83a6ac51730e3e53 to your computer and use it in GitHub Desktop.
Save hugeorange/828f54e13e5c954d83a6ac51730e3e53 to your computer and use it in GitHub Desktop.
Flutter Overlay 创建自定义 Toast
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tablet/components/text/index.dart';
import 'package:tablet/style/color.dart';
class Toast {
static int duration = 2500;
static OverlayEntry? _overlayEntry;
static Timer? _timer;
static void success(String? message) {
_show('success', message ?? '', context: Get.context!);
}
static void error(String? message) {
_show('error', message ?? '', context: Get.context!);
}
static void _show(
String type,
String message, {
required BuildContext context,
}) {
_timer?.cancel();
_overlayEntry?.remove();
_overlayEntry = OverlayEntry(
builder: (ctx) => ToastAnimation(
duration: duration,
child: ToastBody(type: type, message: message),
),
);
Overlay.of(context).insert(_overlayEntry!);
_timer = Timer(Duration(milliseconds: duration), () {
_overlayEntry?.remove();
_overlayEntry = null;
});
}
}
class ToastBody extends StatelessWidget {
const ToastBody({super.key, required this.type, required this.message});
// success / error
final String type;
final String message;
@override
Widget build(BuildContext context) {
final color = type == 'success' ? RColor.green : RColor.red;
final icon = type == 'success' ? Icons.done : Icons.error;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8),
),
width: 680,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(icon, color: Colors.white),
const SizedBox(width: 8),
Expanded(
child: RText(
message,
size: 18,
isMultiline: true,
weight: FontWeight.w500,
color: Colors.white,
),
),
],
),
);
}
}
class ToastAnimation extends StatefulWidget {
const ToastAnimation({
super.key,
required this.child,
this.duration = 2500,
this.transitionDuration = 300,
});
final Widget child;
final int duration;
final int transitionDuration;
@override
State<ToastAnimation> createState() => _ToastAnimationState();
}
class _ToastAnimationState extends State<ToastAnimation>
with SingleTickerProviderStateMixin {
late final AnimationController controller;
late Animation<double> animation;
Timer? _timer;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(milliseconds: widget.transitionDuration),
vsync: this,
)..forward();
animation = Tween<double>(begin: -60, end: 60).animate(
CurvedAnimation(parent: controller, curve: Curves.easeInOut),
);
final delay = widget.duration - widget.transitionDuration;
_timer = Timer(Duration(milliseconds: delay), controller.reverse);
}
@override
void dispose() {
_timer?.cancel();
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (BuildContext context, child) {
return Positioned(
top: animation.value,
left: 0,
right: 0,
child: Center(child: widget.child),
);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment