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

Flutter FutureBuilder snapshot为空:JSON数据无法渲染至ListView

问题分析与解决

你的问题核心在于_getBans函数没有正确返回包含数据的Future,导致FutureBuilder始终拿到空的snapshot。

原因详解

看你的_getBans实现:

static Future _getBans() async { 
  Storage.getLoggedToken().then((token) async { 
    // ... 请求并处理数据的逻辑
  }); 
}

虽然你在then回调里正确获取并返回了bans列表,但你没有把这个then链的结果返回给外层的async函数。这意味着_getBans实际上返回的是一个立即完成的Future<void>,结果为null——所以FutureBuilder的snapshot.data自然一直是null,即使后续请求成功拿到了数据也无法传递过来。

修复方案

有两种简单的方式修复这个问题:

方案1:用await替代then(推荐,代码更简洁易读)

彻底去掉回调嵌套,改用await来处理异步操作,确保函数正确返回Future<List<Ban>>:

static Future<List<Ban>?> _getBans() async { 
  // 用await获取token,替代then回调
  final token = await Storage.getLoggedToken();
  
  var body = { "token": token }; 
  final response = await http.post(
    '${URLS.BASE_URL}/punishments.php', 
    headers: ApiService.header, 
    body: json.encode(body)
  ); 

  if (response.statusCode == 200) { 
    List<Ban> bans = []; 
    var jsonData = json.decode(response.body)["bans"]; 
    for(var b in jsonData){ 
      Ban ban = Ban(b["player"], b["reason"], int.parse(b["end"]), b["by"]); 
      bans.add(ban); 
    } 
    print(response.body); 
    print(bans.length); 
    return bans; 
  } else { 
    return null; 
  }
}

方案2:返回then链的结果

如果你坚持使用then,需要在调用Storage.getLoggedToken().then(...)前面加上return,让外层函数把这个异步链的结果返回出去:

static Future<List<Ban>?> _getBans() async { 
  // 加上return,将then链的结果作为函数的返回值
  return Storage.getLoggedToken().then((token) async { 
    var body = { "token": token }; 
    final response = await http.post(
      '${URLS.BASE_URL}/punishments.php', 
      headers: ApiService.header, 
      body: json.encode(body)
    ); 

    if (response.statusCode == 200) { 
      List<Ban> bans = []; 
      var jsonData = json.decode(response.body)["bans"]; 
      for(var b in jsonData){ 
        Ban ban = Ban(b["player"], b["reason"], int.parse(b["end"]), b["by"]); 
        bans.add(ban); 
      } 
      print(response.body); 
      print(bans.length); 
      return bans; 
    } else { 
      return null; 
    } 
  }); 
}

额外优化建议

除了修复核心问题,还有两个最佳实践可以提升用户体验和代码健壮性:

  1. 避免在build方法中直接调用Future
    build方法会频繁触发(比如屏幕旋转、父组件重建),直接在这里调用_getBans()会导致重复发起网络请求。建议在initState中初始化Future并存储:

    class _YourPageState extends State<YourPage> {
      Future<List<Ban>?>? _bansFuture;
    
      @override
      void initState() {
        super.initState();
        _bansFuture = _getBans();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          // ... 其他代码
          body: FutureBuilder(
            future: _bansFuture, // 使用存储的Future
            builder: (context, snapshot) {
              // ... builder逻辑
            }
          )
        );
      }
    }
    
  2. 完善snapshot状态处理
    当前你只判断了snapshot.data == null,但初始状态下snapshot.data本来就是null,会导致页面一开始就显示“无可用数据”。建议根据connectionState和错误状态做更细致的处理:

    builder: (BuildContext context, AsyncSnapshot<List<Ban>?> snapshot) { 
      if (snapshot.connectionState == ConnectionState.waiting) {
        // 加载中显示指示器
        return const Center(child: CircularProgressIndicator());
      } else if (snapshot.hasError) {
        // 处理加载错误
        return Center(child: Text('加载失败: ${snapshot.error}'));
      } else if (!snapshot.hasData || snapshot.data!.isEmpty) { 
        return const Center(child: Text('Keine aktiven Ban vorhanden')); 
      } else { 
        return ListView.builder( 
          itemCount: snapshot.data!.length, 
          itemBuilder: (context, index) { 
            return ListTile( 
              title: Text(snapshot.data![index].player), 
            ); 
          }, 
        ); 
      } 
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:17:40