Flutter 支持 Loading 的 Button

介绍

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);
  }
}

使用

使用方法如下,其中 SpinKitDoubleBounceflutter_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,
)
原文地址:https://www.cnblogs.com/seliote/p/15244906.html