0

I'm currently playing around with the TMDB API and I'm trying to combine two maps of GET methods and display them in a single ListView (basically I'm trying to combine two movie collections into one).

Here's the code of the Home Screen of my App (wouldn't call it an app, it's more of a 'training ground').

class _HomeScreenState extends State<HomeScreen> {
  List trendingMovies = [];
  List topRatedMovies = [];
  List tvShows = [];
  List genre = [];
  List scienceFiction = [];
  List starWars = [];
  List batman = [];

  final String apiKey = 'myapikey';
  final String readAccessToken = 'myreadaccesstoken';
  int collectionId;

  @override
  void initState() {
    loadMovies();
    super.initState();
  }

  loadMovies()async{
    TMDB tmdbWithCustomLogs = TMDB(
        ApiKeys(
            apiKey,
            readAccessToken
        ),
        logConfig: ConfigLogger(
            showLogs: true,
            showErrorLogs: true
        )
    );

    Map trendingResult = await tmdbWithCustomLogs.v3.trending.getTrending();
    Map topRatedResult = await tmdbWithCustomLogs.v3.movies.getTopRated();
    Map tvShowsResult = await tmdbWithCustomLogs.v3.tv.getPouplar();
    Map genreResult = await tmdbWithCustomLogs.v3.geners.getMovieList();
    Map scienceFictionResult = await tmdbWithCustomLogs.v3.movies.getPouplar();
    Map starWarsResult = await tmdbWithCustomLogs.v3.collections.getDetails(collectionId = 10);

    //THE RELEVANT CODE
    Map batmanResult = await tmdbWithCustomLogs.v3.collections.getDetails(collectionId = 120794);
    Map batmanResult2 = await tmdbWithCustomLogs.v3.collections.getDetails(collectionId = 263);
    //THE RELEVANT CODE ENDS HERE

    setState(() {
      trendingMovies = trendingResult['results'];
      topRatedMovies = topRatedResult['results'];
      tvShows = tvShowsResult['results'];
      genre = genreResult['genres'];
      scienceFiction = scienceFictionResult['results'];
      starWars = starWarsResult['parts'];

      //THE RELEVANT CODE
      batman = [batmanResult['parts'], batmanResult2['parts']];
      //THE RELEVANT CODE ENDS HERE

    });

    print(genre);
  }

and here's the code for displaying the ListView

class BatmanMovies extends StatelessWidget {
  final List batman;

  const BatmanMovies({Key key, this.batman}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          ModifiedText(
            text: 'I\'m Vengeance, I\'m The Night, I\'m Batman',
            size: 25,
            weight: FontWeight.bold,
            color: Colors.blue,
          ),
          SizedBox(height: 10),
          Container(
            height: 210,
            child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemCount: batman.length,
                itemBuilder: (context, index) {
                  return InkWell(
                    onTap: () {
                      Navigator.push(
                          context,
                          MaterialPageRoute(builder: (context) => DetailScreen(
                            title: batman[index]['title'],
                            bannerURL: 'https://image.tmdb.org/t/p/w500'+batman[index]['backdrop_path'],
                            posterURL: 'https://image.tmdb.org/t/p/w500'+batman[index]['poster_path'],
                            synopsis: batman[index]['overview'],
                            rating: batman[index]['vote_average'].toString(),
                            releasedOn: batman[index]['release_date'],
                            id: batman[index]['id'].toString(),
                          ))
                      );
                    },
                    child: Container(
                      width: 140,
                      child: Column(
                        children: [
                          Container(
                            height: 200,
                            decoration: BoxDecoration(
                                image: DecorationImage(
                                    image: NetworkImage(
                                        'https://image.tmdb.org/t/p/w500'+batman[index]['poster_path']
                                    )
                                )
                            ),
                          ),
                        ],
                      ),
                    ),
                  );
                }
            ),
          )
        ],
      ),
    );
  }
}

but when I run it I get the following error,

======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building:
type 'String' is not a subtype of type 'int' of 'index'

When the exception was thrown, this was the stack: 
#0      BatmanMovies.build.<anonymous closure> (package:asteric/categories/batman.dart:55:88)
#1      SliverChildBuilderDelegate.build (package:flutter/src/widgets/sliver.dart:455:22)
#2      SliverMultiBoxAdaptorElement._build (package:flutter/src/widgets/sliver.dart:1201:28)
#3      SliverMultiBoxAdaptorElement.createChild.<anonymous closure> (package:flutter/src/widgets/sliver.dart:1214:55)
#4      BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2647:19)
...
====================================================================================================

is there something I should add or change into my code or is it impossible to do what I'm trying to do?

Thanks and sorry for my wording, I'm a noob to programming in general.

1 Answers1

0

A Simpler Example

Here is the class MovieDB. Think of it as the database from where you fetch the movie collection details

class MovieDB {
  Map batmanResult = {
    'id': 69,
    'parts': [
      {
        "id": 11,
        "title": "Star Wars: Episode IV - A New Hope",
      },
      {
        "id": 12,
        "title": "Star Wars: Episode V - A Greater Hope",
      }
    ]
  };

  Map batmanResult2 = {
    'id': 420,
    'parts': [
      {
        "id": 1,
        "title": "Star Wars: Episode VI - A much needed Hope",
      }
    ]
  };
}

With the help of a MovieDB instance db, you do the following :

List batman = [db.batmanResult['parts'], db.batmanResult2['parts']];

Mistake

batman is now a List of List of Map -List<List<Map>> and NOT List<Map>

So instead of batman[0]['id'] (which gives the error you mentioned), you will access the first movie like batman[0][0]['id'], which is pretty cumbersome as you will have to keep a track of number of items in each of the two lists.

Solution

Create a new list batmanMovies, iterate batman and add each movie to that list.

List batmanMovies = [];
  
batman.forEach((list) {
  for (int i = 0; i < list.length; i++) {
    batmanMovies.add(list[i]);
  }
});

Now you can access each movie correctly :

print(batmanMovies[2]['title'])

Output

Star Wars: Episode VI - Sigh

abdev
  • 597
  • 4
  • 17
  • Thank you for your answer but it gives me another error, "Unhandled Exception: NoSuchMethodError: The getter 'length' was called on null." – Rizki Azka Jul 12 '21 at 13:04
  • What does ```batman``` contain? Try printing it. (You may share it here then) @RizkiAzka – abdev Jul 12 '21 at 13:34
  • You have declared it as ```final List batman```. The error you get shows that ```batman``` did not get populated before ```batman.forEach((list)``` was called. – abdev Jul 12 '21 at 14:08
  • `[[{video: false, vote_average: 8.5, overview: Batman raises the stakes in his war on crime. With the help of Lt. Jim Gordon and District Attorney Harvey Dent, Batman sets out to dismantle... ` – Rizki Azka Jul 13 '21 at 01:15
  • I meant that it has not been initialised like ```final List batman = []``` which saves us from running into issues related to calling methods on null objects. Could you share the above in a ```gist``` so that I can have a look? – abdev Jul 13 '21 at 11:05
  • Oh, I did try initializing the list as final, but it prevented me from running the code and what's a gist? – Rizki Azka Jul 13 '21 at 19:35