Parse Array in Object in Array in Dart / Flutter - json

I have a REST Service I like to consume, but I do not know how to parse that JSON to an Object.
My JSON looks like that:
[
{
"types": {
"KEYWORD": "STRING"
},
"displaynames": {
"KEYWORD": "Keyword"
},
"rows": [
{
"KEYWORD": "Test 1"
},
{
"KEYWORD": "Test 2"
}
]
}
]
That is my object I created from that:
import 'dart:convert';
List<Todo> welcomeFromJson(String str) =>
List<Todo>.from(json.decode(str).map((x) => Todo.fromJson(x)));
String welcomeToJson(List<Todo> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Todo {
Todo({
required this.types,
required this.displaynames,
required this.rows,
});
Displaynames types;
Displaynames displaynames;
List<Displaynames> rows;
factory Todo.fromJson(Map<String, dynamic> json) => Todo(
types: Displaynames.fromJson(json["types"]),
displaynames: Displaynames.fromJson(json["displaynames"]),
rows: List<Displaynames>.from(
json["rows"].map((x) => Displaynames.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"types": types.toJson(),
"displaynames": displaynames.toJson(),
"rows": List<dynamic>.from(rows.map((x) => x.toJson())),
};
}
class Displaynames {
Displaynames({
required this.keyword,
});
String keyword;
factory Displaynames.fromJson(Map<String, dynamic> json) => Displaynames(
keyword: json["KEYWORD"],
);
Map<String, dynamic> toJson() => {
"KEYWORD": keyword,
};
}
I try Loading the JSON and Display like that by using the pull_to_refresh Package.
import 'package:flutter/material.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
import 'dart:convert';
import 'package:user_portal/model/todo.dart';
class TodoRoute extends StatefulWidget {
const TodoRoute({super.key});
#override
State<TodoRoute> createState() => _TodoRouteState();
}
class _TodoRouteState extends State<TodoRoute> {
late List<Todo> todoList = [];
final RefreshController refreshController =
RefreshController(initialRefresh: true);
Future<bool> fetchTodo() async {
const String jsonstr =
'[ { "types": { "KEYWORD": "STRING" }, "displaynames": { "KEYWORD": "Keyword" }, "rows": [ { "KEYWORD": "Test 1" }, { "KEYWORD": "Test 2" } ] }]';
todoList = (json.decode(jsonstr) as List)
.map((data) => Todo.fromJson(data))
.toList();
setState(() {});
return true;
}
#override
Widget build(context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo'),
),
body: SmartRefresher(
controller: refreshController,
enablePullUp: true,
onRefresh: () async {
final result = await fetchTodo();
if (result) {
refreshController.refreshCompleted();
} else {
refreshController.refreshFailed();
}
},
onLoading: () async {
final result = await fetchTodo();
if (result) {
refreshController.loadComplete();
} else {
refreshController.loadFailed();
}
},
child: ListView.separated(
scrollDirection: Axis.vertical,
itemCount: todoList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todoList[0].rows[index].keyword),
);
},
separatorBuilder: (context, index) => const Divider(),
),
),
);
}
}
But the Only Item I get is the Test 1 Item not the Test 2 Item and I do not know where to look further for that Problem.

try this way it's so easy...
add this method in your TOdo model
static List< Todo > fromList(json) {
List< Todo > tempList = [];
try {
json.forEach((element) {
tempList.add(Todo.fromJson(element));
});
return tempList;
} catch (e) {
return tempList;
}
}
change this function
Future<bool> fetchTodo() async {
const String jsonstr =
'[ { "types": { "KEYWORD": "STRING" },
"displaynames": { "KEYWORD": "Keyword" }, "rows": [ {
"KEYWORD": "Test 1" }, { "KEYWORD": "Test 2" }
]
}]';
Map data = json.decode(jsonstr);
todoList = Todo.fromList(data);
setState(() {});
return true;
}

