【问题标题】:Nested Future in FlutterFlutter 中的嵌套 Future
【发布时间】:2026-01-27 00:30:01
【问题描述】:

我是 Flutter 的新手,(来自网络,尤其是 JS/VueJS)

我在 firebase 中有一个数据库,它有一个名为 edito 的集合,在里面,我有不同的艺术家,他们有一个特定的 ID,可以用它来调用 Deezer Api。

所以我要做的是首先调用我的数据库并获取每个艺术家的 ID,然后将此 ID 作为参数放入函数中以完成 url。

我做了2个Future函数,一个调用db,一个调用api。

但我不明白如何在构建中使用一个与其他的来获取列表视图,其中包含每个数据的 deezer api 信息。

我正在获取列表,但它陷入了无限循环。

我所有的应用程序都将在这个嵌套函数上,是否可以这样做并在我想要的任何小部件中调用它?

这是我的代码,谢谢

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';


class GetAlbum extends StatefulWidget {
  @override
  _GetAlbumState createState() => _GetAlbumState();
}

class _GetAlbumState extends State<GetAlbum> {
  
  Map mapResponse;

  Future<QuerySnapshot> getDocument() async{
    return FirebaseFirestore.instance.collection("edito").get();
  }


  Future<dynamic> fetchData(id) async{
    http.Response response;
    response = await http.get('https://api.deezer.com/album/' + id);
    if(response.statusCode == 200){
      setState(() {
        mapResponse = json.decode(response.body);
      });
    }
}

  Future<dynamic> getDocut;
  Future<dynamic> getArtist;

@override
  void initState() {
   getDocut = getDocument();
   getArtist = fetchData(null);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<QuerySnapshot>(
      future : getDocut,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
        if(!snapshot.hasData) {
          return CircularProgressIndicator();
        }else{
          return new ListView(
              children: snapshot.data.docs.map<Widget>((document){
                print(document.data().length);
                  return FutureBuilder(
                      future: fetchData(document.data()['idDeezer'].toString()),
                      builder: (context, snapshot){
                        return Container(
                          child: mapResponse==null?Container(): Text(mapResponse['title'].toString(), style: TextStyle(fontSize: 30),),
                        );
                      }
                  );
          }).toList(),
          );
        }

      },
    );
  }
}



【问题讨论】:

    标签: flutter dart flutter-layout future


    【解决方案1】:

    这是一个简化的示例,它进行了两个链接的 Future 调用,其中第二个依赖于第一个的数据,并在 FutureBuilder 中使用结果:

    import 'package:flutter/material.dart';
    
    class FutureBuilder2StatefulPage extends StatefulWidget {
      @override
      _FutureBuilder2StatefulPageState createState() => _FutureBuilder2StatefulPageState();
    }
    
    class _FutureBuilder2StatefulPageState extends State<FutureBuilder2StatefulPage> {
      Future<String> _slowData;
    
      @override
      void initState() {
        super.initState();
        _slowData = getAllSlowData(); // combined async calls into one future
      }
    
      // linked async calls
      Future<String> getAllSlowData() async {
        int id = await loadId(); // make 1st async call for id
        return loadMoreData(id: id); // use id in 2nd async call
      }
    
      Future<int> loadId() async {
        int _id = await Future.delayed(Duration(seconds: 2), () => 42);
        print('loadId() completed with: $_id'); // debugging
        return _id;
      }
    
      Future<String> loadMoreData({int id}) async {
        return await Future.delayed(Duration(seconds: 2), () => 'Retrieved data for id:$id');
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('FutureBldr Stateful'),
          ),
          body: FutureBuilder<String>(
            future: _slowData,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Center(child: Text(snapshot.data));
              }
              return Center(child: Text('Loading...'));
            },
          ),
        );
      }
    }
    

    这避免了嵌套可能容易出错的FutureBuilder

    不建议直接从FutureBuilder 调用未来的方法,因为如果重建其包含的小部件(这种情况经常发生),可能会多次调用。

    【讨论】:

    • 刚刚在上面的例子中在loadId()中添加了一个使用print来调试异步方法的例子。
    【解决方案2】:

    我尝试在第一个中添加 firebase,但我在 get AllSlowDAta 中的 id 为空,但我在 Future.delayed 中得到了正确的结果。

      // linked async calls
      Future<String> getAllSlowData() async {
        String id = await loadId(); // make 1st async call for id
        return loadMoreData(id: id); // use id in 2nd async call
      }
    
      Future<dynamic> loadId() async {
       //return await Future.delayed(Duration(seconds: 2), () => '302127');
       await FirebaseFirestore.instance.collection("edito")
            .get()
            .then((QuerySnapshot querySnapshot) {
               querySnapshot.docs.forEach((doc) {
               
                   return  doc.data()["idDeezer"];
              });
        });
      }
    
      Future<dynamic> loadMoreData({String id}) async {
        http.Response response;
        response = await http.get('https://api.deezer.com/album/' + id);
        if(response.statusCode == 200){
          setState(() {
           return json.decode(response.body);
          });
        }
      }
    

    【讨论】: