Home > Net >  Flutter type 'Null' is not a subtype of type 'int', trying to get complicated JS
Flutter type 'Null' is not a subtype of type 'int', trying to get complicated JS

Time:11-01

This is my json here: https://my-json-server.typicode.com/fluttirci/testJson/db

This code only works if there is an only one json object however, with this employees JSON, it doesn't work. Flutter documentation isn't very clear about this subject. They only work on one line jsons. What I wanna do is, I wanna get all that data into my phone screen. If I get it, I will show them on a table or a grid. But yet it doesn't won't work. It says type 'Null' is not a subtype of type 'int' . Here is my code:

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

Future<Album> fetchAlbum() async {
  final response = await http.get(
      Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));

  print(response);
  Map<String, dynamic> userMap = jsonDecode(response.body);
  if (response.statusCode == 200) {
    return Album.fromJson(userMap); //testing
  } else {
    throw Exception('Failed to load album');
  }
}

class Album {
  final int userId;
  final int id;
  final String title;

  Album(this.userId, this.id, this.title);

  Album.fromJson(Map<String, dynamic> json)
      : userId = json['userId'],
        id = json['id'],
        title = json['title'];

  Map<String, dynamic> toJson() => {
        'userId': userId,
        'id': id,
        'title': title,
      };
}

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Future<Album> futureAlbum;
  late Future<Album> user;
  @override
  void initState() {
    super.initState();
    user = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        brightness: Brightness.dark,
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<Album>(
            future: user,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!.title);
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }
              return const CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

CodePudding user response:

Try this:

Future<List<Album>> fetchAlbum() async {
  final response = await http.get(
      Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));

  print(response);
  Map<String, dynamic> userMap = jsonDecode(response.body);
  if (response.statusCode == 200) {
    return (userMap['employees'] as List).map((e) => Album.fromJson(e)).toList()
    
  } else {
    throw Exception('Failed to load album');
  }
}

then change your FutureBuilder to this:

FutureBuilder<List<Album>>(
        future: user,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            List<Album> data = snapshot.data ?? [];

                return ListView.builder(
                  itemBuilder: (context, index) {
                    return Column(children: [
                      Text(data[index].title ?? ""),
                    ]);
                  },
                  itemCount: data.length,
                );
          } else if (snapshot.hasError) {
            return Text('${snapshot.error}');
          }
          return const CircularProgressIndicator();
        },
      )

CodePudding user response:

Your response.body return a list on employees key. And test this model with the response

Future<List<Album>?> fetchAlbum() async {
  final response = await http.get(
      Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body)["employees"] as List?;
    return data?.map((e) => Album.fromMap(e)).toList();
  } else {
    throw Exception('Failed to load album');
  }
}

class Album {
  final int userId;
  final int id;
  final String title;

  Album(this.userId, this.id, this.title);

  Map<String, dynamic> toMap() {
    final result = <String, dynamic>{};

    result.addAll({'userId': userId});
    result.addAll({'id': id});
    result.addAll({'title': title});

    return result;
  }

  factory Album.fromMap(Map<String, dynamic> map) {
    return Album(
      map['userId']?.toInt() ?? 0,
      map['id']?.toInt() ?? 0,
      map['title'] ?? '',
    );
  }

  String toJson() => json.encode(toMap());

  factory Album.fromJson(String source) => Album.fromMap(json.decode(source));
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late final user = fetchAlbum();
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FutureBuilder<List<Album>?>(
          future: user,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data?.length,
                itemBuilder: (context, index) =>
                    Text("${snapshot.data?[index].title}"),
              );
            } else if (snapshot.hasError) {
              return Text('${snapshot.error}');
            }
            return const CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

CodePudding user response:

so the resultMap should look right like this :

    {
  "employees": [
    {
      "userId": 1,
      "id": 2,
      "title": "Doe"
    },
    {
      "userId": 2,
      "id": 3,
      "title": "Smith"
    },
    {
      "userId": 3,
      "id": 4,
      "title": "Jones"
    }
  ]
}

This is a map that only has one property, which it values as a List of other maps

so accessing json['userId'] will try to get the userId from that map, which doesn't exist in the map

you need to access the employees property :

json["employees"]

then you get the value of it, which is the nested List of maps, and now you can access an element in the List with its index, then get the userId :

json["employees"][0]["userId"] // 1
json["employees"][1]["userId"] // 2
json["employees"][2]["userId"] // 3

hope this gives you a better approach to what you are trying to do, and what you need to do.

so this :

 return Album.fromJson(userMap);

should be replaced with this, as an example:

 return Album.fromJson(userMap["employees"][0]);

here the userMap["employees"][0] is :

{
  "userId": 1,
  "id": 2,
  "title": "Doe"
},

and now it should make an Album object from it.

if you want to get a List instead of the List<Map<string, dynamic>>, you need to iterate over the whole list using the map method or with a for loop

hope it helps

  • Related