Flutter如何创建内部带Spinner、可隔离setState调用的可复用按钮
Flutter 加载状态按钮封装方案
完全可以把Spinner切换逻辑完全封装在组件内部,核心思路是让按钮接收异步回调函数,内部自行管理加载状态,无需外部传入控制参数也不用外部触发状态更新。
具体实现逻辑
- 组件为StatefulWidget,内部State中定义私有布尔变量
_isLoading,默认值为false,作为控制Spinner显示的唯一内部状态 - 组件接收的
onPressed回调要求为返回Future的异步函数,匹配网络请求这类异步操作的场景 - 点击事件触发时做如下处理:
- 若当前已经处于加载状态,直接拦截点击避免重复触发
- 调用内部
setState将_isLoading置为true,按钮切换为Spinner显示 - 用
try-finally包裹异步回调的执行逻辑,无论回调执行成功还是抛出异常,最终都在finally块中调用setState将_isLoading重置为false,恢复按钮文字显示
- 构建布局时根据
_isLoading的状态动态切换显示内容:加载时显示Spinner,否则显示按钮文字
参考实现代码
import 'package:flutter/material.dart'; class LoadingButton extends StatefulWidget { const LoadingButton({ super.key, required this.text, required this.onPressed, this.style, this.textStyle, this.disabled = false, }); final String text; final Future<void> Function() onPressed; final ButtonStyle? style; final TextStyle? textStyle; final bool disabled; @override State<LoadingButton> createState() => _LoadingButtonState(); } class _LoadingButtonState extends State<LoadingButton> { bool _isLoading = false; Future<void> _handleTap() async { if (_isLoading || widget.disabled) return; setState(() { _isLoading = true; }); try { await widget.onPressed(); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override Widget build(BuildContext context) { return ElevatedButton( style: widget.style, onPressed: _handleTap, child: _isLoading ? const CircularProgressIndicator(color: Colors.white) : Text( widget.text, style: widget.textStyle, ), ); } }
使用示例
外部使用时无需管理任何加载状态,直接传入异步的请求逻辑即可:
LoadingButton( text: "提交数据", onPressed: () async { // 这里直接写你的网络请求逻辑即可,不用手动控制loading await Future.delayed(const Duration(seconds: 2)); // 请求完成后按钮会自动恢复文字显示 }, )
可选扩展
如果有特殊场景需要外部强制控制加载状态,可以新增一个可选的bool? showLoading参数,构建时优先使用外部传入的参数值,内部_isLoading作为降级选项,兼顾灵活性和通用性。
内容的提问来源于stack exchange,提问作者lxknvlk
相关产品推荐
相关产品推荐

