I am getting an array of object from my rest API and try to access in flutter but I can not access.I want to access posts,comments,like and status.
The error code is The getter 'data' isn't defined for the type 'List'.
Try importing the library that defines 'data', correcting the name to the name of an existing getter, or defining a getter or field named 'data
and error is given by line snapshot.data!.data[index].posts[postPosition].url and snapshot.data!.data[index].status
The data from API:
[
{
"_id": "6304e73ecdc5d350cc33e902",
"userId": "6304e42231ef2e7a4dec924d",
"posts": [
{
"postType": "image",
"post": "https://www.theskinnybeep.com/wp-content/uploads/2019/01/Versace-Man-2019.jpg",
"_id": "6304e73ecdc5d350cc33e903"
},
{
"postType": "image",
"post": "https://www.theskinnybeep.com/wp-content/uploads/2019/01/Versace-Man-2019.jpg",
"_id": "6304e73ecdc5d350cc33e904"
}
],
"status": "testing status",
"comments": [
{
"commentText": "Testing comment",
"commentAt": "2022-08-23T14:41:55.646Z",
"commentReplys": [
{
"userId": "6304e02d481e08d44e618d41",
"replyText": "Testing comment",
"replyAt": "2022-08-23T14:41:55.646Z",
"replyLikes": [
{
"userId": "6304e02d481e08d44e618d41",
"_id": "6304e73ecdc5d350cc33e907",
"isNotified": true
},
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8d07d513ce62b9c099f"
}
],
"_id": "6304e73ecdc5d350cc33e906"
},
{
"userId": "6304e02d481e08d44e618d41",
"replyText": "reply text testing",
"replyAt": "2022-08-23T15:57:51.259Z",
"_id": "6304f90191c32e0deac663b8",
"replyLikes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8d07d513ce62b9c099f"
}
]
}
],
"commentLikes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8f67d513ce62b9c09a2"
}
],
"commentId": "bc174de0-22f1-11ed-9c5d-23d89a83ff32",
"_id": "6304e73ecdc5d350cc33e905"
},
{
"commentText": "Testing comment",
"commentAt": "2022-08-23T15:02:11.123Z",
"commentId": "90928740-22f4-11ed-b912-e99836187b6d",
"_id": "6304ec67825b5926f0f074cf",
"commentReplys": [
{
"userId": "6304e02d481e08d44e618d41",
"replyText": "reply text testing",
"replyAt": "2022-08-23T15:57:51.259Z",
"_id": "6304f90191c32e0deac663b8",
"replyLikes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8d07d513ce62b9c099f"
}
]
}
],
"commentLikes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8f67d513ce62b9c09a2"
}
]
},
{
"commentText": "Testing comment",
"commentAt": "2022-08-23T15:02:11.123Z",
"commentId": "90928740-22f4-11ed-b912-e99836187b6d",
"_id": "6304ec81825b5926f0f074d1",
"commentReplys": [
{
"userId": "6304e02d481e08d44e618d41",
"replyText": "reply text testing",
"replyAt": "2022-08-23T15:57:51.259Z",
"_id": "6304f90191c32e0deac663b8",
"replyLikes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8d07d513ce62b9c099f"
}
]
}
],
"commentLikes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"isNotified": true,
"_id": "6305f8f67d513ce62b9c09a2"
}
]
}
],
"likes": [
{
"userId": "6304e42231ef2e7a4dec924d",
"_id": "63052dc7a1728d463769681b"
}
],
"__v": 0
},
{
"_id": "63070a03584ed0febe5b5a5f",
"status": "testing",
"posts": [],
"comments": [],
"likes": []
}
]
Flutter code:
Widget build(BuildContext context) {
return SizedBox(
height: 600,
child: FutureBuilder<List<Posts>>(
future: fetchPost(),
builder: ((context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.data[index].posts!.length,
itemBuilder: (context, postPosition) {
return Column(
children: [
Text(snapshot.data!.data[index].status),
ListView.builder(itemCount:snapshot.data!.data[index].posts.length,itemBuilder: (context, postPosition) {
return Column(children: [
Image.network(snapshot.data!.data[index].posts[postPosition].url)
],);
})
],
);
}
}
);
} else
return CircularProgressIndicator();
}),
),
);
}
The Posts class:
List<Posts> postsFromJson(String str) => List<Posts>.from(json.decode(str).map((x) => Posts.fromJson(x)));
String postsToJson(List<Posts> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Posts {
Posts({
this.id,
this.userId,
required this.posts,
this.status,
this.comments,
this.likes,
});
String? id;
String? userId;
List<Post> posts;
String? status;
List<Comment>? comments;
List<Like>? likes;
factory Posts.fromJson(Map<String, dynamic> json) => Posts(
id: json["_id"],
userId: json["userId"],
posts: List<Post>.from(json["posts"].map((x) => Post.fromJson(x))),
status: json["status"],
comments: List<Comment>.from(json["comments"].map((x) => Comment.fromJson(x))),
likes: List<Like>.from(json["likes"].map((x) => Like.fromJson(x))),
);
get length => null;
get data => null;
Map<String, dynamic> toJson() => {
"_id": id,
"userId": userId,
"posts": List<dynamic>.from(posts.map((x) => x.toJson())),
"status": status,
"comments": List<dynamic>.from(comments!.map((x) => x.toJson())),
"likes": List<dynamic>.from(likes!.map((x) => x.toJson())),
};
}
I am not sure how your IDE lets you compile the code while you added type in FutureBuilder.
The problem seems here. After snapshot.data you should not add another data variable. snapshot.data!.data should not be added.
itemCount: snapshot.data![index].posts!.length,
itemBuilder: (context, postPosition) {
return Column(
children: [
Text(snapshot.data![index].status),
ListView.builder(itemCount:snapshot.data![index].posts.length,itemBuilder: (context, postPosition) {
return Column(children: [
Image.network(snapshot.data![index].posts[postPosition].url)
],);
from your api response and you named it as "class:Post" it not a list. but Post.post is a list. Try to delete the list just like this, and print the result. you may have other error when casting it to your widget because the code will be a bit different. you can ask more if you face other difficulties
example:
PostModel postModelFromJson(String str) =>
postModel.fromJson(json.decode(str));
String postModelToJson(PostModel data) =>
json.encode(data.toJson());
Related
here is the list which i get from server
`
[
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-19",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-20",
"ShiftName": ""
},
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-21",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-21",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-21",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-21",
"ShiftName": ""
},
]
`
here you can see date is repeating and all i want to avoid date repetition on FE. you can see app Screenshot to get an idea which i get and which i want to achieve.
the data i get.
the data i want to be achieved
I tried to avoid date repetition and insert data on same list if date is same but all the time i get repetition date and data as you can see in image.
my code:
Expanded(
child: Consumer<EmployeeWageAccountsProvider>(
builder: (context, data, child) {
if (!data.isLoading) {
int length = data.getEmployeeAccountsData!.length;
if (data.getEmployeeAccountsData!.isNotEmpty) {
wageAccountsData = data.getEmployeeAccountsData!;
return ListView.builder(
itemCount: length,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (context, i) {
return WageAccountsCard(
date: Helper.formatStringDate(
wageAccountsData[i].date!),
balance: wageAccountsData[i].balance,
name: wageAccountsData[i].name,
shiftName: wageAccountsData[i].shiftName,
);
},
);
}
return noDataFound(context, 50);
}
return const WageAccountsShimmer();
}),
)
wageacount card
class WageAccountsCard extends StatelessWidget {
final String? date;
final String? balance;
final String? name;
final String? shiftName;
const WageAccountsCard(
{Key? key, this.date, this.name, this.balance,
this.shiftName})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(8),
decoration: CustomBoxDecoration.cardDecoration(context,
shadow: true),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
shiftName!,
style: wageTextStyle,
),
Text(
date.toString(),
style: wageTextStyle,
),
],
),
SizedBox(
height: Styles.height(context) * 0.01,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).wage_type,
style: cTextStyle,
),
Text(
S.of(context).balance,
style: cTextStyle,
),
],
),
const Divider(
color: Colors.black,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
name!,
style: cTextStyle,
),
Text(
balance.toString(),
style: cTextStyle,
),
],
),
SizedBox(
height: Styles.height(context) * 0.01,
),
],
),
);
}
}
model class
class EmployeeWageAccountsModel {
String? name;
String? balance;
String? date;
String? shiftName;
EmployeeWageAccountsModel({this.name, this.balance, this.date,
this.shiftName});
EmployeeWageAccountsModel.fromJson(Map<String, dynamic> json) {
name = json['Name'];
balance = json['Balance'];
date = json['Date'];
shiftName = json['ShiftName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['Name'] = name;
data['Balance'] = balance;
data['Date'] = date;
data['ShiftName'] = shiftName;
return data;
}
}
parsing data from API
List<EmployeeWageAccountsModel> employeeWageAccountsList =
List<EmployeeWageAccountsModel>.from(
employeeWageAccountsResponse.map((model) =>
EmployeeWageAccountsModel.fromJson(model)));
You can use collection package and group your data with date :
var grouped = groupBy(xdata, (Map value) => value['Date']);
then you can make list base on grouped. Note that xdata is your list that you get from server.
grouped is map and contain this:
{
2022-10-19: [
{Name: 101 Working hours, Balance: 8.00, Date: 2022-10-19, ShiftName: AU128},
{Name: 102 Bonus pay, Balance: 3:48, Date: 2022-10-19, ShiftName: },
{Name: 110 Split Shift, Balance: 1:00, Date: 2022-10-19, ShiftName: },
{Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-19, ShiftName: }
],
2022-10-20: [
{Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-20, ShiftName: }
],
2022-10-21: [
{Name: 101 Working hours, Balance: 8.00, Date: 2022-10-21, ShiftName: AU128},
{Name: 102 Bonus pay, Balance: 3:48, Date: 2022-10-21, ShiftName: },
{Name: 110 Split Shift, Balance: 1:00, Date: 2022-10-21, ShiftName: },
{Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-21, ShiftName: }
]
}
also change your model class to this:
class EmployeeWageAccountsModel {
String? date;
String? shiftName;
List<Wage>? wages;
EmployeeWageAccountsModel({this.wages, this.date, this.shiftName});
EmployeeWageAccountsModel.fromJson(Map<String, dynamic> json) {
wages = (json.values.first as List).map((e) => Wage.fromJson(e)).toList();
date = json.keys.first;
shiftName = json.values.first[0]['ShiftName'];
}
...
}
class Wage {
String? name;
String? balance;
Wage({this.name, this.balance});
Wage.fromJson(Map<String, dynamic> json) {
name = json['Name'];
balance = json['Balance'];
}
}
and parse it like this:
var grouped =
groupBy(employeeWageAccountsResponse, (Map value) => value['Date']);
List<EmployeeWageAccountsModel> employeeWageAccountsList =
List<EmployeeWageAccountsModel>.from(grouped.entries
.map((e) => EmployeeWageAccountsModel.fromJson({e.key: e.value})));
then change your list item to this:
class WageAccountsCard extends StatelessWidget {
final String? date;
final String? shiftName;
final List<Wage>? wages;
const WageAccountsCard(
{Key? key,
this.date,
this.shiftName,
this.wages})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(8),
decoration: CustomBoxDecoration.cardDecoration(context, shadow: true),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
shiftName!,
style: wageTextStyle,
),
Text(
date.toString(),
style: wageTextStyle,
),
],
),
SizedBox(
height: Styles.height(context) * 0.01,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).wage_type,
style: cTextStyle,
),
Text(
S.of(context).balance,
style: cTextStyle,
),
],
),
const Divider(
color: Colors.black,
),
ListView.builder(
itemBuilder: (context, index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
wages![index].name!,
style: cTextStyle,
),
Text(
wages![index].balance.toString(),
style: cTextStyle,
),
],
);
},
itemCount: wages!.length,
shrinkWrap: true,
),
SizedBox(
height: Styles.height(context) * 0.01,
),
],
),
);
}
}
You could build a Map<String, List<Map<String, String>>> using forEach where key would be unique dates and values would be the list of objects.
void main() {
const data = [
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-19",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-20",
"ShiftName": ""
},
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-21",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-21",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-21",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-21",
"ShiftName": ""
},
];
final Map<String, List<Map<String, String>>> dates = {};
data.forEach((e) {
if (dates.containsKey(e['Date'])) {
dates[e['Date']]?.add(e);
} else {
dates[e['Date']!] = [e];
}
});
dates.forEach((e, k) {
print("$e\n${k.join("\n")}");
print('\n');
});
}
One way to achive this is to use equatable package so you can compare objects
class Person extends Equatable {
const Person(this.name,this.date);
final String name;
final DateTime date;
#override
List<Object> get props => [name,date];
}
now you can compare two objects of type Person and with a simple function you can remove the repetition .
example:
List<Person> yourList = [];
// before you add new object check if its already available
int getEqual(Person onePerson)=>yourList.indexWhere((element)=> element == onePerson);
if (getEqual(onePerson) != -1){
// object was not found in the list
yourList.add(onePerson);
}
// then sort your list by date using sort method
yourList.sort((a,b) => a.date.compareTo(b.date));
I have a list of objects called options
List<Map<String, dynamic>> options = [
{"name": "usually", "value": 100, "id": 1},
{"name": "sometimes", "value": 70, "id": 2},
{"name": "rarely", "value": 40, "id": 3},
{"name": "never", "value": 0, "id": 4},
];
also, I have another list called questions
late List<Map<String, dynamic>> questions = [
{"id": 1, "name": "Q1", "options": options},
{"id": 2, "name": "Q2", "options": options},
{"id": 3, "name": "Q3", "options": options},
{"id": 4, "name": "Q4", "options": options},
{"id": 5, "name": "Q5", "options": options},
{"id": 6, "name": "Q6", "options": options},
{"id": 7, "name": "Q7", "options": options},
{"id": 8, "name": "Q8", "options": options},
{"id": 9, "name": "Q9", "options": options},
{"id": 10, "name": "Q10", "options": options},
{"id": 11, "name": "Q11", "options": options},
{"id": 12, "name": "Q12", "options": options},
{"id": 13, "name": "Q13", "options": options},
{"id": 14, "name": "Q14", "options": options},
];
How can I perform multi select for each question which allow the user select one option per one question ?
Code for the UI
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.separated(
itemCount: questions.length,
separatorBuilder: (context, index) {
return Divider();
},
itemBuilder: (context, index) {
return ListTile(
title: Text(questions[index]['name']),
subtitle: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: List.generate(
questions[index]['options'].length,
(lowerIndex) => Padding(
padding: const EdgeInsets.all(5.0),
child: InkWell(
onTap: () =>
ChoicesHelper.addValueToSelectedValuesList(
questions[index]['options'][lowerIndex]
['value'],
questions[index]['id'],
questions[index]['options'][lowerIndex]
['id']),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 20,
height: 20,
color: Colors.grey ),
SizedBox(width: 1),
Text(questions[index]['options']
[lowerIndex]['name'])
])))),
),
);
},
),
),
ElevatedButton(
onPressed: () {
ChoicesHelper.calculateResults();
},
child: Text('Calculate')),
],
),
);
Code for the methods in another class
static List<int> selectedValues = [];
static List<Map<String, dynamic>> answeredQuestionsIds = [];
static void addValueToSelectedValuesList(int value, int questionID, optionID) {
selectedValues.add(value);
answeredQuestionsIds.add({"questionID": questionID,"optionID": optionID});
}
As you can see, I just need to select one box for each question and turn it's color to red, but the problem is that all the row for single question is selected and all the boxes turns into red.
How can I select only one option for one question ?
try this:
class ListWidget extends StatefulWidget {
const ListWidget({Key? key}) : super(key: key);
#override
State<ListWidget> createState() => _ListWidgetState();
}
class _ListWidgetState extends State<ListWidget> {
late List<Map<String, dynamic>> questions = [
{"id": 1, "name": "Q1", "options": options},
{"id": 2, "name": "Q2", "options": options},
{"id": 3, "name": "Q3", "options": options},
{"id": 4, "name": "Q4", "options": options},
{"id": 5, "name": "Q5", "options": options},
{"id": 6, "name": "Q6", "options": options},
{"id": 7, "name": "Q7", "options": options},
{"id": 8, "name": "Q8", "options": options},
{"id": 9, "name": "Q9", "options": options},
{"id": 10, "name": "Q10", "options": options},
{"id": 11, "name": "Q11", "options": options},
{"id": 12, "name": "Q12", "options": options},
{"id": 13, "name": "Q13", "options": options},
{"id": 14, "name": "Q14", "options": options},
];
List<Map<String, dynamic>> options = [
{"name": "usually", "value": 100, "id": 1},
{"name": "sometimes", "value": 70, "id": 2},
{"name": "rarely", "value": 40, "id": 3},
{"name": "never", "value": 0, "id": 4},
];
List<Question> _questions = [];
#override
void initState() {
super.initState();
_questions = questions.map((e) => Question.fromJson(e)).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: _questions.length,
itemBuilder: (context, index) {
return _buildQuestion(
_questions[index],
(int? value) {
print("cvalue = ${value}");
var q = Question(
name: _questions[index].name,
id: _questions[index].id,
options: _questions[index].options,
groupValue: value!);
setState(() {
_questions[index] = q;
});
},
);
}),
);
}
_buildQuestion(Question question, Function(int?)? onChanged) {
return Row(
children: [
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[0].name),
value: question.options[0].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[1].name),
value: question.options[1].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[2].name),
value: question.options[2].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[3].name),
value: question.options[3].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
],
);
}
}
class Question {
final String name;
final int id;
final List<OptionModel> options;
final int groupValue;
Question(
{required this.name,
required this.id,
required this.options,
required this.groupValue});
static Question fromJson(Map<String, dynamic> json) {
return Question(
name: json['name'],
id: json['id'],
groupValue: -1,
options: (json['options'] as List)
.map((e) => OptionModel.fromJson(e))
.toList());
}
}
class OptionModel {
final String name;
final int value;
final int id;
OptionModel({required this.name, required this.id, required this.value});
static OptionModel fromJson(Map<String, dynamic> json) {
return OptionModel(
name: json['name'], id: json['id'], value: json['value']);
}
}
I have following List:
[
{
"ID": "1",
"ParentID": "0",
"CategoryName": "FourWheeler",
"Children": [
{
"ID": "9",
"ParentID": "1",
"CategoryName": "Jeep",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "10",
"ParentID": "1",
"CategoryName": "Taxi",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "11",
"ParentID": "1",
"CategoryName": "Car",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "12",
"ParentID": "1",
"CategoryName": "Van",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "13",
"ParentID": "1",
"CategoryName": "Other",
"ParentCategoryName": "FourWheeler"
}]
},
{
"ID": "2",
"ParentID": "0",
"CategoryName": "Boat",
"Children": [
{
"ID": "14",
"ParentID": "2",
"CategoryName": "Motorboat",
"ParentCategoryName": "Boat"
},
{
"ID": "15",
"ParentID": "2",
"CategoryName": "Sailingboat",
"ParentCategoryName": "Boat"
},
{
"ID": "16",
"ParentID": "2",
"CategoryName": "SteamBoat",
"ParentCategoryName": "Boat"
},
{
"ID": "17",
"ParentID": "2",
"CategoryName": "Other",
"ParentCategoryName": "Boat"
}]
}
]
I need to populate ListView on the basis of this list. We should have listview populated such a way that there are headers and each headers will have their respective items.
For Example, ListView should look something like,
**FourWheeler**
Jeep
Taxi
Car
Van
Other
**Boat**
Motorboat
Sailingboat
Steamboat
Other
For simple list like this:
List Fruits = ['Apple','Orange','Kiwi','Avocado'];
I would have done like this
Fruits.map<Widget>((fruit)=>Container(child: Text(fruit))).toList();
But I don't know how to deal with the scenario I have. Any help will be appreciated. Thanks
This is one way you can populate a ListView as you have described:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [
for (final header in data) ...[
ListTile(title: Text('**${header['CategoryName']}**')),
for (final item in header['Children'] ?? [])
ListTile(title: Text(' ${item['CategoryName']}')),
],
],
),
),
);
}
}
const List<Map<String, dynamic>> data = [
{
"ID": "1",
"ParentID": "0",
"CategoryName": "FourWheeler",
"Children": [
{
"ID": "9",
"ParentID": "1",
"CategoryName": "Jeep",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "10",
"ParentID": "1",
"CategoryName": "Taxi",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "11",
"ParentID": "1",
"CategoryName": "Car",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "12",
"ParentID": "1",
"CategoryName": "Van",
"ParentCategoryName": "FourWheeler"
},
{
"ID": "13",
"ParentID": "1",
"CategoryName": "Other",
"ParentCategoryName": "FourWheeler"
}
]
},
{
"ID": "2",
"ParentID": "0",
"CategoryName": "Boat",
"Children": [
{
"ID": "14",
"ParentID": "2",
"CategoryName": "Motorboat",
"ParentCategoryName": "Boat"
},
{
"ID": "15",
"ParentID": "2",
"CategoryName": "Sailingboat",
"ParentCategoryName": "Boat"
},
{
"ID": "16",
"ParentID": "2",
"CategoryName": "SteamBoat",
"ParentCategoryName": "Boat"
},
{
"ID": "17",
"ParentID": "2",
"CategoryName": "Other",
"ParentCategoryName": "Boat"
}
]
}
];
You could also group each header with something like an ExpansionTile:
ListView(
children: [
for (final header in data)
ExpansionTile(
title: Text('**${header['CategoryName']}**'),
children: [
for (final item in header['Children'] ?? [])
ListTile(title: Text(' ${item['CategoryName']}')),
],
),
],
),
my json:
{
"result": {
"name": "json1",
"pages": [{
"zones": [{
"title": "title1"
},
{
"title": "title2"
}],
"id": 4
},
{
"zones": [{
"title": "title3"
},
{
"title": "title4"
}],
"id": 12
}],
"creatorUserName": "admin",
"id": 2
}
}
how can I build an algorithm in dart flutter so I can get all pages title against Ids?
if (id = 12) zones print -> Text(title3), Text(title4),
else if empty zones print -> Text(title1), Text(title2), zones -> Text(title3), Text(title4),
my example code:
List post = snapshot.data["result"]["pages"];
List post = pagesArray;
children: post.map((post) => Container(
child: Center(child: Text(post.title]),)
)).toList(),
You will need something like this:
void initState() {
result = getResponse();
}
Widget getTextWidgets(var array)
{
List<Widget> list = new List<Widget>();
for(var i = 0; i < array.length; i++){
list.add(new Text(array[i]["title"], style: TextStyle(color: Colors.black)));
}
return new Row(children: list);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(
'Sample',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
backgroundColor: Colors.black,
),
body: Container(
padding: EdgeInsets.all(5),
child: ListView.builder(
shrinkWrap: true,
itemCount: result.length,
itemBuilder: (BuildContext context, int index) {
if(result[index]["id"] == 4){
getTextWidgets(result[index]["zones"]);
}
else if(result[index]["id"] == 12){
getTextWidgets(result[index]["zones"]);
}
},
),
))
);
}
getResponse() {
var response = '{ "result": { "name": "json1", "pages": [{ "zones": [{ "title": "title1" }, { "title": "title2" }], "id": 4 }, { "zones": [{"title": "title3"}, {"title": "title4"}], "id": 12 }], "creatorUserName": "admin", "id": 2 }}';
print('Respone ${response}');
var value = json.decode(response);
var pages = value["result"]["pages"];
return pages;
}
I have json:
{
"name": "Test",
"groupsCount": 5,
"groups": [
{
"name": "William Cooper",
"number": 1,
"linesCount": 2,
"lines": [
{
"name": "Brittany Ramirez",
"number": 1,
"placesCount": 15,
"places": [
{
"name": "Elizabeth Welch",
"number": 1
},
{
"name": "Bradley Pugh",
"number": 2
},
{
"name": "Noah Johnson",
"number": 3
},
{
"name": "Laura Cox",
"number": 4
},
{
"name": "Tiffany Hatfield",
"number": 5
},
{
"name": "Patricia Hayes",
"number": 6
},
{
"name": "Kevin Jenkins MD",
"number": 7
},
{
"name": "Brent Wilkins",
"number": 8
},
{
"name": "Mr. Bruce Hart",
"number": 9
},
{
"name": "Courtney Newman",
"number": 10
},
{
"name": "Dawn Campbell",
"number": 11
},
{
"name": "Ashley Scott",
"number": 12
},
{
"name": "Robert Becker",
"number": 13
},
{
"name": "Kevin Williams",
"number": 14
},
{
"name": "Elizabeth Davidson",
"number": 15
}
]
},
{
"name": "Carol Watson",
"number": 2,
"placesCount": 12,
"places": [
{
"name": "Kathleen Jones",
"number": 16
},
{
"name": "Robin Smith",
"number": 17
},
{
"name": "David Johnson",
"number": 18
},
{
"name": "Richard Boyd",
"number": 19
},
{
"name": "Mason Randolph",
"number": 20
},
{
"name": "James Bernard",
"number": 21
},
{
"name": "Timothy Miller",
"number": 22
},
{
"name": "Thomas Stone",
"number": 23
},
{
"name": "Steven Jones",
"number": 24
},
{
"name": "William Hernandez",
"number": 25
},
{
"name": "Joy Duarte",
"number": 26
},
{
"name": "Justin Anderson",
"number": 27
}
]
}
]
},
{
"name": "Sarah Jordan",
"number": 2,
"linesCount": 1,
"lines": [
{
"name": "Lacey Estrada",
"number": 3,
"placesCount": 15,
"places": [
{
"name": "Madison Smith",
"number": 28
},
{
"name": "Kathleen Wells",
"number": 29
},
{
"name": "Dale Gross",
"number": 30
},
{
"name": "Brandy Cabrera",
"number": 31
},
{
"name": "Ashley Torres",
"number": 32
},
{
"name": "Ralph Long",
"number": 33
},
{
"name": "Christopher Walker",
"number": 34
},
{
"name": "Kimberly Moore",
"number": 35
},
{
"name": "Andrea Ortiz",
"number": 36
},
{
"name": "Heidi Todd",
"number": 37
},
{
"name": "Austin Wang",
"number": 38
},
{
"name": "Bryan Adams",
"number": 39
},
{
"name": "Jeffrey Alvarado",
"number": 40
},
{
"name": "Richard Morrison",
"number": 41
},
{
"name": "Jennifer Hodge",
"number": 42
}
]
}
]
},
{
"name": "Tiffany Lynch",
"number": 3,
"linesCount": 1,
"lines": [
{
"name": "Kimberly Howe DVM",
"number": 4,
"placesCount": 15,
"places": [
{
"name": "Nancy King",
"number": 43
},
{
"name": "Angela Cabrera",
"number": 44
},
{
"name": "Elizabeth Mack",
"number": 45
},
{
"name": "Shelly Riggs",
"number": 46
},
{
"name": "Jeremy French",
"number": 47
},
{
"name": "Deborah Myers",
"number": 48
},
{
"name": "Robert Kramer",
"number": 49
},
{
"name": "Brian Cunningham MD",
"number": 50
},
{
"name": "Christopher Brown",
"number": 51
},
{
"name": "Kathryn James",
"number": 52
},
{
"name": "Cassandra Martinez",
"number": 53
},
{
"name": "Nathan Long",
"number": 54
},
{
"name": "Molly Wilson",
"number": 55
},
{
"name": "Matthew Reilly",
"number": 56
},
{
"name": "Erin Maddox",
"number": 57
}
]
}
]
},
{
"name": "Christopher Martin",
"number": 4,
"linesCount": 1,
"lines": [
{
"name": "Gina Brewer",
"number": 5,
"placesCount": 13,
"places": [
{
"name": "Dennis Owens",
"number": 58
},
{
"name": "Rebecca Caldwell",
"number": 59
},
{
"name": "James Mckinney",
"number": 60
},
{
"name": "Donna Lee",
"number": 61
},
{
"name": "Michelle Martinez",
"number": 62
},
{
"name": "Jennifer Davis",
"number": 63
},
{
"name": "Shawn Moore",
"number": 64
},
{
"name": "Jeremiah Reilly",
"number": 65
},
{
"name": "Bruce Mendoza",
"number": 66
},
{
"name": "Juan Weaver",
"number": 67
},
{
"name": "Brian Bates",
"number": 68
},
{
"name": "Caitlin Jenkins",
"number": 69
},
{
"name": "Rachel Thomas",
"number": 70
}
]
}
]
},
{
"name": "Michelle Thompson",
"number": 5,
"linesCount": 2,
"lines": [
{
"name": "Kathleen Hall",
"number": 6,
"placesCount": 12,
"places": [
{
"name": "Jennifer Vaughan",
"number": 71
},
{
"name": "Glenn Mayer",
"number": 72
},
{
"name": "Allison Coleman",
"number": 73
},
{
"name": "Brittany Harris",
"number": 74
},
{
"name": "John Mccullough",
"number": 75
},
{
"name": "James Curtis",
"number": 76
},
{
"name": "John Smith",
"number": 77
},
{
"name": "Alison Morales",
"number": 78
},
{
"name": "Matthew Jones",
"number": 79
},
{
"name": "Jessica Watson",
"number": 80
},
{
"name": "Yvonne Anderson",
"number": 81
},
{
"name": "David Price",
"number": 82
}
]
},
{
"name": "Sarah White",
"number": 7,
"placesCount": 10,
"places": [
{
"name": "Kathleen Owen",
"number": 83
},
{
"name": "Amy Strickland",
"number": 84
},
{
"name": "James Collier",
"number": 85
},
{
"name": "Keith Smith Jr.",
"number": 86
},
{
"name": "Christopher York",
"number": 87
},
{
"name": "Patricia Todd",
"number": 88
},
{
"name": "Matthew Harris",
"number": 89
},
{
"name": "Betty Mckee",
"number": 90
},
{
"name": "Kayla Hahn",
"number": 91
},
{
"name": "Craig Duncan",
"number": 92
}
]
}
]
}
]
}
and I want to build view using lines and places. So if group have 2 lines and each line has 15 places I want to build something like that:
...............
...............
This is my code:
import 'package:flutter/material.dart';
import 'package:parking/models/groups.dart';
import 'package:parking/models/parking_group.dart';
import 'package:parking/services/parking_service.dart';
class ParkingDetailsScreen extends StatefulWidget {
#override
createState() => ParkingDetailsState();
}
class ParkingDetailsState extends State<ParkingDetailsScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Parking Details"),
),
body: FutureBuilder<ParkingGroup>(
future: ParkingService.getParking(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? _buildParking(snapshot.data)
: Center(child: CircularProgressIndicator());
},
)
);
}
}
final _parkings = <Groups>[];
Widget _buildParking(ParkingGroup parkingGroup) {
_parkings.clear();
_parkings.addAll(parkingGroup.groups);
return ListView.builder(
itemCount: _parkings.length,
itemBuilder: (context, index) {
return _buildRow(_parkings[index], context);
},
);
}
Widget _buildRow(Groups parkingGroup, BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
child: Row(
children: _buildRowList(parkingGroup),
),
);
}
List<Widget> _buildRowList(Groups parkingGroup) {
List<Widget> places = [];
for (var place in parkingGroup.lines) {
for (var placeLine in place.places) {
places.add(_buildPlace(placeLine));
}
}
return places;
}
Widget _buildPlace(place) {
return Container(
padding: const EdgeInsets.only(bottom: 8.0),
child:
SizedBox(
height: 5,
width: 5,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: Colors.blueAccent),
)),
),
);
}
//
//Widget _buildRow(Groups parkingGroup, BuildContext context) {
// return Container(
// padding: const EdgeInsets.all(16.0),
// child: Row(
// children: [
// Container(
// padding: const EdgeInsets.only(bottom: 8.0),
// child:
// SizedBox(
// height: 30,
// width: 30,
// child: DecoratedBox(
// decoration: BoxDecoration(
// border: Border.all(color: Colors.blueAccent),
// )),
// ),
// ),
// ],
// ),
// );
//}
but for this code I see all places in one line:
..............................
instead of
...............
...............
What I need to change in my code to get good result?
You need to change your _buildRow() and _buildRowList() methods:
Widget _buildRow(Groups parkingGroup, BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
child: Column( // As you expect multiple lines you need a column not a row
children: _buildRowList(parkingGroup),
),
);
}
List<Widget> _buildRowList(Groups parkingGroup) {
List<Widget> lines = []; // this will hold Rows according to available lines
for (var line in parkingGroup.lines) {
List<Widget> placesForLine = [] // this will hold the places for each line
for (var placeLine in line.places) {
placesForLine.add(_buildPlace(placeLine));
}
lines.add(Row(children: placesForLine));
}
return lines;
}