Flutter异步按钮构建插件async_button_builder的使用

Flutter 异步按钮构建插件 async_button_builder 的使用

AsyncButtonBuilder 提供了一种简单的方式来扩展任何类型的按钮,使其具有异步特性。它允许在执行异步任务的按钮上添加加载、禁用、错误和完成状态(每种状态之间有流畅的动画)。

开始使用

首先,在 pubspec.yaml 文件中添加依赖:

dependencies:
  async_button_builder: <最新版本>

然后,将 AsyncButtonBuilder 包裹在一个按钮上,并将 onPressedchild 元素传递给 builder 而不是直接传递给按钮。这两个字段是必需的。

AsyncButtonBuilder(
  child: Text('点击我'),
  onPressed: () async {
    await Future.delayed(Duration(seconds: 1));
  },
  builder: (context, child, callback, _) {
    return TextButton(
      child: child,
      onPressed: callback,
    );
  },
)

builder 中的第四个参数允许你监听加载状态。这可以用来根据加载状态来有条件地样式化按钮。此包依赖于 freezed 来创建一个密封联合,以便更好地处理可能的状态。

注意(重大变更):从 v3.0.0 开始,error 现在接受错误和堆栈跟踪作为参数。

AsyncButtonBuilder(
  child: Text('点击我'),
  loadingWidget: Text('加载中...'),
  onPressed: () async {
    await Future.delayed(Duration(seconds: 1));

    // 查看示例文件以了解如何处理超时
    throw '哎呀';
  },
  builder: (context, child, callback, buttonState) {
    final buttonColor = buttonState.when(
      idle: () => Colors.yellow[200],
      loading: () => Colors.grey,
      success: () => Colors.orangeAccent,
      error: (err, stack) => Colors.orange,
    );

    return OutlinedButton(
      child: child,
      onPressed: callback,
      style: OutlinedButton.styleFrom(
        primary: Colors.black,
        backgroundColor: buttonColor,
      ),
    );
  },
)

你还可以通过 buttonState 字段自行驱动按钮的状态:

AsyncButtonBuilder(
  buttonState: ButtonState.completing(),
  // ...
)

通知

从 v3.0.0 开始,你可以使用更高层次的父组件来处理来自按钮的通知。为什么不使用类似 runZonedGuarded 的方法呢?通知冒泡不仅处理错误,还处理按钮的状态。例如,如果你想在应用中心显示一个圆形加载器来通知用户正在发生某些事情,可以通过监听 AsyncButtonNotification 并使用 buttonState 来确定要做什么。

MaterialApp(
  home: NotificationListener<AsyncButtonNotification>(
    onNotification: (notification) {
      notification.buttonState.when(
        idle: () => // 什么也不做 -> 你也可以使用 maybeWhen
        loading: () => // 显示圆形加载小部件?
        success: () => // 显示成功提示?
        error: (_, __) => // 显示错误提示?
      );

      // 告诉通知停止冒泡
      return true;
    },
    // 这个异步按钮可以嵌套任意深度*
    child: AsyncButtonBuilder(
      duration: duration,
      errorDuration: const Duration(milliseconds: 100),
      errorWidget: const Text('错误'),
      onPressed: () async {
        throw ArgumentError();
      },
      builder: (context, child, callback, state) {
        return TextButton(onPressed: callback, child: child);
      },
      child: const Text('点击我'),
    ),
  ),
)

要禁用通知,可以在 notifications 中传递 false

自定义

async_button_builder 即使对于自定义按钮也适用。你可以定义自己的小部件来表示加载、错误和完成状态,还可以定义它们之间的过渡。这个示例有点冗长,但展示了其中的一些可能性。

AsyncButtonBuilder(
  child: Padding(
    padding: const EdgeInsets.symmetric(
      horizontal: 16.0,
      vertical: 8.0,
    ),
    child: Text(
      '点击我',
      style: TextStyle(color: Colors.white),
    ),
  ),
  loadingWidget: Padding(
    padding: const EdgeInsets.all(8.0),
    child: SizedBox(
      height: 16.0,
      width: 16.0,
      child: CircularProgressIndicator(
        valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
      ),
    ),
  ),
  successWidget: Padding(
    padding: const EdgeInsets.all(4.0),
    child: Icon(
      Icons.check,
      color: Colors.purpleAccent,
    ),
  ),
  onPressed: () async {
    await Future.delayed(Duration(seconds: 2));
  },
  loadingSwitchInCurve: Curves.bounceInOut,
  loadingTransitionBuilder: (child, animation) {
    return SlideTransition(
      position: Tween<Offset>(
        begin: Offset(0, 1.0),
        end: Offset(0, 0),
      ).animate(animation),
      child: child,
    );
  },
  builder: (context, child, callback, state) {
    return Material(
      color: state.maybeWhen(
        success: () => Colors.purple[100],
        orElse: () => Colors.blue,
      ),
      // 防止加载指示器显示在按钮下方
      clipBehavior: Clip.hardEdge,
      shape: StadiumBorder(),
      child: InkWell(
        child: child,
        onTap: callback,
      ),
    );
  },
)

示例代码

以下是一个完整的示例代码,展示如何使用 AsyncButtonBuilder 构建不同类型的按钮。

