Home > Software design >  flutter FutureBuilder loop snapshot arrays
flutter FutureBuilder loop snapshot arrays

Time:06-24

I am getting API data and it returns data of 5 arrays like this:

print('one: ${jsonResponse['one']}');
print('two: ${jsonResponse['two']}');
print('three: ${jsonResponse['three']}');
print('four: ${jsonResponse['four']}');
print('five: ${jsonResponse['five']}');

Each line is an array of data and all comes from single API URL.

My question is: how to divide this arrays in order to show them as separate block in my screen?

Code

This is what I have right now:

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  late Future myCards;

  @override
  void initState() {
    super.initState();
    myCards = getHome();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(12.0),
            child: FutureBuilder(
              future: myCards,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  // require to separate arrays here
                  return Text('Hello!');
                } else if (snapshot.hasError) {
                  return Text('${snapshot.error}');
                }
                return const CircularProgressIndicator();
              }
            ),
          ),
        ),
      ),
    );
  }

  getHome() async {
    var response = await http.get(
      Uri.parse('https://example.com/api/home'),
      headers: {
        HttpHeaders.acceptHeader: 'application/json'
      },
    );
    if (response.statusCode == 200) {
      var jsonResponse = convert.jsonDecode(response.body) as Map<
          String,
          dynamic>;
      if (kDebugMode) {
        print('one: ${jsonResponse['one']}'); // returning array of data
        print('two: ${jsonResponse['two']}'); // returning array of data
        print('three: ${jsonResponse['three']}'); // returning array of data
        print('four: ${jsonResponse['four']}'); // returning array of data
        print('five: ${jsonResponse['five']}'); // returning array of data
      }
      return jsonResponse;
    } else {
      //
    }
  }
}

PS: Please feel free to suggest better approach if you have any in your mind, I'm open to all solutions.

Update

I've managed to get my data separately in my screen with code below

child: FutureBuilder(
  future: myCards,
  builder: (context, AsyncSnapshot snapshot) {
    if (snapshot.hasData) {
      return Column(
        children: [
          //one
          ListView.builder(
                  scrollDirection: Axis.horizontal,
                  shrinkWrap: true,
                  itemCount: snapshot.data['one'].length,
                  itemBuilder: (BuildContext context, int index) {
                      return Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(snapshot.data['one'][index]['heading']),
                        ],
                      );
                  }
              ),
          //two
          ListView.builder(
              shrinkWrap: true,
              itemCount: snapshot.data['two'].length,
              itemBuilder: (BuildContext context, int index) {
                return Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(snapshot.data['two'][index]['name']),
                  ],
                );
              }
          ),
        ],
      );
    } else if (snapshot.hasError) {
      return Center(child: Text('${snapshot.error}'));
    }
    // By default, show a loading spinner.
    return const Center(child: CircularProgressIndicator());
  }
),

But I'm facing following error:

RenderBox was not laid out: RenderRepaintBoundary#bc93a relayoutBoundary=up14 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1979 pos 12: 'hasSize'

CodePudding user response:

if the scrollDirection of each list is vercital then this should solve the problem.

  ListView.builder(
           shrinkWrap: true,
           physics: const NeverScrollableScrollPhysics(),                  
           itemCount: snapshot.data['one'].length,
              itemBuilder: (BuildContext context, int index) {
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(snapshot.data['one'][index]['heading']),
                    ],
                  );
              }
          ),
      //two
      ListView.builder(
          shrinkWrap: true,
          itemCount: snapshot.data['two'].length,
          itemBuilder: (BuildContext context, int index) {
            return Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(snapshot.data['two'][index]['name']),
              ],
            );
          }
      ),

But if one is horizontal you have to give it height.

example-->

                 SizedBox(
          height: 200,
          child:           ListView.builder(
              scrollDirection: Axis.horizontal,
              shrinkWrap: true,
              itemCount: snapshot.data['one'].length,
              itemBuilder: (BuildContext context, int index) {
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(snapshot.data['one'][index]['heading']),
                    ],
                  );
              }
          )),
      //two
      ListView.builder(
          shrinkWrap: true,
          itemCount: snapshot.data['two'].length,
          itemBuilder: (BuildContext context, int index) {
            return Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(snapshot.data['two'][index]['name']),
              ],
            );
          }
      )

CodePudding user response:

You could have sth like:

            child: FutureBuilder<Map<String, dynamic>>(
              future: myCards,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  // Get your map from the snapshot
                  // If possible change "dynamic" to the specific type
                  final Map<String, dynamic> responseMap = snapshot.data!;
                  final List<Widget> children = [];

                  for(final key in responseMap.keys) {
                    // Considering the "dynamic" is a List of Strings
                    final List<String> response = responseMap[key];
                    // Iterate through each response
                    for(final item in response) {
                      // Build the widget for each item
                      children.add(Text(item));
                    } 
                  }
                  // Use a Widget that can display a list of Widgets,
                  // such as ListView, PageView, Column
                  return Column(children:children);

                } else if (snapshot.hasError) {
                  return Text('${snapshot.error}');
                }
                return const CircularProgressIndicator();
              }
            ),
  • Related