搜索分类、插件与代码仓

async_button 封面
插件详情v2.0.0-beta.1+2

async_button

用于异步 onPressed 函数的自定义按钮。支持在 [空闲]、[加载中]、[成功] 和 [失败] 按钮状态之间无缝切换动画。

AndroidiOSWebmacOSWindowsLinux
近 30 天下载
13
喜欢数
33
Pub 分数
150

安装插件

flutter pub add async_button

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

一个 Flutter 包,提供可自定义的按钮,专为异步 onPressed 操作设计。它能无缝处理空闲、加载、成功和失败状态之间的状态转换,并带有平滑的动画效果。非常适合表单、登录流程以及任何需要在异步执行期间提供反馈的操作。该按钮会动态适应操作的生命周期,通过清晰地指示进度和结果来提升用户体验。

适合场景

  • 表单提交
  • 身份验证流程
  • 数据保存操作
  • API 请求触发
  • 用户操作反馈

主要特点

  • 具有空闲/加载/成功/失败状态的有状态按钮
  • 内置动画过渡
  • 可自定义的外观和行为
  • 自动状态管理
  • 支持错误处理和成功指示

适合对象

  • 开发交互式 UI 的开发者
  • 频繁执行异步操作的应用程序
  • 注重用户体验一致性的团队
  • 需要响应式表单控件的项目
  • 具有复杂用户工作流的 Flutter 应用

使用建议

  • 需要理解 async/await 模式
  • 复杂设计可能需要额外的样式
  • 仅限按钮特定用例
  • 不适用于非交互元素
  • 依赖于平台特定的渲染行为

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

Async Button

Buttons get boring when there is an asynchronous onTap function involved. Users don't get to see if or when the onTap function has finished execution, or if it has even finished sucessfully. To put it simply, this project aims to make them a little un-boring.

What is this?

This project provides you with the same buttons that you are used to, just with some modifications under the hood. You can manipulate the button state based on whether the onTap function is idle, loading or has finished loading(successfully or unsuccessfully).

How to use it?

This project provides you with 3 types of buttons. Let's list them below with an example.

AsyncElevatedBtn

Just like ElevatedButton, but for async onPressed

AsyncBtnStatesController btnStateController = AsyncBtnStatesController();

AsyncElevatedBtn(
  asyncBtnStatesController: btnStateController,
  onPressed: () async {
    btnStateController.update(AsyncBtnState.loading);
    try {
      // Await your api call here
      await Future.delayed(const Duration(seconds: 2));
      btnStateController.update(AsyncBtnState.success);
    } catch (e) {
      btnStateController.update(AsyncBtnState.failure);
    }
  },
  // * It is NOT mandatory to define [loadingStyle, successStyle, failureStyle]
  // * if you don't need it.

  // This should ideally be the button's loading state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  loadingStyle: AsyncBtnStateStyle(
    style: ElevatedButton.styleFrom(
      backgroundColor: Colors.amber,
    ),
    widget: const SizedBox.square(
      dimension: 24,
      child: CircularProgressIndicator(
        color: Colors.white,
      ),
    ),
  ),

  // This should ideally be the button's success state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  successStyle: AsyncBtnStateStyle(
    style: ElevatedButton.styleFrom(
      backgroundColor: Colors.green,
      foregroundColor: Colors.white,
    ),
    widget: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.check),
        SizedBox(width: 4),
        Text('Success!')
      ],
    ),
  ),

  // This should ideally be the button's failure state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  failureStyle: AsyncBtnStateStyle(
    style: ElevatedButton.styleFrom(
      backgroundColor: Colors.red,
      foregroundColor: Colors.white,
    ),
    widget: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.error),
        SizedBox(width: 4),
        Text('Error!'),
      ],
    ),
  ),
  child: const Text('Execute'),
);

Too much? We have created a custom constructor that uses the exact values for loadingStyle, successStyle and failureStyle as above

AsyncBtnStatesController btnStateController = AsyncBtnStatesController();

AsyncElevatedBtn.withDefaultStyles(
  asyncBtnStatesController: btnStateController,
  onPressed: () async {
    btnStateController.update(AsyncBtnState.loading);
    try {
      // Await your api call here
      await Future.delayed(const Duration(seconds: 2));
      btnStateController.update(AsyncBtnState.success);
    } catch (e) {
      btnStateController.update(AsyncBtnState.failure);
    }
  },
  child: const Text('Execute'),
);

AsyncTextBtn

And this is our version of async TextButton

AsyncBtnStatesController btnStateController = AsyncBtnStatesController();

