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

Flutter中FutureBuilder展示订单触发无限循环问题排查

Flutter FutureBuilder无限循环问题修复与替代方案

无限循环原因分析

  1. FutureBuilder的future参数重复执行:在build方法中直接调用Provider.of<Orders>(context, listen: false).fetchandsetorders()作为FutureBuilder的future参数,每次页面重建时都会创建新的Future实例,触发新一轮数据请求。而请求完成后调用notifyListeners()会再次触发页面重建,形成循环。
  2. 不必要的页面重建触发:AppBar的backgroundColor使用Random()生成随机值,每次build都会生成新颜色,强制页面重建,进一步加剧循环。

代码修正步骤

1. 初始化Future到initState,避免重复执行

将数据请求的Future移到initState中初始化,确保只在页面初始化时执行一次:

class _OrderScreenState extends State<OrderScreen> {
  late Future<void> _fetchOrdersFuture;

  @override
  void initState() {
    super.initState();
    // 仅在页面初始化时触发一次数据请求
    _fetchOrdersFuture = Provider.of<Orders>(context, listen: false).fetchandsetorders();
  }

  @override
  Widget build(BuildContext context) {
    final orderdata = Provider.of<Orders>(context);
    print('I am buildmethod');
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blue, // 替换随机颜色,避免不必要的页面重建
        title: Text('List of orders: ' + orderdata.ordercount.toString()),
      ),
      drawer: AppDrawer(),
      body: FutureBuilder(
        future: _fetchOrdersFuture, // 使用initState中初始化的Future实例
        builder: (context, snapshot){
          if(snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else {
            if(snapshot.error != null) {
              return Text('加载失败: ${snapshot.error}');
            } else {
              return ListView.builder(
                itemCount: orderdata.ordercount,
                itemBuilder: (context, index) {
                  return OrderItemWidget(
                    order: orderdata.orders[index],
                  );
                });
            }
          }
        },
      ),
    );
  }
}

2. 完善Provider的异常处理

在fetchandsetorders方法中添加异常捕获,避免请求失败时无提示:

Future<void> fetchandsetorders() async {
    print('I am fetchandsetorders method of provider');
    try {
        final url = Uri.parse(mylink);
        final response = await http.get(url);
        // 检查响应状态码,确保请求成功
        if (response.statusCode != 200) {
            throw Exception('请求失败,状态码: ${response.statusCode}');
        }
        final List<OrderItem> loadedorders = [];
        final Map<String, dynamic> extradeddata = json.decode(response.body) as Map<String, dynamic>;
        extradeddata.forEach((orderid, orderdata) {
            loadedorders.add(
                OrderItem(
                    id: orderid,
                    products: (orderdata['products'] as List<dynamic>).map((item) {
                        return CartItem(
                            id: item['id'],
                            title: item['title'],
                            quantity: item['qty'],
                            price: item['price']);
                    }).toList(),
                    amount: orderdata['amount'],
                    date: DateTime.parse(orderdata['date']),
                ),
            );
        });
        _orders = loadedorders.reversed.toList();
        notifyListeners();
    } catch (e) {
        rethrow; // 将异常抛出,让FutureBuilder捕获并处理
    }
}

替代FutureBuilder的更优方案:结合Provider状态管理

直接在Provider中管理加载状态、错误状态和数据,通过Consumer监听状态变化,代码逻辑更清晰,避免FutureBuilder的常见陷阱:

1. 修改Provider,添加状态管理

class Orders with ChangeNotifier {
  List<OrderItem> _orders = [];
  bool _isLoading = false;
  String? _error;

  List<OrderItem> get orders => [..._orders];
  int get ordercount => _orders.length;
  bool get isLoading => _isLoading;
  String? get error => _error;

  Future<void> fetchandsetorders() async {
    _isLoading = true;
    _error = null;
    notifyListeners(); // 通知页面显示加载状态
    try {
      final url = Uri.parse(mylink);
      final response = await http.get(url);
      if (response.statusCode != 200) {
        throw Exception('请求失败');
      }
      final Map<String, dynamic> extradeddata = json.decode(response.body) as Map<String, dynamic>;
      final List<OrderItem> loadedorders = [];
      extradeddata.forEach((orderid, orderdata) {
        loadedorders.add(
          OrderItem(
            id: orderid,
            products: (orderdata['products'] as List<dynamic>).map((item) => CartItem(
              id: item['id'],
              title: item['title'],
              quantity: item['qty'],
              price: item['price']
            )).toList(),
            amount: orderdata['amount'],
            date: DateTime.parse(orderdata['date']),
          ),
        );
      });
      _orders = loadedorders.reversed.toList();
    } catch (e) {
      _error = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners(); // 通知页面加载完成,更新状态
    }
  }
}

2. 修改页面代码,使用Consumer监听状态

class _OrderScreenState extends State<OrderScreen> {
  @override
  void initState() {
    super.initState();
    // 页面初始化时触发数据加载
    Provider.of<Orders>(context, listen: false).fetchandsetorders();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blue,
        title: Consumer<Orders>(
          builder: (context, orderdata, child) => Text('List of orders: ${orderdata.ordercount}'),
        ),
      ),
      drawer: AppDrawer(),
      body: Consumer<Orders>(
        builder: (context, orderdata, child) {
          // 根据状态渲染不同UI
          if (orderdata.isLoading) {
            return const Center(child: CircularProgressIndicator());
          }
          if (orderdata.error != null) {
            return Text('加载失败: ${orderdata.error}');
          }
          return ListView.builder(
            itemCount: orderdata.ordercount,
            itemBuilder: (context, index) => OrderItemWidget(order: orderdata.orders[index]),
          );
        },
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 14:54:22