Search code examples
jsonapiflutterflutter-futurebuilderflutter-http

Iterating over an array of objects in JSON with Flutter FutureBuilder


I am having trouble trying to iterate over a JSON array of objects from a remote URL using Flutter's FutureBuilder.

My goal is to:

  • Fetch JSON data from an API
  • Output the data into a 2 column gridview layout

The JSON data is an array of objects(or a List of Maps in dart), the objects have simple string data.

I know that I need to build a future to fetch the data from the API and decode the JSON, then I need to create a FutureBuilder to output the List data into my Gridview Builder. That is what I have tried to do in my code below.

import 'dart:convert';
import 'dart:async';
import 'dart:core';

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


class HomeSection extends StatefulWidget {
  @override
  _HomeSectionState createState() => _HomeSectionState();
}

class _HomeSectionState extends State<HomeSection> {
  @override
  void initState() {
    super.initState();
  }

  Future<List<dynamic>> fetchSectionData() async {
    String dataUrl =
        'https://www.thisisthejsonapilink.co.uk/fetch-data';
    var response = await http.get(Uri.parse(dataUrl));
    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception('Failed to get the data');
    }
  }

  @override
  Widget build(BuildContext context) {

    return FutureBuilder(
      future: fetchSectionData,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return GridView.builder(
            itemCount: 12,
            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 300,
              crossAxisSpacing: 16.0,
              mainAxisSpacing: 18.0,
              childAspectRatio: MediaQuery.of(context).size.height / 930,
            ),
            itemBuilder: (BuildContext context, int index) => GestureDetector(
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => OtherScreen()),
                );
              },
              child: Column(
                children: [
                  Container(
                    margin: const EdgeInsets.only(bottom: 12.0),
                    height: 144,
                  ),
                  Text(
                    snapshot.data[index].title,
                  ),
                  Text(
                    snapshot.data[index].duration + ' - ' + snapshot.data[index].type,
                  ),
                ],
              ),
            ),
          );
        } else {
          return Center(
            child: CircularProgressIndicator(
              color: Colors.black,
            ),
          );
        }
      },
    );
  }
}

My JSON that returns from the API link is structured like this:

[
  {
    "Title": "Audio 1",
    "Duration": "5 min",
    "type": "audio",
    "bodyText": "lorem ipsum blah blah audio",
    "url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
  },
  {
    "Title": "Video 1",
    "Duration": "5 min",
    "type": "video",
    "bodyText": "lorem ipsum blah blah video",
    "url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
  },
  {
    "Title": "Audio 2",
    "Duration": "5 min",
    "type": "audio",
    "bodyText": "lorem ipsum blah blah audio",
    "url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
  },
  {
    "Title": "Video 2",
    "Duration": "5 min",
    "type": "video",
    "bodyText": "lorem ipsum blah blah video",
    "url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
  },
  {
    "Title": "Audio 3",
    "Duration": "5 min",
    "type": "audio",
    "bodyText": "lorem ipsum blah blah audio",
    "url": "https://www.silvermansound.com/music/dirty-gertie.mp3"
  },
  {
    "Title": "Video 3",
    "Duration": "5 min",
    "type": "video",
    "bodyText": "lorem ipsum blah blah video",
    "url": "https://assets.mixkit.co/videos/preview/mixkit-woman-wearing-a-mask-while-running-40158-large.mp4"
  },
]

This is the error I am getting in my VS code debug console:

The argument type 'Future<List<dynamic>> Function()' can't be assigned to the parameter type 'Future<Object?>?'.

The red line appears right where I try to define my future in the FutureBuilder here under fetchSectionData:

return FutureBuilder(
      future: fetchSectionData,
      builder: (context, snapshot) {

I am not sure what this error means. Could somebody please explain it? The Future is definitely returning a <List>, but how do I get this list into the futurebuilder so that I can iterate over it and output the data into the gridview?

I am fairly new to flutter and come from a javascript web background, usually in javascript you can just loop over an array and output it that way. I'm tempted to do it that way here but I know that wouldn't be right.

When I looked up the Flutter documentation on how to fetch data from the internet it mentioned that I have to convert the response into a custom dart object, but nothing I try seems to work.

Appreciate your help!


Solution

  • Try this !

    return FutureBuilder<List<dynamic>>(
          future: fetchSectionData,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              return GridView.builder(
                itemCount: 12,
                gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                  maxCrossAxisExtent: 300,
                  crossAxisSpacing: 16.0,
                  mainAxisSpacing: 18.0,
                  childAspectRatio: MediaQuery.of(context).size.height / 930,
                ),
                itemBuilder: (BuildContext context, int index) => GestureDetector(
                  onTap: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(builder: (context) => OtherScreen()),
                    );
                  },
                  child: Column(
                    children: [
                      Container(
                        margin: const EdgeInsets.only(bottom: 12.0),
                        height: 144,
                      ),
                      Text(
                        '${snapshot.data[index]['Title']}',
                      ),
                      Text(
                        '${snapshot.data[index]['Duration'] + ' - ' + snapshot.data[index]['type']}',
                      ),
                    ],
                  ),
                ),
              );
            } else {
              return Center(
                child: CircularProgressIndicator(
                  color: Colors.black,
                ),
              );
            }
          },
        );