AsyncTextBtn(
  asyncBtnStatesController: btnStateController,
  onPressed: () async {
    btnStateController.update(AsyncBtnState.loading);
    try {
      // Await your api call here
      await Future.delayed(const Duration(seconds: 2));
      btnStateController.update(AsyncBtnState.success);
    } catch (e) {
      btnStateController.update(AsyncBtnState.failure);
    }
  },
  // * It is NOT mandatory to define [loadingStyle, successStyle, failureStyle]
  // * if you don't need it.

  // This should ideally be the button's loading state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  loadingStyle: AsyncBtnStateStyle(
    style: TextButton.styleFrom(
      foregroundColor: Colors.amber,
    ),
    widget: const SizedBox.square(
      dimension: 24,
      child: CircularProgressIndicator(
        color: Colors.amber,
      ),
    ),
  ),


  // This should ideally be the button's success state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  successStyle: AsyncBtnStateStyle(
    style: TextButton.styleFrom(
      foregroundColor: Colors.green,
    ),
    widget: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.check),
        SizedBox(width: 4),
        Text('Success!')
      ],
    ),
  ),

  // This should ideally be the button's failure state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  failureStyle: AsyncBtnStateStyle(
    style: TextButton.styleFrom(
      foregroundColor: Colors.red,
    ),
    widget: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.error),
        SizedBox(width: 4),
        Text('Error!')
      ],
    ),
  ),
  child: const Text('Execute'),
);

Here again, you can use the following constructor instead

AsyncBtnStatesController btnStateController = AsyncBtnStatesController();

AsyncTextBtn.withDefaultStyles(
  asyncBtnStatesController: btnStateController,
  onPressed: () async {
    btnStateController.update(AsyncBtnState.loading);
    try {
      // Await your api call here
      await Future.delayed(const Duration(seconds: 2));
      btnStateController.update(AsyncBtnState.success);
    } catch (e) {
      btnStateController.update(AsyncBtnState.failure);
    }
  },
  child: const Text('Execute'),
);

AsyncOutlinedBtn

Similarly, here's one for async OutlinedButton

AsyncBtnStatesController btnStateController = AsyncBtnStatesController();

AsyncOutlinedBtn(
  asyncBtnStatesController: btnStateController,
  onPressed: () async {
    btnStateController.update(AsyncBtnState.loading);
    try {
      // Await your api call here
      await Future.delayed(const Duration(seconds: 2));
      btnStateController.update(AsyncBtnState.success);
    } catch (e) {
      btnStateController.update(AsyncBtnState.failure);
    }
  },
  // * It is NOT mandatory to define [loadingStyle, successStyle, failureStyle]
  // * if you don't need it.

  // This should ideally be the button's loading state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  loadingStyle: AsyncBtnStateStyle(
    style: OutlinedButton.styleFrom(
      foregroundColor: Colors.amber,
    ),
    widget: const SizedBox.square(
      dimension: 24,
      child: CircularProgressIndicator(
        color: Colors.amber,
      ),
    ),
  ),

  // This should ideally be the button's success state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  successStyle: AsyncBtnStateStyle(
    style: OutlinedButton.styleFrom(
      foregroundColor: Colors.green,
    ),
    widget: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.check),
        SizedBox(width: 4),
        Text('Success!')
      ],
    ),
  ),

  // This should ideally be the button's failure state indicator.
  // If [style] or [widget] properties are not defined, we consider the button's
  // corresponding default [style] and [child] property
  failureStyle: AsyncBtnStateStyle(
    style: OutlinedButton.styleFrom(
      foregroundColor: Colors.red,
    ),
    widget: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.error),
        SizedBox(width: 4),
        Text('Error!')
      ],
    ),
  ),
  child: const Text('Execute'),
);

Here as well, there is an alternative constructor

AsyncBtnStatesController btnStateController = AsyncBtnStatesController();

AsyncOutlinedBtn.withDefaultStyles(
  asyncBtnStatesController: btnStateController,
  onPressed: () async {
    btnStateController.update(AsyncBtnState.loading);
    try {
      // Await your api call here
      await Future.delayed(const Duration(seconds: 2));
      btnStateController.update(AsyncBtnState.success);
    } catch (e) {
      btnStateController.update(AsyncBtnState.failure);
    }
  },
  child: const Text('Execute'),
);

More Intuitive Examples

All buttons also supports these arguments - styleBuilder, loadingStyleBuilder, successStyleBuilder and failureStyleBuilder which creates the custom state styles at runtime. This essentially mean that one can create unlimited number of custom state widgets and styles based on different conditions.

There are also scopes for fallback styles and widgets if none of the necessary conditions are met.

Login Button

Click here for Code!

Download Button

Click here for Code!

Similar Projects

If this library doesn't cater to your requirements. Here are the some similar projects you can explore:

  • async_button_builder

  • easy_loading_button

  • animated_loading_button

Contribute

The creator always appreciates a helping hand, so don't hesitate before creating a pull request or reporting a bug/issue.

Connect with the Creator

The creator does aspire to help grow the flutter community. So if you have a question related to this project, your project, or anything flutter, connect with him over the following links. Don't worry, it's free!😉


Made with ❤️

所属分类

源码仓库

使用此插件的项目

暂无相关数据。