Created
June 3, 2023 08:44
-
-
Save hugeorange/828f54e13e5c954d83a6ac51730e3e53 to your computer and use it in GitHub Desktop.
Flutter Overlay 创建自定义 Toast
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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