async_button_builder

一个构建器,可在执行异步任务的按钮之上添加加载、禁用、出错和完成状态。它可以与几乎任何按钮一起使用,甚至可以与自定义 Material 按钮一起使用。它还通过AnimatedSize结合AnimatedSwitcher来实现状态之间的流畅动画,从而可以定义自己的过渡效果。

入门

包含此包

  async_button_builder: <latest_version>

将构建器包装在按钮周围,将 onPressed 和 child 元素直接传递给构建器,而不是按钮本身。这两者是唯一必需的字段。

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

ezgif-7-61c436edaec2

构建器的第四个值允许您监听加载状态。这可用于有条件地设置按钮的样式。此包依赖于freezed,以便创建已密封的联合以更好地处理各种可能的状态。

AsyncButtonBuilder(
  child: Text('Click Me'),
  loadingWidget: Text('Loading...'),
  onPressed: () async {
    await Future.delayed(Duration(seconds: 1));

    throw 'shucks';
  },
  builder: (context, child, callback, buttonState) {
    final buttonColor = buttonState.when(
      idle: () => Colors.yellow[200],
      loading: () => Colors.grey,
      success: () => Colors.orangeAccent,
      error: () => Colors.orange,
    );

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

ezgif-7-a971c6afaabf

您还可以使用buttonState字段自行驱动按钮的状态。

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

async_button_builder甚至适用于自定义按钮。您可以为加载、错误和完成状态定义自己的小部件,以及定义它们之间的过渡。这个例子有点冗长,但展示了其中的一些可能性。

AsyncButtonBuilder(
  child: Padding(
    // Value keys are important as otherwise our custom transitions
    // will have no way to differentiate between children.
    key: ValueKey('foo'),
    padding: const EdgeInsets.symmetric(
      horizontal: 16.0,
      vertical: 8.0,
    ),
    child: Text(
      'Click Me',
      style: TextStyle(color: Colors.white),
    ),
  ),
  loadingWidget: Padding(
    key: ValueKey('bar'),
    padding: const EdgeInsets.all(8.0),
    child: SizedBox(
      height: 16.0,
      width: 16.0,
      child: CircularProgressIndicator(
        valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
      ),
    ),
  ),
  successWidget: Padding(
    key: ValueKey('foobar'),
    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,
      ),
      // This prevents the loading indicator showing below the
      // button
      clipBehavior: Clip.hardEdge,
      shape: StadiumBorder(),
      child: InkWell(
        child: child,
        onTap: callback,
      ),
    );
  },
),

ezgif-7-4088c909ba83

GitHub

https://github.com/Nolence/async_button_builder