Change your fetchTodo() to this:
Future<bool> fetchTodo() async {
const String jsonstr =
'[ { "types": { "KEYWORD": "STRING" }, "displaynames": { "KEYWORD": "Keyword" }, "rows": [ { "KEYWORD": "Test 1" }, { "KEYWORD": "Test 2" } ] }]';
todoList = welcomeFromJson(jsonstr);//<--- add this
setState(() {});
return true;
}
also change this in asdas class:
factory Todo.fromJson(Map<String, dynamic> json) => Todo(
types: Displaynames.fromJson(json["types"]),
displaynames: Displaynames.fromJson(json["displaynames"]),
rows: json["rows"] != null ? (json["rows"] as List).map((x) => Displaynames.fromJson(x)).toList():[],

Related

Filtering nested JSON response in Flutter

Hello I have a question regarding filtering of nested json response from an API.
Suppose I have this sample JSON response from an API:
{
"results": {
"data": {
"parentName": "Vic",
"parentFamName": "Lo",
"children": [
{
"childName": "Mark",
"childCount": 2,
"grandChildren": [
{
"grandChildName": "Kent",
"grandChildAge": 4,
"isBoy": true,
},
{
"grandChildName": "Chris",
"grandChildAge": 8
"isBoy": true,
}]
},
{
"childName": "Kim",
"childCount": 3,
"grandChildren": [
{
"grandChildName": "Martin",
"grandChildAge": 6,
"isBoy": true,
},
{
"grandChildName": "Thesa",
"grandChildAge": 4
"isBoy": false,
},
{
"grandChildName": "Beck",
"grandChildAge": 2,
"isBoy": false,
}]
}]
}
}
}
then ill be decoding the response by:
final jsonResponse = jsonDecode(jsonString.body);
final parentResponse = jsonResponse['results']['data'];
But before I display it in a ListView.builder,
how do I filter the parenetResponse wherein it will only include "isBoy" = true?
like the final data response to display would be:
{
"results": {
"data": {
"parentName": "Vic",
"parentFamName": "Lo",
"children": [
{
"childName": "Mark",
"childCount": 2,
"grandChildren": [
{
"grandChildName": "Kent",
"grandChildAge": 4,
"isBoy": true,
},
{
"grandChildName": "Chris",
"grandChildAge": 8
"isBoy": true,
}]
},
{
"childName": "Kim",
"childCount": 3,
"grandChildren": [
{
"grandChildName": "Martin",
"grandChildAge": 6,
"isBoy": true,
},
]
}]
}
}
}
Thank you for any help!
First create model class to parse your json:
class ParentModel {
final String parentName;
final List<ChildrenModel> children;
ParentModel({required this.parentName, required this.children});
static ParentModel fromJson(Map<String, dynamic> json) {
var children = json['children'] as List;
return ParentModel(
parentName: json['parentName'],
children: children.map((e) => ChildrenModel.fromJson(e)).toList());
}
}
class ChildrenModel {
final int count;
final String name;
final List<GrandChildModel> grandChildren;
ChildrenModel({
required this.count,
required this.name,
required this.grandChildren,
});
static ChildrenModel fromJson(Map<String, dynamic> json) {
var grandChildren = json["grandChildren"] as List;
return ChildrenModel(
count: json['childCount'],
name: json['childName'],
grandChildren:
grandChildren.map((e) => GrandChildModel.fromJson(e)).toList());
}
}
class GrandChildModel {
final int age;
final String name;
final bool isBoy;
GrandChildModel({required this.age, required this.name, required this.isBoy});
static GrandChildModel fromJson(Map<String, dynamic> json) {
return GrandChildModel(
age: json['grandChildAge'],
name: json['grandChildName'],
isBoy: json['isBoy']);
}
}
then use this to show the boy grandChildren:
class TestParent extends StatefulWidget {
const TestParent({super.key});
#override
State<TestParent> createState() => _TestParentState();
}
class _TestParentState extends State<TestParent> {
#override
Widget build(BuildContext context) {
final parentResponse = jsonResponse['results']!['data'];
var parent = ParentModel.fromJson(parentResponse!);
return Scaffold(
appBar: AppBar(title: Text("Testing")),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: parent.children.length,
itemBuilder: (context, index) {
return ListView.builder(
shrinkWrap: true,
itemCount: parent.children[index].grandChildren.length,
itemBuilder: (context, i) {
var grandChild = parent.children[index].grandChildren[i];
if (grandChild.isBoy) {
return Text(grandChild.name);
} else {
return SizedBox();
}
},
);
},
),
)
],
),
);
}
}
Note that here jsonResponse is your json;
result:
Without making model classes you could simply modify the response like this:
parentResponse['children'] = parentResponse['children']
.map((child) => child
..['grandChildren'] = child['grandChildren']
.where((grandChild) => grandChild['isBoy'] as bool)
.toList())
.toList();

