You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter重构ConnectionState相关switch case实现代码复用

Flutter FutureBuilder 状态逻辑复用重构方案

核心思路很简单:把所有场景通用的连接状态判断、加载/空态/无连接/报错的兜底UI逻辑抽成带泛型的公共组件,只把「拿到有效数据后渲染业务UI」的差异部分作为回调参数暴露出去,后续任何页面要用FutureBuilder,直接传对应异步请求和数据渲染逻辑就行,不用重复写大段switch分支。

第一步:封装通用泛型组件

直接写一个支持任意数据类型的GenericFutureBuilder,公共逻辑全内置,支持自定义不同状态下的占位组件:

import 'package:flutter/material.dart';

class GenericFutureBuilder<T> extends StatelessWidget {
  const GenericFutureBuilder({
    super.key,
    required this.future,
    required this.dataBuilder,
    this.loadingWidget,
    this.noneWidget,
    this.emptyWidget,
    this.errorWidget,
  });

  /// 要监听的异步请求
  final Future<T>? future;
  /// 拿到有效非空数据后的渲染逻辑,由业务页面传入
  final Widget Function(BuildContext context, T validData) dataBuilder;
  /// 以下为可选自定义状态组件,不传则使用默认样式
  final Widget? loadingWidget;
  final Widget? noneWidget;
  final Widget? emptyWidget;
  final Widget? errorWidget;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<T>(
      future: future,
      builder: (context, snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
            return noneWidget ?? const Text("当前无网络连接");
          case ConnectionState.active:
          case ConnectionState.waiting:
            return loadingWidget ?? const Center(child: CircularProgressIndicator());
          case ConnectionState.done:
            // 补全原代码缺失的错误处理
            if (snapshot.hasError) {
              return errorWidget ?? Center(
                child: Text("加载失败:${snapshot.error?.toString() ?? '未知错误'}"),
              );
            }
            if (snapshot.hasData) {
              final data = snapshot.data;
              // 自动处理列表类型的空数据场景
              if (data is List && data.isEmpty) {
                return emptyWidget ?? const Center(child: Text("暂无有效数据"));
              }
              // 数据校验通过,交给业务层渲染对应UI
              return dataBuilder(context, data as T);
            }
            // 无数据兜底
            return emptyWidget ?? const Center(child: Text("暂无有效数据"));
        }
      },
    );
  }
}

第二步:业务页面替换原有代码

原来写大段switch的地方直接用封装好的组件,只需要关心拿到数据之后怎么渲染,不用重复写状态判断:

// 商品列表示例
GenericFutureBuilder<List<ProductsList>>(
  future: _fetchProductsList,
  // 这里只写商品列表的渲染逻辑即可
  dataBuilder: (context, productList) {
    return ListView.builder(
      primary: false,
      shrinkWrap: true,
      scrollDirection: Axis.horizontal,
      itemCount: productList.length,
      itemBuilder: (BuildContext context,int index){
        return ProductCard(
          img: productList[index].image,
          title: productList[index].title,
          price: productList[index].price,
          rating: productList[index].rating.rate,
          discount: 50,
          onTap: () {}
        );
      }
    );
  },
)

灵活扩展说明

  • 任意类型的异步数据都能复用:不管是返回用户信息、配置项这类单对象,还是订单列表、评论列表这类数组,只要指定对应泛型,传入future和渲染逻辑即可
  • 支持单页面自定义状态样式:如果某个页面需要定制加载动画、空态文案、报错提示,直接给组件传对应的可选参数就行,不会影响其他页面的默认样式,比如:
GenericFutureBuilder<UserInfo>(
  future: _fetchUserInfo,
  // 自定义加载动画
  loadingWidget: const Center(child: CircularProgressIndicator(color: Colors.blue, strokeWidth: 2,)),
  // 自定义空态
  emptyWidget: const Center(child: Text("用户信息不存在")),
  dataBuilder: (context, userInfo) => UserProfileCard(user: userInfo),
)

注:原代码存在一个逻辑疏漏,没有处理异步请求报错的场景,封装时补全了这部分逻辑,避免请求出错后页面一直卡在加载状态。

内容的提问来源于stack exchange,提问作者user10033434

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 17:21:22