介绍
Flutter 常见的 loading 框一般是采用 showDialog
的方式弹出一个新的界面,首先并不美观易用,其次,还有一个很大的弊端,因为取消 Loading 框时需要采用 Navigator.pop
方法,会造成严重路由混乱,甚至在一些特殊场景下甚至没有办法实现。
下图是一个按钮,在点击时会触发相应事件,按钮变为不可点击的 loading 状态,事件执行完成后 loading 状态自动结束,并执行其他逻辑操作。
代码
主要代码如下:
import 'package:flutter/material.dart';
/// 含 Loading 的按钮
///
/// @author seliote
/// @version 2021-09-08
/// 异步的 VoidCallback,可用于等待执行完成
typedef AsyncVoidCallback = Future<void> Function();
/// 含 Loading 的按钮
class LoadingButton extends StatefulWidget {
final Widget childWidget;
final Widget loadingWidget;
final AsyncVoidCallback? callback;
final VoidCallback? onLongPress;
final ButtonStyle? style;
final FocusNode? focusNode;
final bool autofocus;
final Clip clipBehavior;
const LoadingButton(this.childWidget, this.loadingWidget, this.callback,
{Key? key,
this.onLongPress,
this.style,
this.focusNode,
this.autofocus = false,
this.clipBehavior = Clip.none})
: super(key: key);
@override
_LoadingButtonState createState() => _LoadingButtonState();
}
class _LoadingButtonState extends State<LoadingButton> {
bool loading = false;
@override
Widget build(BuildContext context) {
return loading
? widget.loadingWidget
: TextButton(
child: loading ? widget.loadingWidget : widget.childWidget,
// 保留原始功能,为 null 时或 loading 时禁用按钮
onPressed: loading
? () => null
: (widget.callback == null
? () => null
: () async {
// 执行前变换
setState(() {
loading = !loading;
});
await widget.callback!();
// 等待执行完成后再变换
setState(() {
loading = !loading;
});
}),
onLongPress: widget.onLongPress,
style: widget.style,
focusNode: widget.focusNode,
autofocus: widget.autofocus,
clipBehavior: widget.clipBehavior);
}
}
使用
使用方法如下,其中 SpinKitDoubleBounce
为 flutter_spinkit 库 提供的 Loading 动画:
LoadingButton(
Row(
children: <Widget>[
Text(
AppLocalizations.of(globalKey.currentState!.context)!
.nextStep,
style: TextStyle(
color: Colors.lightBlueAccent,
fontWeight: FontWeight.w600),
),
],
),
Padding(
padding: const EdgeInsets.all(8),
child: SpinKitDoubleBounce(color: Colors.blueAccent),
),
callback,
)