Put JSON Data in Flutter Stacked Chart

I have try to put my JSON data in flutter Stacked Chart.
I already work on simple charts using JSON Data like bar, column, pie, Doughnut charts etc.
I have refer
stacked-column-chart(syncfusion_flutter_charts),
Grouped Bar Chart(charts_flutter)
Stack Overflow Que-Ans
below like my API response/JSON String
[{
"name": "ABC",
"subject": [{
"name": "Math",
"marks": "54"
},
{
"name": "Physics",
"marks": "65"
}
]
},
{
"name": "PQR",
"subject": [{
"name": "Chemistry",
"marks": "53"
},
{
"name": "Biology",
"marks": "22"
},
{
"name": "English",
"marks": "7 "
},
{
"name": "Math",
"marks": "12"
}
]
}, {
"name": "JKL",
"subject": [{
"name": "Chemistry",
"marks": "53"
},
{
"name": "Biology",
"marks": "22"
},
{
"name": "English",
"marks": "79 "
},
{
"name": "Math",
"marks": "12"
},
{
"name": "Physics",
"marks": "72"
}
]
}
]
Or I want below type of graph using JSON Data
Note: Suggest me my JSON string is wrong, you can create your own JSON data and display the output
Using charts_flutter. Please customize it for your usecase its a bare minimum implementation to validate that its working for your json.
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'dart:convert';
class StackedBarChart extends StatelessWidget {
final bool animate;
StackedBarChart({this.animate = false});
// EXCLUDE_FROM_GALLERY_DOCS_END
#override
Widget build(BuildContext context) {
String jsonString = '[{"name":"ABC","subject":[{"name":"Math","marks":"54"},{"name":"Physics","marks":"65"}]},{"name":"PQR","subject":[{"name":"Chemistry","marks":"53"},{"name":"Biology","marks":"22"},{"name":"English","marks":"7 "},{"name":"Math","marks":"12"}]},{"name":"JKL","subject":[{"name":"Chemistry","marks":"53"},{"name":"Biology","marks":"22"},{"name":"English","marks":"79 "},{"name":"Math","marks":"12"},{"name":"Physics","marks":"72"}]}]';
final studentMarks = studentMarksFromJson(jsonString);
var subjects = <Subject?>{};
var subjectsDist = <Subject?>{};
int c=0;
for (var stdnt in studentMarks) {
for (var subjs in stdnt.subject) {
if (!subjectsDist.where((element) => element?.name==subjs.name).isNotEmpty) {
subjs.sno=c++;
subjectsDist.add(subjs);
}
}
}
print(subjectsDist.length);
List<List<OrdinalMarks>> SubjectData = [];
for (var subjs in subjectsDist) {
List<OrdinalMarks> marksData = [];
for (var stdnt in studentMarks) {
if (stdnt.subject
.where((element) => element.name == subjs?.name).isNotEmpty) {
var temp = stdnt.subject
.where((element) => element.name == subjs?.name)
.first;
marksData.add(OrdinalMarks(temp.name, int.parse(temp.marks),stdnt.name));
} else {
marksData.add(OrdinalMarks(subjs!.name, 0,stdnt.name));
}
}
SubjectData.add(marksData);
}
var palettes = charts.MaterialPalette.getOrderedPalettes(subjectsDist.length+2);
int cnt=0;
List<charts.Series<OrdinalMarks, String>> chartData = [
];
for(var d in SubjectData)
{
chartData.add(new charts.Series<OrdinalMarks, String>(
id: d.first.subjectName,
domainFn: (OrdinalMarks m, _) => m.studentName,
measureFn: (OrdinalMarks m, _) => m.marks,
data: d,
fillColorFn: ( subj, _) {
// print(subj.subjectName+": subj.subjectName :" + pallets[subj.subjectName].toString()??charts.MaterialPalette.blue.shadeDefault.toString());
return palettes.elementAt( subjectsDist.where((element) => element?.name==subj.subjectName).first?.sno??0 ).shadeDefault; //pallets[subj.subjectName]??charts.MaterialPalette.blue.shadeDefault;
},
colorFn: ( subj, _) {
// print(subj.subjectName+": subj.subjectName :" + pallets[subj.subjectName].toString()??charts.MaterialPalette.blue.shadeDefault.toString());
return palettes.elementAt(subjectsDist.where((element) => element?.name==subj.subjectName).first?.sno??0).shadeDefault;
},
));
}
return Scaffold(
// Use Obx(()=> to update Text() whenever count is changed.
appBar: AppBar(title: Text("Chart")),
// Replace the 8 lines Navigator.push by a simple Get.to(). You don't need context
body:new charts.BarChart(
chartData,
animate: animate,
behaviors: [new charts.SeriesLegend(showMeasures: true)],
animationDuration: Duration(seconds: 3),
));
}
}
/// Sample ordinal data type.
class OrdinalMarks {
final String subjectName;
final int marks;
final String studentName;
OrdinalMarks(this.subjectName, this.marks,this.studentName);
}
List<StudentMarks> studentMarksFromJson(String str) => List<StudentMarks>.from(json.decode(str).map((x) => StudentMarks.fromJson(x)));
String studentMarksToJson(List<StudentMarks> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class StudentMarks {
StudentMarks({
required this.name,
required this.subject,
});
String name;
List<Subject> subject;
factory StudentMarks.fromJson(Map<String, dynamic> json) => StudentMarks(
name: json["name"],
subject: List<Subject>.from(json["subject"].map((x) => Subject.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"name": name,
"subject": List<dynamic>.from(subject.map((x) => x.toJson())),
};
}
class Subject {
Subject({
required this.name,
required this.marks,
});
String name;
String marks;
int? sno;
factory Subject.fromJson(Map<String, dynamic> json) => Subject(
name: json["name"],
marks: json["marks"],
);
Map<String, dynamic> toJson() => {
"name": name,
"marks": marks,
};
}

Parse Json in flutter with nested objects

I am currently having issues when trying to print my Varients class (below) I do need to continue to add the nested objects later on but want to address this issue before I do.
So I can return response.body but when I print Varient.fromJson(i).varients I get Failed, no response.
JSON
{
"varients":[
{
"ProductId":"CMK8866",
"Colour":"Mid Blue Wash",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/2/5/2/4/252476b6dcf258958d52438b19b811d76da60dc0_CMK8866_1.jpg?imwidth=120",
"price":"28.00",
"name":"Mid Blue Wash Long Leg Straight Jeans",
"retailer":"prettylittlething"
},
{
"ProductId":"CMG0839",
"Colour":"Light Blue Wash",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/f/7/d/f/f7dfd26d523507c2f2e6697e408d932c8e0fac76_cmg0839_1.jpg?imwidth=120",
"price":"28.00",
"name":"Light Wash Long Leg Straight Jeans",
"retailer":"prettylittlething"
},
{
"ProductId":"CMQ3243",
"Colour":"Off White",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/d/9/0/2/d902d215e155a61d4cfc8999d714430f5fda01f9_CMK8867_1.jpg?imwidth=120",
"price":"28.00",
"name":"Off White Contrast Stitch Long Leg Straight Jeans",
"retailer":"prettylittlething"
},
{
"ProductId":"CMQ3245",
"Colour":"Stone",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/f/f/f/8/fff81a83fae46694dda3fcfef04d28508de64fdc_cmq3243_1.jpg?imwidth=120",
"price":"22.00",
"name":"Off White Long Leg Straight Jeans",
"retailer":"prettylittlething"
},
{
"ProductId":"CMQ3642",
"Colour":"Ecru",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/7/7/0/4/77046819c17f097804383b4fda0e0fda56b35239_cmq3245_1.jpg?imwidth=120",
"price":"28.00",
"name":"Stone Long Leg Straight Jeans",
"retailer":"prettylittlething"
},
{
"ProductId":"CMS6181",
"Colour":"Teal",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/0/6/0/7/0607455b765c3aed0356be8dafab301182919d2f_cmq3642_1.jpg?imwidth=120",
"price":"21.00",
"name":"Ecru With Contrast Stitch Long Leg Straight Jeans",
"retailer":"prettylittlething"
},
{
"ProductId":"CMS6180",
"Colour":"Chocolate",
"Sizes":[
"4",
"6",
"8",
"10",
"12",
"14",
"16"
],
"image":"https://cdn-img.prettylittlething.com/5/5/9/6/559602e94377d0ebbc5f222b30bbb96ec3104bf8_cms6181_1.jpg?imwidth=120",
"price":"25.00",
"name":"Teal Long Leg Straight Jean",
"retailer":"prettylittlething"
}
]
}
Code
Future<dynamic> requestProductMonitor() async {
final product_link = productIDEditingController.text;
var url = Uri.parse(
"http://5.226.139.20:8000/getvarient/?url=https://www.prettylittlething.com/mid-blue-wash-long-leg-straight-jeans.html");
http.Response response = await http.get(url);
try {
if (response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body);
print("this is the Json Response $jsonResponse");
setState(() {
for (Map<String, dynamic> i in jsonResponse) {
listModel1.add(Varient.fromJson(i));
//var test = Varient.fromJson(i).varients;
//print("This is the varients $test");
//product_size.add(User.fromJson(i).name);
}
});
showDropDown();
} else {
print("Error");
}
} catch (exp) {
print("Failed, no response");
}
}
This is my Varients class:
List<Varient> modelUserFromJson(String str) =>
List<Varient>.from(json.decode(str).map((x) => Varient.fromJson(x)));
String modelUserToJson(List<Varient> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Varient {
String varients;
Varient({
required this.varients,
});
factory Varient.fromJson(Map<String, dynamic> json) => Varient(
varients: json["varients"],
);
Map<String, dynamic> toJson() => {
"varients": varients,
};
}
Wondering if anyone has any ideas ?
main.dart
import 'package:flutter/material.dart';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main() {
runApp(
const MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),
);
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
dynamic varient;
requestProductMonitor() async {
final response = await http.get(Uri.parse(
"http://5.226.139.20:8000/getvarient/?url=https://www.prettylittlething.com/mid-blue-wash-long-leg-straight-jeans.html"));
if (response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body);
varient = await jsonResponse["varients"];
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
#override
void initState() {
requestProductMonitor();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: FutureBuilder(
future: requestProductMonitor(), // async work
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return GridView.builder(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 350,
mainAxisSpacing: 1,
crossAxisSpacing: 10),
scrollDirection: Axis.vertical,
itemCount: varient.length,
itemBuilder: (BuildContext context, int index) {
List<String> sizes =
varient[index]["Sizes"].cast<String>();
return Wrap(
alignment: WrapAlignment.center,
children: [
Image.network(
varient[index]["image"],
fit: BoxFit.cover,
),
Text(varient[index]["name"]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
sizes.isEmpty
? const Text("No Size Available")
: const Text("Available Sizes: "),
sizes.isNotEmpty
? DropdownButton<String>(
items: sizes.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Center(child: Text(value)),
);
}).toList(),
onChanged: (_) {},
)
: const SizedBox(),
],
)
],
);
});
}
}
},
),
));
}
}
This is your desired output, the sizes are now shown in a drop-down menu and can read data from the JSON to get name and image.

