nickname
nickname

Reputation: 11

Flutter - Errors when trying to save JSON Array to List

I am new to flutter and trying to fetch API data into a list. My goal is to later input that information into a ListView.

I am familiar with doing api calls but now I'm triying to get information from a json array. Previously I made a model class to retrieve the objects I need to continue with my process, but I've reached a dead end since I can't seem to figure out how to extract the objects from an array properly.

I am using multiple files. "API Services", "readData Model", and the actual screen where the data will be shown.

Everything else is fine, it's just that I am unable to actually save the data to a list.

First I will show you how my code is set up and the JSON response data I am trying to save:

JSON Response Body:

The example I am showing only has one chunk of image data, but we need an array for it since it should be displaying every chunk in a list.

 {"status":200,
  "content":[{"image_id":"151",
              "image_url":"https:\\\/imageurl.jpg",
              "returned_value":"14.0",
              "alarm":"false",
              "account":"[email protected]",
              "create_at":"2020-11-17 07:13:42",
              "location":"NY"
             }]
  }

API POST function:

  Future<ReadResponseModel> readData(
      ReadRequestModel requestData) async {


   final response = await http.post("$url/read",
        body: requestData.toJson() ,
        headers: {
    "Client-Service": "frontend-client",
    "Auth-Key": "simplerestapi",
    "Content-Type":"application/x-www-form-urlencoded",
    "Authorization": token,
    "User-ID": userId,
    });

    print(response.body);
    if (response.statusCode == 200 || response.statusCode == 400) {
      dynamic resBody = json.decode(response.body);
      return ReadResponseModel.fromJson(resBody);
    } else {
      throw Exception('Failed to load data!');
    }
  }

readResponseModel Class: I have tried two methods to process this information but have failed at both of them.

This is Method 1:

This one will give me the following error: [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'

Originally set as final String for all values, but since I was getting this error I've been trying to make sure each one is the correct type (ex. retVal as int or alarm as bool). Still no luck so far.

class ReadResponseModel {
  final dynamic imageID;
  final dynamic imgUrl;
  final dynamic retVal;
  final bool alarm;
  final dynamic account;
  final dynamic createAt;
  final dynamic location;

  ReadResponseModel({this.imageID, this.imgUrl, this.retVal, this.alarm,
    this.account, this.createAt, this.location,});

  factory ReadResponseModel.fromJson(Map<String , dynamic> json) {
    return ReadResponseModel(
      imageID: json['content']['image_id'] as String ,
      imgUrl: json['content']['image_url'] as String ,
      retVal: json['content']["returned_value"] as String,
      alarm: json['content']['alarm'] as bool ,
      account: json['content']['account'] as String,
      createAt: json['content']['create_at'] as String,
      location: json['content']['location'] as String,
    );
  }
}

Method 2:

This one will give me the following error: [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type '(dynamic) => Content' is not a subtype of type '(dynamic) => String' of 'f' -- I dont even know where that f came from.

class ReadResponseModel {
  List<String> content;
  ReadResponseModel({this.content});

  factory ReadResponseModel.fromJson(Map<String, dynamic> json) {
    return ReadResponseModel(
      content: json['content'] != null
          ? json['content']
              .map<String>((json) => Content.fromJson(json))
              .toList()
          : null,
    );
  }
}

class Content {
  final String imageID;
  final String imgUrl;
  final String retVal;
  final String alarm;
  final String account;
  final String createAt;
  final String location;
  final String error;

  Content({this.imageID,
    this.imgUrl,
    this.retVal,
    this.alarm,
    this.account,
    this.createAt,
    this.location,
    this.error});

  factory Content.fromJson(Map<String, dynamic> json) =>
      Content(
        imageID: json['image_id'],
        imgUrl: json['s3_url'],
        retVal: json['predict'],
        alarm: json['alarm'],
        account: json['account'],
        createAt: json['create_at'],
        location: json['location'],
      );
}

The following code is just what I'm doing to connect all this to my widget:

APIService readService = new APIService();
      readRequestModel.companyID = "123";

      readService.readData(readRequestModel).then((value) async {
         if (value != null) {
           setState(() {
             isApiCallProcess = false;
           });
       ///Trying to call the fetched data and show it in the console:
            print("Account: ${value.account}");
            print("Returned Value: ${value.retVal}");
            print("Image ID: ${value.imgUrl}");
         }

I'm not entirely sure why I am getting these errors, and for method 2 I don't even know where that "f" came from. If anyone could shed some light on the subject it would be greatly appreciated.

Upvotes: 0

Views: 1133

Answers (3)

Sagar Acharya
Sagar Acharya

Reputation: 3777

From the above mentioned code I have created a sample example for you, basically you are messing up with the mode class.Take a look at the example below.

Json you provided:

{"status":200,
    "content":[{"image_id":"151",
                "image_url":"https:\\\/imageurl.jpg",
                "returned_value":"14.0",
                "alarm":"false",
                "account":"[email protected]",
                "create_at":"2020-11-17 07:13:42",
                "location":"NY"
               }]
    }

Based on the json the model class below :

// To parse this JSON data, do
//
//     final readResponseModel = readResponseModelFromJson(jsonString);

import 'dart:convert';

ReadResponseModel readResponseModelFromJson(String str) => ReadResponseModel.fromJson(json.decode(str));

String readResponseModelToJson(ReadResponseModel data) => json.encode(data.toJson());

class ReadResponseModel {
    ReadResponseModel({
        this.status,
        this.content,
    });

    int status;
    List<Content> content;

    factory ReadResponseModel.fromJson(Map<String, dynamic> json) => ReadResponseModel(
        status: json["status"],
        content: List<Content>.from(json["content"].map((x) => Content.fromJson(x))),
    );

    Map<String, dynamic> toJson() => {
        "status": status,
        "content": List<dynamic>.from(content.map((x) => x.toJson())),
    };
}

class Content {
    Content({
        this.imageId,
        this.imageUrl,
        this.returnedValue,
        this.alarm,
        this.account,
        this.createAt,
        this.location,
    });

    String imageId;
    String imageUrl;
    String returnedValue;
    String alarm;
    String account;
    DateTime createAt;
    String location;

    factory Content.fromJson(Map<String, dynamic> json) => Content(
        imageId: json["image_id"],
        imageUrl: json["image_url"],
        returnedValue: json["returned_value"],
        alarm: json["alarm"],
        account: json["account"],
        createAt: DateTime.parse(json["create_at"]),
        location: json["location"],
    );

    Map<String, dynamic> toJson() => {
        "image_id": imageId,
        "image_url": imageUrl,
        "returned_value": returnedValue,
        "alarm": alarm,
        "account": account,
        "create_at": createAt.toIso8601String(),
        "location": location,
    };
}

And them main class for fetching the data and showing it in the listview.

import 'package:flutter/material.dart';
import 'package:json_parsing_example/model2.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SampleApp(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class SampleApp extends StatefulWidget {
  @override
  _SampleAppState createState() => _SampleAppState();
}

class _SampleAppState extends State<SampleApp> {
  bool _isLoading = false;
  List<Content> list = List();

  fetchData() async {
    setState(() {
      _isLoading = true;
    });

    String data =
        await DefaultAssetBundle.of(context).loadString("json/parse.json");

    // This is the above where you get the remote data
    // Like var response = await get or post
    

    final readResponseModel = readResponseModelFromJson(data);

    list = readResponseModel.content;

    setState(() {
      _isLoading = false;
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Your heading'),
        ),
        body: Container(
            child: _isLoading
                ? Center(child: CircularProgressIndicator())
                : Column(
                    children: <Widget>[
                      ListView.builder(
                          shrinkWrap: true,
                          itemCount: list.length,
                          itemBuilder: (context, index) {
                            return Card(
                              child: Column(
                                children: <Widget>[
                                  
                                  Text('${list[index].account}'),
                                  Text('${list[index].location}')
                                ],
                              ),
                            );
                          })
                    ],
                  )));
  }
}

Let me know if it works

Upvotes: 1

chunhunghan
chunhunghan

Reputation: 54397

You can copy paste run full code below
Step 1: Use List<Content> content; not List<String>
Step 2: Use List<Content>.from(json["content"].map((x) => Content.fromJson(x)))
Step 3: In sample JSON's image_url not equal model's s3_url, you need to modify to correct one
code snippet

class ReadResponseModel {
  List<Content> content;
  ReadResponseModel({this.content});

  factory ReadResponseModel.fromJson(Map<String, dynamic> json) {
    return ReadResponseModel(
      content: json['content'] != null
          ? List<Content>.from(json["content"].map((x) => Content.fromJson(x)))
          : null,
    );
  }
}
...
dynamic resBody = json.decode(jsonString);
ReadResponseModel model = ReadResponseModel.fromJson(resBody);
print(model.content[0].account);

output

I/flutter ( 4426): [email protected]

full code

import 'package:flutter/material.dart';
import 'dart:convert';

class ReadResponseModel {
  List<Content> content;
  ReadResponseModel({this.content});

  factory ReadResponseModel.fromJson(Map<String, dynamic> json) {
    return ReadResponseModel(
      content: json['content'] != null
          ? List<Content>.from(json["content"].map((x) => Content.fromJson(x)))
          : null,
    );
  }
}

class Content {
  final String imageID;
  final String imgUrl;
  final String retVal;
  final String alarm;
  final String account;
  final String createAt;
  final String location;
  final String error;

  Content(
      {this.imageID,
      this.imgUrl,
      this.retVal,
      this.alarm,
      this.account,
      this.createAt,
      this.location,
      this.error});

  factory Content.fromJson(Map<String, dynamic> json) => Content(
        imageID: json['image_id'],
        imgUrl: json['s3_url'],
        retVal: json['predict'],
        alarm: json['alarm'],
        account: json['account'],
        createAt: json['create_at'],
        location: json['location'],
      );
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    String jsonString = '''
    {"status":200,
  "content":[{"image_id":"151",
              "image_url":"https:\\\/imageurl.jpg",
              "returned_value":"14.0",
              "alarm":"false",
              "account":"[email protected]",
              "create_at":"2020-11-17 07:13:42",
              "location":"NY"
             }]
  }
    ''';

    dynamic resBody = json.decode(jsonString);
    ReadResponseModel model = ReadResponseModel.fromJson(resBody);
    print(model.content[0].account);

    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Upvotes: 1

PDHide
PDHide

Reputation: 19979

Can you try setting returned_value and image_id as int instead of string and give it a try ?

Upvotes: 0

Related Questions