import 'package:async_button_builder/async_button_builder.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('异步按钮')),
      body: SizedBox.expand(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            const Divider(),
            const Text('文本按钮:'),
            AsyncButtonBuilder(
              child: const Text('点击我'),
              onPressed: () async {
                await Future.delayed(const Duration(seconds: 1));
              },
              builder: (context, child, callback, _) {
                return TextButton(
                  onPressed: callback,
                  child: child,
                );
              },
            ),
            const Divider(),
            const Text('凸起按钮:'),
            AsyncButtonBuilder(
              child: const Text('点击我'),
              onPressed: () async {
                await Future.delayed(const Duration(seconds: 1));
              },
              builder: (context, child, callback, _) {
                return ElevatedButton(
                  onPressed: callback,
                  child: child,
                );
              },
            ),
            const Divider(),
            const Text('自定义边框按钮(错误):'),
            AsyncButtonBuilder(
              loadingWidget: const Text('加载中...'),
              onPressed: () async {
                await Future.delayed(const Duration(seconds: 1));

                throw '哎呀';

                // 如果你想添加超时,可以使用类似的方法
                //
                // try {
                //   await Future.delayed(Duration(seconds: 1))
                //       .timeout(Duration(milliseconds: 500));
                // } on TimeoutException catch (_) {
                //   // 显示弹出窗口或其他
                //   rethrow;
                // } on Exception catch (_) {
                //   // 显示对话框或其他
                //   rethrow;
                // }
                //
                // 我们重新抛出以便让 async_button_builder 处理错误状态
              },
              builder: (context, child, callback, buttonState) {
                final buttonColor = buttonState.when(
                  idle: () => Colors.yellow[200],
                  loading: () => Colors.grey,
                  success: () => Colors.orangeAccent,
                  error: (_, __) => Colors.orange,
                );

                return OutlinedButton(
                  onPressed: callback,
                  style: OutlinedButton.styleFrom(
                    foregroundColor: Colors.black,
                    backgroundColor: buttonColor,
                  ),
                  child: child,
                );
              },
              child: const Text('点击我'),
            ),
            const Divider(),
            const Text('自定义材料按钮:'),
            const SizedBox(height: 6.0),
            AsyncButtonBuilder(
              loadingWidget: const Padding(
                padding: EdgeInsets.all(8.0),
                child: SizedBox(
                  height: 16.0,
                  width: 16.0,
                  child: CircularProgressIndicator(
                    valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                  ),
                ),
              ),
              successWidget: const Padding(
                padding: EdgeInsets.all(4.0),
                child: Icon(
                  Icons.check,
                  color: Colors.purpleAccent,
                ),
              ),
              onPressed: () async {
                await Future.delayed(const Duration(seconds: 2));
              },
              loadingSwitchInCurve: Curves.bounceInOut,
              loadingTransitionBuilder: (child, animation) {
                return SlideTransition(
                  position: Tween<Offset>(
                    begin: const Offset(0, 1.0),
                    end: const Offset(0, 0),
                  ).animate(animation),
                  child: child,
                );
              },
              builder: (context, child, callback, state) {
                return Material(
                  color: state.maybeWhen(
                    success: () => Colors.purple[100],
                    orElse: () => Colors.blue,
                  ),
                  // 防止加载指示器显示在按钮下方
                  clipBehavior: Clip.hardEdge,
                  shape: const StadiumBorder(),
                  child: InkWell(
                    onTap: callback,
                    child: child,
                  ),
                );
              },
              child: const Padding(
                padding: EdgeInsets.symmetric(
                  horizontal: 16.0,
                  vertical: 8.0,
                ),
                child: Text(
                  '点击我',
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ),
            const Divider(),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter异步按钮构建插件async_button_builder的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter异步按钮构建插件async_button_builder的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,我可以为你提供一个关于如何使用 async_button_builder 插件的示例代码。async_button_builder 是一个 Flutter 插件,用于创建异步操作的按钮,并处理加载、成功和失败状态。

首先,确保你已经在 pubspec.yaml 文件中添加了 async_button_builder 依赖:

dependencies:
  flutter:
    sdk: flutter
  async_button_builder: ^x.y.z  # 替换为最新版本号

然后运行 flutter pub get 以获取依赖。

以下是一个使用 async_button_builder 的完整示例代码:

import 'package:flutter/material.dart';
import 'package:async_button_builder/async_button_builder.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Async Button Builder Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 模拟异步操作的函数
  Future<void> _simulateAsyncOperation() async {
    await Future.delayed(Duration(seconds: 2)); // 模拟延迟
    // 你可以在这里添加你的实际异步操作,比如网络请求
    // 如果操作成功,返回null或者一个成功的结果
    // 如果操作失败,抛出一个异常
    return null; // 操作成功
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Async Button Builder Demo'),
      ),
      body: Center(
        child: AsyncButtonBuilder<void>(
          future: () => _simulateAsyncOperation(), // 异步函数
          onLoading: (context) {
            return CircularProgressIndicator(); // 加载中的状态
          },
          onSuccess: (context, result) {
            return Text('操作成功!'); // 成功时的状态
          },
          onError: (context, error, stackTrace) {
            return Text('操作失败: $error'); // 失败时的状态
          },
          builder: (context) {
            return ElevatedButton(
              onPressed: () {
                // 触发异步操作
                context.read<AsyncButtonBuilderController>().trigger();
              },
              child: Text('点击我'),
            );
          },
        ),
      ),
    );
  }
}

在这个示例中:

  1. AsyncButtonBuilder 组件用于构建一个具有异步操作的按钮。
  2. future 参数是一个返回 Future 的函数,这里我们传入 _simulateAsyncOperation 函数来模拟异步操作。
  3. onLoading 参数返回一个 Widget,表示按钮在加载状态时的显示内容。
  4. onSuccess 参数返回一个 Widget,表示按钮在异步操作成功时的显示内容。
  5. onError 参数返回一个 Widget,表示按钮在异步操作失败时的显示内容。
  6. builder 参数返回一个 Widget,用于构建按钮本身。在按钮的 onPressed 回调中,我们调用 context.read<AsyncButtonBuilderController>().trigger() 来触发异步操作。

这个示例展示了如何使用 async_button_builder 插件来创建一个异步按钮,并处理其不同状态。希望这对你有所帮助!

回到顶部