Can't decode/Parse Json type list<String> with this list<String> type Model in Dart

I wanted to create a drop down from json list. I am using app.quicktype.io to covert json to PODO (Plain Old dart Object)
Here is the My JSON data:
[
{
"country_name": "Andorra",
"alpha2_code": "AD",
"country_code": "376",
"states": [
{ "state_name": "Andorra la Vella" },
{ "state_name": "Canillo" }
]
},
]
And here is the PODO (Plain Old Dart Object) I created with app.quicktype.io:
class CountryDetailsModel {
CountryDetailsModel({
this.countryName,
this.alpha2Code,
this.countryCode,
this.states,
});
String countryName;
String alpha2Code;
String countryCode;
List<StateNames> states;
factory CountryDetailsModel.fromJson(Map<String, dynamic> json) =>
CountryDetailsModel(
countryName: json["country_name"],
alpha2Code: json["alpha2_code"],
countryCode: json["country_code"],
states: List<StateNames>.from(
json["states"].map((x) => StateNames.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"country_name": countryName,
"alpha2_code": alpha2Code,
"country_code": countryCode,
"states": List<dynamic>.from(states.map((x) => x.toJson())),
};
}
class StateNames {
StateNames({
this.stateName,
});
String stateName;
factory StateNames.fromJson(Map<String, dynamic> json) => StateNames(
stateName: json["state_name"],
);
Map<String, dynamic> toJson() => {
"state_name": stateName,
};
}
You can copy paste run full code below
In working demo, simulate network delay with 3 seconds
You can use FutureBuilder and use return countryDetailsModelFromJson(jsonString);
code snippet
Future<List<CountryDetailsModel>> getHttp() async {
String jsonString = ...
return countryDetailsModelFromJson(jsonString);
}
...
FutureBuilder(
future: _future,
builder:
(context, AsyncSnapshot<List<CountryDetailsModel>> snapshot) {
...
return DropdownButton<CountryDetailsModel>(
//isDense: true,
hint: Text('Choose'),
value: _selectedValue,
icon: Icon(Icons.check_circle_outline),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.blue[300],
),
onChanged: (CountryDetailsModel newValue) {
setState(() {
_selectedValue = newValue;
});
},
items: snapshot.data
.map<DropdownMenuItem<CountryDetailsModel>>(
(CountryDetailsModel value) {
return DropdownMenuItem<CountryDetailsModel>(
value: value,
child: Text(value.countryName),
);
}).toList(),
);
}
}
})
working demo
full code
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
List<CountryDetailsModel> countryDetailsModelFromJson(String str) =>
List<CountryDetailsModel>.from(
json.decode(str).map((x) => CountryDetailsModel.fromJson(x)));
String countryDetailsModelToJson(List<CountryDetailsModel> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class CountryDetailsModel {
CountryDetailsModel({
this.countryName,
this.alpha2Code,
this.countryCode,
this.states,
});
String countryName;
String alpha2Code;
String countryCode;
List<StateNames> states;
factory CountryDetailsModel.fromJson(Map<String, dynamic> json) =>
CountryDetailsModel(
countryName: json["country_name"],
alpha2Code: json["alpha2_code"],
countryCode: json["country_code"],
states: List<StateNames>.from(
json["states"].map((x) => StateNames.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"country_name": countryName,
"alpha2_code": alpha2Code,
"country_code": countryCode,
"states": List<dynamic>.from(states.map((x) => x.toJson())),
};
}
class StateNames {
StateNames({
this.stateName,
});
String stateName;
factory StateNames.fromJson(Map<String, dynamic> json) => StateNames(
stateName: json["state_name"],
);
Map<String, dynamic> toJson() => {
"state_name": stateName,
};
}
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
CountryDetailsModel _selectedValue;
Future<List<CountryDetailsModel>> _future;
Future<List<CountryDetailsModel>> getHttp() async {
String jsonString = '''
[
{
"country_name": "Andorra",
"alpha2_code": "AD",
"country_code": "376",
"states": [
{ "state_name": "Andorra la Vella" },
{ "state_name": "Canillo" },
{ "state_name": "Encamp" },
{ "state_name": "La Massana" },
{ "state_name": "Les Escaldes" },
{ "state_name": "Ordino" },
{ "state_name": "Sant Julia de Loria" }
]
},
{
"country_name": "Azerbaijan",
"alpha2_code": "AZ",
"country_code": "994",
"states": [
{ "state_name": "Abseron" },
{ "state_name": "Baki Sahari" },
{ "state_name": "Ganca" },
{ "state_name": "Ganja" },
{ "state_name": "Kalbacar" },
{ "state_name": "Lankaran" },
{ "state_name": "Mil-Qarabax" },
{ "state_name": "Mugan-Salyan" },
{ "state_name": "Nagorni-Qarabax" },
{ "state_name": "Naxcivan" },
{ "state_name": "Priaraks" },
{ "state_name": "Qazax" },
{ "state_name": "Saki" },
{ "state_name": "Sirvan" },
{ "state_name": "Xacmaz" }
]
}]
''';
return countryDetailsModelFromJson(jsonString);
}
#override
void initState() {
_future = getHttp();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _future,
builder:
(context, AsyncSnapshot<List<CountryDetailsModel>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return DropdownButton<CountryDetailsModel>(
//isDense: true,
hint: Text('Choose'),
value: _selectedValue,
icon: Icon(Icons.check_circle_outline),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.blue[300],
),
onChanged: (CountryDetailsModel newValue) {
setState(() {
_selectedValue = newValue;
});
},
items: snapshot.data
.map<DropdownMenuItem<CountryDetailsModel>>(
(CountryDetailsModel value) {
return DropdownMenuItem<CountryDetailsModel>(
value: value,
child: Text(value.countryName),
);
}).toList(),
);
}
}
}));
}
}
Finally I found a solution.
Here is the Process:
List<CountryDetailsModel> countryDetails() {
List<Map> map = CountryData.countryData;
List<CountryDetailsModel> list =
map.map((json) => CountryDetailsModel.fromJson(json)).toList();
return list;
}
And for printing all countries:
countryDetails().
forEach((element) {
print(element.countryName);
});

