Wigets学习之Toast轻提示
时间:2021-07-01 07:52:31
Flutter
中只有一种轻提示SnackBar
,所以要实现其他效果需要自实现
SnackBar
从底部弹出一条提示信息
content
:提示内容(必填)backgroundColor
:背景色elevation
:阴影,behavior=SnackBarBehavior.floating
才能看到效果margin
:外边距,需要behavior=SnackBarBehavior.floating
才行,否则会报错padding
:内边距width
:宽度,需要behavior=SnackBarBehavior.floating && margin == null
才行,否则会报错shape
:裁剪,可通过此属性设置圆角,如:shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(48)))
behavior
:设置定位,默认为SnackBarBehavior.fixed(紧贴底部)
,behavior=SnackBarBehavior.floating(悬停底部)
action
:操作,可用SnackBarAction
指定duration
:动画时长animation
:动画onVisible
:显示事件
SnackBarAction
SnackBar
操作
label
:操作的文字onPressed
:操作的点击事件textColor
:文字颜色disabledTextColor
:文字禁用颜色
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
@override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: scaffoldMessengerKey,
title: 'Toast轻提示',
home: Scaffold(
appBar: AppBar(
title: Text("Toast轻提示"),
),
body: Row(
children: [
OutlinedButton(
onPressed: () {
scaffoldMessengerKey.currentState?.showSnackBar(SnackBar(
content: Text('这是一条提示'),
behavior: SnackBarBehavior.floating,
elevation: 12,
margin: EdgeInsets.all(12),
action: SnackBarAction(
label: '确定',
onPressed: () {},
),
onVisible: () {
print('显示');
}));
},
child: Text('显示提示')),
],
),
),
);
}
}
Toast(自实现)
注意:此实现父部件一定要是一个
StatefulWidget
,否则使用Overlay.of(context)
获取OverlayState
时总是为null
,等找到解决办法再更新一下代码。
toast.dart
import 'dart:async';
import 'package:flutter/material.dart';
class Toast {
Toast(
{required this.context,
this.child,
this.message,
this.position = ToastPosition.center,
this.icon,
this.duration = 2000,
this.color = Colors.black,
this.textColor = Colors.white})
: assert(child == null || message == null,
'child != null || message != null'),
assert(message != null && child == null,
'message != null && child == null') {
OverlayState overlay = Overlay.of(context)!;
// 屏幕大小
final offset = MediaQuery.of(context).size.height * 0.2;
// 距顶部距离
final double? top = position == ToastPosition.top ? offset : null;
// 距底部距离
final double? bottom = position == ToastPosition.bottom ? offset : null;
final Widget iconText = icon != null
? Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 4,
),
icon!,
SizedBox(
height: 4,
),
Text(message!)
],
)
: Text(message!);
// 提示信息
final text = child ?? iconText;
if (_overlayEntry != null) {
clear();
}
_overlayEntry = OverlayEntry(builder: (BuildContext context) {
// 如果是Toast.loading则icon一定是_LoadingIcon类型
final isDisabledTap = icon != null && icon is _LoadingIcon;
final body = _Body(
text,
color: color,
);
final absorbPointer = AbsorbPointer(
ignoringSemantics: true,
child: body,
);
return Positioned(
top: top,
bottom: bottom,
child: _MessageDefaultStyle(
isDisabledTap ? absorbPointer : body,
color: textColor,
));
});
overlay.insert(_overlayEntry!);
// 展示时长为0,则为不关闭
if (duration != 0) {
_timer = Timer(Duration(milliseconds: duration!), clear);
}
}
final BuildContext context;
// 自定义内容
final Widget? child;
// 提示内容
final String? message;
// 位置
final ToastPosition? position;
// 图标
final Widget? icon;
// 展示时长(ms)
final int? duration;
// 背景色
final Color? color;
// 文本颜色
final Color? textColor;
factory Toast.loading(
{required BuildContext context,
Widget? child,
String? message,
int? duration,
Color? color,
Color? textColor}) = _ToastLoading;
factory Toast.success(
{required BuildContext context,
Widget? child,
String? message,
int? duration,
Color? color,
Color? textColor}) = _ToastSuccess;
factory Toast.error(
{required BuildContext context,
Widget? child,
String? message,
int? duration,
Color? color,
Color? textColor}) = _ToastError;
// 清除
static clear() {
_timer?.cancel();
_timer = null;
_overlayEntry?.remove();
_overlayEntry = null;
}
}
enum ToastPosition { top, center, bottom }
OverlayEntry? _overlayEntry;
Timer? _timer;
class _ToastLoading extends Toast {
_ToastLoading(
{required this.context,
this.child,
this.message,
this.duration = 2000,
this.color = Colors.black,
this.textColor = Colors.white,
this.iconBackgroupColor = Colors.white,
this.iconColor = Colors.black12})
: super(
context: context,
child: child,
message: message,
duration: duration,
icon: _LoadingIcon(
backgroundColor: iconBackgroupColor!,
color: iconColor!,
),
color: color,
textColor: textColor);
final BuildContext context;
// 自定义内容
final Widget? child;
// 提示内容
final String? message;
// 展示时长(ms)
final int? duration;
// 背景色
final Color? color;
// 文本颜色
final Color? textColor;
// loading背景色
final Color? iconBackgroupColor;
// loading颜色
final Color? iconColor;
}
class _ToastSuccess extends Toast {
_ToastSuccess(
{required this.context,
this.child,
this.message,
this.duration = 2000,
this.color = Colors.black,
this.textColor = Colors.white,
this.iconColor = Colors.white})
: super(
context: context,
child: child,
message: message,
duration: duration,
icon: Icon(
Icons.check_circle,
color: iconColor,
size: 36,
),
color: color,
textColor: textColor);
final BuildContext context;
// 自定义内容
final Widget? child;
// 提示内容
final String? message;
// 展示时长(ms)
final int? duration;
// 背景色
final Color? color;
// 文本颜色
final Color? textColor;
// icon颜色
final Color? iconColor;
}
class _ToastError extends Toast {
_ToastError(
{required this.context,
this.child,
this.message,
this.duration = 2000,
this.color = Colors.black,
this.textColor = Colors.white,
this.iconColor = Colors.white})
: super(
context: context,
child: child,
message: message,
duration: duration,
icon: Icon(
Icons.cancel,
color: iconColor,
size: 36,
),
color: color,
textColor: textColor);
final BuildContext context;
// 自定义内容
final Widget? child;
// 提示内容
final String? message;
// 展示时长(ms)
final int? duration;
// 背景色
final Color? color;
// 文本颜色
final Color? textColor;
// icon颜色
final Color? iconColor;
}
// 文本默认样式
class _MessageDefaultStyle extends StatelessWidget {
const _MessageDefaultStyle(this.child, {Key? key, this.color})
: super(key: key);
final Widget child;
final Color? color;
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: color!),
child: child,
);
}
}
// 内容主体
class _Body extends StatelessWidget {
const _Body(this.child, {Key? key, this.color}) : super(key: key);
final Widget child;
final Color? color;
@override
Widget build(BuildContext context) {
// 屏幕大小
final windowWidth = MediaQuery.of(context).size.width;
// 最大宽度
final maxWidth = windowWidth * 0.7;
return Container(
width: windowWidth,
child: Center(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: color!.withOpacity(0.7),
borderRadius: BorderRadius.all(Radius.circular(4))),
constraints: BoxConstraints(
minWidth: 88, minHeight: 40, maxWidth: maxWidth),
child: child),
));
}
}
// 加载Icon
class _LoadingIcon extends StatelessWidget {
_LoadingIcon(
{Key? key,
this.backgroundColor = Colors.white,
this.color = Colors.black12})
: super(key: key);
final Color backgroundColor;
final Color color;
@override
Widget build(BuildContext context) {
return CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(color),
backgroundColor: backgroundColor,
);
}
}
context
:上下文(必填)child
:自定义内容message
:提示内容position
:显示位置,默认为ToastPosition.center
icon
:图标duration
:展示时长,默认为 2000,单位:ms
color
:背景色textColor
:文本颜色
import 'package:flutter/material.dart';
import 'package:learn/widgets/toast.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Toast轻提示",
home: Home(),
);
}
}
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Toast轻提示'),
),
body: Wrap(
children: [
OutlinedButton(
child: Text('显示Toast'),
onPressed: () {
Toast(
context: context,
message: "这是一个toast",
);
},
),
OutlinedButton(
child: Text('显示顶部Toast'),
onPressed: () {
Toast(
context: context,
message: "这是一个顶部toast",
position: ToastPosition.top);
},
),
OutlinedButton(
child: Text('显示底部Toast'),
onPressed: () {
Toast(
context: context,
message: "这是一个底部toast",
position: ToastPosition.bottom);
},
),
OutlinedButton(
child: Text('显示自定义背景色Toast'),
onPressed: () {
Toast(
context: context,
message: "这是一个自定义背景色toast",
color: Colors.yellow.shade800);
},
),
OutlinedButton(
child: Text('显示自定义颜色Toast'),
onPressed: () {
Toast(
context: context,
message: "这是一个自定义颜色toast",
textColor: Colors.green);
},
),
],
));
}
}
Toast 还提供了三个命名构造函数:Toast.loading
,Toast.success
,Toast.error
import 'package:flutter/material.dart';
import 'package:learn/widgets/toast.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Toast轻提示",
home: Home(),
);
}
}
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Toast轻提示'),
),
body: Wrap(
children: [
OutlinedButton(
child: Text('显示Loading Toast'),
onPressed: () {
Toast.loading(context: context, message: "这是一个Loading Toast");
},
),
OutlinedButton(
child: Text('显示成功Toast'),
onPressed: () {
Toast.success(context: context, message: "这是一个成功Toast");
},
),
OutlinedButton(
child: Text('显示失败Toast'),
onPressed: () {
Toast.error(context: context, message: "这是一个Loading Toast");
},
),
],
));
}
}