Home > Software design >  Flutter read local Json from assets
Flutter read local Json from assets

Time:06-14

I am trying to understand how to work with Json in Flutter/Dart.

I want to read a local Json from the assets folder and display it on the screen.

I have in the assets folder the following Json

{
  "title": [
    {
      "number": 8,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    },

    {
      "number": 9,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    },
    {
      "number": 10,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    }
.
.
.
  ]
}


I tried to created a Class "DataModel" to be able to read from JSON file.

class DataModel {
  List<Title>? title;

  DataModel({this.title});

  DataModel.fromJson(Map<String, dynamic> json) {
    if (json['title'] != null) {
      title = <Title>[];
      json['title'].forEach((v) {
        title!.add(new Title.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.title != null) {
      data['title'] = this.title!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Title {
  int? number;
  List<double>? firstarray;
  List<int>? secondarray;

  Title({this.number, this.firstarray, this.secondarray});

  Title.fromJson(Map<String, dynamic> json) {
    number = json['number'];
    firstarray = json['firstarray'].cast<double>();
    secondarray = json['secondarray'].cast<int>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['number'] = this.number;
    data['firstarray'] = this.firstarray;
    data['secondarray'] = this.secondarray;
    return data;
  }
}


and I am trying to read it as follows. But i get the error message, when i am trying to diplay it "The getter 'number' isn't defined for the type 'DataModel'. Try importing the library that defines 'number', correcting the name to the name of an existing getter, or defining a getter or field named 'number'.dartundefined_getter" The same message for the firstarray ans second array.

  Future<DataModel> readJsonData() async {
    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final list = json.decode(jsonString) as Map<String, dynamic>;
    return DataModel.fromJson(list);
  }

Follows how i am trying to display it on the screen.

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/measure_data_model.dart';
import 'package:flutter/services.dart' as rootBundle;

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

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

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

class MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: FutureBuilder(
      future: readJsonData(),
      builder: (context, data) {
        if (data.hasError) {
          return Center(child: Text("${data.error}"));
        } else if (data.hasData) {
          var items = data.data as List<DataModel>;
          return ListView.builder(
              // ignore: unnecessary_null_comparison
              itemCount: items == null ? 0 : items.length,
              itemBuilder: (context, index) {
                return Card(
                  child: Row(
                    children: [
                      Expanded(
                          child: Container(
                        padding: const EdgeInsets.only(bottom: 8),
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Padding(
                              padding: const EdgeInsets.only(left: 8, right: 8),
                              child: Text(
                                items[index].number.toString(),
                              ),
                            ),
                            Padding(
                              padding: const EdgeInsets.only(left: 8, right: 8),
                              child: Text(items[index].firstarray.toString()),
                            ),
                            Padding(
                              padding: const EdgeInsets.only(left: 8, right: 8),
                              child: Text(items[index].secondarray.toString()),
                            ),
                          ],
                        ),
                      ))
                    ],
                  ),
                );
              });
        } else {
          return const Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    ));
  }

  Future<DataModel> readJsonData() async {
    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final list = json.decode(jsonString) as Map<String, dynamic>;
    return DataModel.fromJson(list);
  }
}

CodePudding user response:

Fix your model and future void:

DataModel

class DataModel {
  List<Title>? title;

  DataModel({this.title});

  DataModel.fromJson(Map<String, dynamic> json) {
    if (json['title'] != null) {
      title = <Title>[];
      json['title'].forEach((v) {
        title!.add(new Title.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.title != null) {
      data['title'] = this.title!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Title {
  int? number;
  List<double>? firstarray;
  List<int>? secondarray;

  Title({this.number, this.firstarray, this.secondarray});

  Title.fromJson(Map<String, dynamic> json) {
    number = json['number'];
    firstarray = json['firstarray'].cast<double>();
    secondarray = json['secondarray'].cast<int>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['number'] = this.number;
    data['firstarray'] = this.firstarray;
    data['secondarray'] = this.secondarray;
    return data;
  }
}

Function

Future<DataModel> readJsonData() async {
    final jsondata = await rootBundle.loadString('assets/measurelist.json');
    final list = json.decode(jsondata) as Map<String, dynamic> ;
    DataModel res = DataModel.fromJson(list);
    return res;
  }

CodePudding user response:

In your code your json data example does not match the way you try to read it. You provide a single element but you try to read a list of elements.

If the json data contains a single element

Your json structure is of type Map<String, dynamic> so this is the data type which json.decode returns (or you also use the method jsonDecode from import 'dart:convert';).

You then try to cast this Map to List<dynamic>which obviously fails.

Instead, the first part should look like this:

    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final json = json.decode(jsonString) as Map<String, dynamic>;

Afterwards you need to call the fromJsonmethod of your data model class.

In your example you try to use map to create one object for each element in the json array. However, in your example json you only have one object which is not part of an array. So that map doesn't make sense.

What should work is directly calling the fromJson method on the single data element you provide, like:

   return DataModel.fromJson(json);

Putting all together, your method should look like this:

  Future<DataModel> readJsonData() async {
    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final json = json.decode(jsonString) as Map<String, dynamic>;
    return DataModel.fromJson(json);
  }

If the json data contains a list of elements

If your datamodel actually contains a list of elements, the json data would look something like this:

[
{
  "title": [
    {
      "number": 8,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    }
  ]
},
{
  "title": [
    {
      "number": 9,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    }
  ]
},

...

]

Then your method for parsing it should look something like this:

  Future<List<DataModel>> readJsonData() async {
    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final listOfJsonElements = json.decode(jsonString) as List;
    return listOfJsonElements.map((jsonElement) => DataModel.fromJson(jsonElement)).toList();
  }
  • Related