Observable from a RESTful paged collection

On one hand, I have a RESTful HAL HATEOAS collection which looks like this :
{
"page": 1,
"limit": 10,
"pages": 18,
"total": 174,
"_links": {
"self": { "href": "/users?page=1&limit=10" },
"first": { "href": "/users?page=1&limit=10" },
"last": { "href": "/users?page=18&limit=10" },
"next": { "href": "/users?page=2&limit=10" }
},
"_embedded": {
"users": [
{
"name": "bob",
"_links": { "self": { "href": "/users/1" } }
},
...
]
}
}
On the other hand, I have an Angular 2 app.
public getUsers(uri: string = this.baseURI): Observable<User> {
return this.http.get(uri)
.map(res => res.json()._embedded.users as User[])
.flatMap(d => d) // Transform the flux of arrays in flux of users
.catch(this.handleError);
} // Get only the 10th first users
What I'm trying to do have an observable of Users which will append data while _links.next != null
Modified service
public getUsers(uri: string = this.baseURI): Observable<User> {
return this.http.get(uri)
.do(res => {
const uri = JSON.parse(res._body)._links.next.href;
this.nextUri = uri ? uri : null;
})
.map(res => res.json()._embedded.users as User[])
.flatMap(d => d) // Transform the flux of arrays in flux of users
.catch(this.handleError);
}
Recursive function
loadAll(uri: string) {
read(uri)
.subscribe(
user => {
this.stockedUsers.push(user);
},
error => console.log(error),
() => {
if (this.nextUri) {
this.loadAll(this.nextUri);
}
}
);
}
Does someone know how to achieve this properly ?
I want to keep thes advantages of the RxJS flux.
UPDATE/ANSWER
Silly me ! I think I answered myself. Maybe this will help others :
public read(uri: string = this.baseURI): Observable<User> {
return Observable.create(observer => this.iteratePages(observer, uri));
}
private iteratePages(observer: Observer<User>, uri): void {
if (uri == null) { return observer.complete(); }
this.http.get(uri).subscribe(res => {
const data = res.json();
for (const user of data._embedded.users) {
observer.next(user as User);
}
const nextUri = (data._links && data._links.next) ? data._links.next.href : null;
this.iteratePages(observer, nextUri);
});
}