How can i make a grouped json data to a list in flutter? I have grouped jsondata.
final data = body.data['items']; // gets json data here
final uniqueSetOfChapters =groupBy(data, (dynamic obj) => obj['chapter']); // grouping it here
uniqueSetOfChapters.forEach((key, value) {
print('$key : : $value');
});
As a result i'm getting a grouped data as follows
Measurement : : [
{sequenceno: 193, _id: 5dc1, chapter: Measurement, title: Measuring Length, package_description: Let us learn about ‘Measurement using Length'., age_level: [99, 6], pkg_sequence: 251},
{sequenceno: 193, _id: 5d99, chapter: Measurement, title: Measuring Weight, package_description: Let us learn about ‘Measuring Weight’., age_level: [99, 6], pkg_sequence: 251},
{sequenceno: 1933, _id: 5d99, chapter: Measurement, title: Measuring Capacity, package_description: This module introduces how to measure the capacity of a container., age_level: [99, 6], pkg_sequence: 251},
{sequenceno: 193, _id: 5ef4, chapter: Measurement, title: Revision - Measuring Length, package_description: In this module, we will revise about measurement using length., age_level: [99, 6], pkg_sequence: 2514],
Data Handling : : [
{sequenceno: 193, _id: 5e23, chapter: Data Handling, title: Bar chart, package_description: This module helps to understand how to represent data., age_level: [99, 6], pkg_sequence: 2511},
{sequenceno: 193, _id: 5f15, chapter: Data Handling, title: Revision - Bar chart, package_description: Let us revise some activities on presenting data in the form of graphs., age_level: [99, 6], pkg_sequence: 251}
]
How can i make this grouped data to a list? I have created a model for this as follows
class Titles{
String? sequenceno;
String? id;
String? chapter;
String? title;
String? packageDescription;
String? ageLevel;
String? pkgSequence;
String? packagescors;
Titles({
this.sequenceno,
this.id,
this.chapter,
this.title,
this.packageDescription,
this.ageLevel,
this.pkgSequence,
this.packagescors,
});
factory Titles.fromMap(Map<String, dynamic> map) {
return Titles(
sequenceno: map['sequenceno'],
id: map["_id"],
chapter: map['chapter'],
title: map['title'],
packageDescription: map['package_description'],
ageLevel: map['age_level'],
pkgSequence: map['pkg_sequence'],
// packagescors: map['packagescors'],
);
}
Map<String, dynamic> toMap(){
var map = <String,dynamic>{
"sequenceno": sequenceno,
"_id": id,
"chapter": chapter,
"title": title,
"package_description": packageDescription,
"age_level": ageLevel,
"pkg_sequence": pkgSequence,
"packagescors": packagescors,
};
return map;
}}
How can I add this grouped data to a list??
You can try with grouped_list package: https://pub.dev/packages/grouped_list
Output sample:
Code snippet:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: Appbar(title: "Titles",),
body: Container(
child: GroupedListView<dynamic, String>(
elements: measurements,
groupBy: (element) => element.chapter,
groupComparator: (value1, value2) => value2.compareTo(value1),
order: GroupedListOrder.ASC,
useStickyGroupSeparators: true,
groupSeparatorBuilder: (String value) => Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
value,
textAlign: TextAlign.start,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor),
),
),
itemBuilder: (c, element) {
return Column(
children: <Widget>[
ListTile(
title: Text(
element.title,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
subtitle: Text(
element.packageDescription,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, ),
),
onTap: () => {},
),
],
);
},
),
),
);
}
I assume you want to convert a list of map :
measurement = [{'sequenceno': '193', '_id': '5d99',... }, {'sequenceno': '1933', '_id': '5d99', ..}, {...}, {...}]
into a list of Titles.
What you can do is :
var titleList = [for (var map in measurement) Titles.fromMap(map) ];
For information, you don't need to use a Factory constructor. The following constructor should work as well :
Titles.fromMap(Map<String, dynamic> map) :
sequenceno = map['sequenceno'],
id = map["_id"],
chapter = map['chapter'],
title = map['title'],
packageDescription = map['package_description'],
ageLevel = map['age_level'],
pkgSequence = map['pkg_sequence'];
Related
In my app, I am using Hive to store data locally. My box is called "favorites" and I managed to store the data in the box with this code:
_save() {
final recipeData = Recipe(
title: widget.recipeDocument['title'],
id: widget.recipeDocument['id'],
price: widget.recipeDocument['price'],
url: widget.recipeDocument['url'],
servings: widget.recipeDocument['servings'],
calories: widget.recipeDocument['calories'],
carbs: widget.recipeDocument['carbs'],
protein: widget.recipeDocument['protein'],
fat: widget.recipeDocument['fat'],
ingredients: widget.recipeDocument['ingredients'],
instructions: widget.recipeDocument['instructions'],);
print('Generated recipeData final $recipeData');
String json =jsonEncode(recipeData);
print('Generated json $json');
final box = Hive.box('favorites'); //<- get an already opened box, no await necessary here
// save recipe information
final Id = widget.recipeDocument['id'];
box.put(Id,json);
On my favorite page, I want to display the title and price in a ListView.
I get data from the box like this:
body: ValueListenableBuilder(
valueListenable: Hive.box('favorites').listenable(),
builder: (context, box, child) {
var box = Hive.box('favorites');
List post = List.from(box.values);
print('List is $post');
The list contains the following:
[
{
"url": "http for URL",
"title": "Bananabread",
"price": "0,77",
"calories": "234",
"carbs": "12",
"fat": "1",
"id": "1",
"protein": "34",
"servings": 1,
"ingredients": [
"2 bananas",
"30 g flour",
"2 eggs"
],
"instructions": [
"1. mix banana and egg.",
"2. add flour.",
"3. bake and enjoy"
]
}
]
Let's say I only want to retrieve the title and price from that. How do I do so?
I tried this:
return ListView(
padding: const EdgeInsets.all(16),
children: <Widget>[
Text('This shows favorites'),
...post.map(
(p) => ListTile(
title: Text(p[1]),
trailing: Text(p[2]),
),
),
],
);
But this only returns "U" and "R"...so the letters from the word URL, I guess?
Try this. You are accessing the key of the map in the list.
return ListView(
padding: const EdgeInsets.all(16),
children: <Widget>[
Text('This shows favorites'),
...post.map(
(p) => ListTile(
title: Text(p['url'].toString()),
trailing: Text(p['title'].toString()),
),
),
],
);
I have a recipe app. Users can save recipes in a weekly planner (using Hive). The date is saved in this format: 2022-09-08 00:00:00.000 (= user wants to cook a recipe on September 8th).
On my planner page I want to display the scheduled recipes. I am using Grouped Listview for that to group my recipes by date. I also need to be able to sort the listed recipes (the recipe a user wants to cook tomorrow should be on top of the list and after that comes the day after tomorrow and so on).
When I use the date in the format 2022-09-08 00:00:00.000 I am able to group the recipes and bring them in the correct order. But I don't like the format. I rather want to display (Thursday, 8. September). So I tried the following:
return Column(
children: [
SizedBox(height: 24),
Expanded(
child: GroupedListView<dynamic,String>(
elements: jsonList,
groupBy: (element) {
element['plannerdate'];
String date = DateFormat('dd. MMMM, EEEE').format(element['plannerdate']);
return date;
},
groupSeparatorBuilder: (value) => Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.white,
child: Text(value),
),
itemBuilder: (context, element) => Card(
child: Row(
children: <Widget>[
CircleAvatar(
radius: 70,
backgroundImage: NetworkImage(element['url'],
),
),
But with that my date gets converted to a string. The format looks nice, but this way the order is messed up. Recipes from 31 August are at the end of the list while recipes from 2 September are on top. Can someone help?
Edit:
My recipe class is:
//Recipe Model class for Hive Recipes Favorite Box
import 'package:hive/hive.dart';
part 'recipe_data_model.g.dart';
#HiveType(typeId: 0)
class RecipeModelData extends HiveObject {
#HiveField(0)
String? id;
#HiveField(1)
String? title;
#HiveField(2)
String? url;
#HiveField(3)
String? price;
#HiveField(4)
int? servings;
#HiveField(5)
String? calories;
#HiveField(6)
String? carbs;
#HiveField(7)
String? protein;
#HiveField(8)
String? fat;
#HiveField(9)
List? ingredients;
#HiveField(10)
List? instructions;
#HiveField(11)
DateTime? plannerdate;
Map<String, dynamic> toJson() =>
{
'id': id,
'title': title,
'url': url,
'price': price,
'servings': servings,
'calories': calories,
'carbs': carbs,
'protein': protein,
'fat': fat,
'ingredients': ingredients,
'instructions': instructions,
'plannerdate': plannerdate,
};
}
And the jsonList with all the recipe details is:
flutter: jsonList: [{id: 1, title: Bananenbrott, url: https://firebasestorage.xyz, price: 0,77, servings: 1, calories: 234, carbs: 12, protein: 34, fat: 1, ingredients: [2 Bananen, 30 g Mehl, 2 Eier], instructions: [1. Zunächst das Ei mit den zerdrückten Bananen in einer Schüssel vermischen., 2. Dann das Mehl sieben und hinzufügen., 3. Alles umrühren und fertig], plannerdate: 2022-08-31 00:00:00.000}, {id: 2, title: Gemüsecurry, url: https://firebasestorage.xys, price: 1,20, servings: 1, calories: 450, carbs: 42, protein: 22, fat: 9, ingredients: [2 Dosen Kokosmilch, 400 ml Wasser, 1 Karotte], instructions: [1. Das Wasser zum Kochen bringen und dann mit der Kokosmilch vermischen., 2. Die Karotte schälen und dann mit dazu geben, 3. Alles miteinander gut kochen und verspeisen.]<…>
You could display the formatted date in the groupSeparatorBuilder instead of in groupBy.
Example
return Column(
children: [
SizedBox(height: 24),
Expanded(
child: GroupedListView<dynamic,DateTime>(
elements: jsonList,
groupBy: (element) => element['plannerdate'],
groupSeparatorBuilder: (value) {
String date = DateFormat('dd. MMMM, EEEE').format(value);
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.white,
child: Text(date),
)},
itemBuilder: (context, element) => Card(
child: Row(
children: <Widget>[
CircleAvatar(
radius: 70,
backgroundImage: NetworkImage(element['url'],
),
),
I am a beginner in Flutter programming, and I am in the learning phase. I am trying to create only the UI of a list using dummy data for the item which can be bought very frequently by the customer. for example a customer has bought pencils very often, and a pen not so often, so the pencils will be on the top of the list and pen will be below the pencils and so on...! Below is the image which I wanted to create
waiting for your suggestions. thanks
in short frequently bought items are on the top of the list.
List dataItems = [
{"product": "pencil", "frequency" :4},
{"product": "pencil2", "frequency" :4},
{"product": "pencil4", "frequency" :4}
];
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(children: const[
Text("No"),
SizedBox(width: 16),
Text("Product"),
SizedBox(width: 16),
Text("Frequency"),
]),
Expanded(
child: ListView.builder(
itemCount: 2,
itemBuilder: (ctx, index){
return Row(children: [
Text(index.toString()),
SizedBox(width: 16),
Text(dataItems[index]["product"]),
SizedBox(width: 16),
Text(dataItems[index]["frequency"].toString()),
Spacer(),
MaterialButton(onPressed: (){}, child: Text("Deliver"), ),
MaterialButton(onPressed: (){}, child: Text("Self Pickup"), )
]);
}
))
],
),
You can use an implement like this, try using row methods in order to divide items inside the row.
i hope it little help
class DemoWork extends StatefulWidget {
const DemoWork({Key? key}) : super(key: key);
#override
State<DemoWork> createState() => _DemoWorkState();
}
class _DemoWorkState extends State<DemoWork> {
List product=[
{'product':'pencil', 'frequency': 10}, {'product':'pen','frequency':24}, {'product':'notebook','frequency':12}, {'product':'markers','frequency':2}, {'product':'erasers','frequency':21}
];
Iterable<dynamic>? data;
#override
void initState() {
// TODO: implement initState
super.initState();
product.sort((a, b) {
return a['frequency'].compareTo(b['frequency']);
});
//output is {product: pen, frequency: 24}, {product: erasers, frequency: 21}, {product: notebook, frequency: 12}, {product: pencil, frequency: 10}, {product: markers, frequency: 2}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 5,
shrinkWrap: true,
reverse: true,// because list show low to high so reverse use to show high to low value and shrink wrap adjust its size use as you want to show and adjust it
itemBuilder: (context, index) {
return ListTile(
leading:Text(index.toString()) ,
title: Text(product[index]['product']),
trailing: Text(product[index]['frequency'].toString()),
);
}),
);
}
}
You can counting time user buying stuffs and use it to sort the list. Of course u need to build your widget for yourself, i will only suggest the logic.
Example
void main() {
var stuffs = ['ball', 'pen', 'pencil', 'glass']; // your stuff here
var history = ['pencil', 'pencil', 'glass', 'pen', 'glass', 'pencil']; // your buy history here, you can add more if you want
print('stuff will dispaly in order: $stuffs');
stuffs.sort((a,b) => history.timesOf(b).compareTo(history.timesOf(a))); // Function that sort the list by 'buy times' of user store in `history` variable
print('stuff will dispaly in order when using history: $stuffs');
}
extension HistoryCheck on List<String> {
int timesOf(String name) => where((_) => _ == name).length;
}
// ===== Result =====
stuff will dispaly in order: [ball, pen, pencil, glass]
stuff will dispaly in order when using history: [pencil, glass, pen, ball]
Suppose you have the following product model and list of products:
// Product Model
class Product {
final int serialNo;
final String name;
final int frequency;
const Product(this.serialNo, this.name, this.frequency);
}
// List of products
List<Product> data = [
Product(1, 'Pencil', 35),
Product(2, 'Pen', 30),
Product(3, 'Notebook', 25),
];
Just before showing this list of products in a list view, you can sort it based on the frequency as shown below:
data.sort((a, b) => b.frequency.compareTo(a.frequency));
final List <Category> _categories = [
Category(
id: '1',
title: 'Poultry',
food: [
Food(
id: '1',
title: 'Chicken 1',
description: "qdwwqd",
price: 500.00,
image: "https://jb-ph-cdn.tillster.com/menu-images/prod/45df1872-c7f7-4b3d-baa9-1b0c4f56a5cc.png",
choice: [],
),
],
),
Category(
id: '2',
title: 'Vegetables',
food: [
Food(
id: '1',
title: 'Cabbage',
description: "qdwwqd",
price: 500.00,
image: "https://jb-ph-cdn.tillster.com/menu-images/prod/45df1872-c7f7-4b3d-baa9-1b0c4f56a5cc.png",
choice: [],
),
],
),
]
'''
In order to insert new data into the food array, first you need to find the correct category:
String myID = '1';
int index = _categories.indexWhere((cat) => cat.id == myID);
Once you did that, it is quite simple:
_categories[index].food.add(Food());
I have been working with the online gallery of Flutter charts (https://google.github.io/charts/flutter/gallery.html) but I'm struggling to add a title for x & y axis values.
Can somebody help me or tell me how to add the labels to the graph?
Its possible using behaviors property, check the code
var chart = charts.LineChart(seriesList,
behaviors: [
new charts.ChartTitle('Dimension',
behaviorPosition: charts.BehaviorPosition.bottom,
titleStyleSpec: chartsCommon.TextStyleSpec(fontSize: 11),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('Dose, mg',
behaviorPosition: charts.BehaviorPosition.start,
titleStyleSpec: chartsCommon.TextStyleSpec(fontSize: 11),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea)
],
defaultRenderer: new charts.LineRendererConfig(includePoints: true));
Source https://google.github.io/charts/flutter/example/behaviors/chart_title
use the 'behavior' list for set title of chart
Widget build(BuildContext context) {
return new charts.LineChart(
seriesList,
animate: animate,
behaviors: [
new charts.ChartTitle('Top title text',
subTitle: 'Top sub-title text',
behaviorPosition: charts.BehaviorPosition.top,
titleOutsideJustification: charts.OutsideJustification.start,
innerPadding: 18),
new charts.ChartTitle('Bottom title text',
behaviorPosition: charts.BehaviorPosition.bottom,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('Start title',
behaviorPosition: charts.BehaviorPosition.start,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('End title',
behaviorPosition: charts.BehaviorPosition.end,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
],
);
}
You can do it by using behaviors using line annotations iterating your list data and make a new LineAnnotationSegment array but you should be aware that some titles may overlap when the next time point is very close.
final data = [
LinearPrices(DateTime(2020, 9, 19), 5),
LinearPrices(DateTime(2020, 9, 26), 15),
LinearPrices(DateTime(2020, 10, 3), 20),
LinearPrices(DateTime(2020, 10, 10), 17),
];
#override
Widget build(BuildContext context) {
return charts.TimeSeriesChart(seriesList, animate: false, behaviors: [
charts.RangeAnnotation( data.map((e) => charts.LineAnnotationSegment(
e.timestamp, charts.RangeAnnotationAxisType.domain,
middleLabel: '\$${e.price}')).toList()),
]);
}
Nevertheless you can use a callback to paint when the user clicks the line by painting either a custom text at the bottom or as a custom label using behaviors like this:
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
final data = [
LinearPrices(DateTime(2020, 9, 19), 5),
LinearPrices(DateTime(2020, 9, 26), 15),
LinearPrices(DateTime(2020, 10, 3), 20),
LinearPrices(DateTime(2020, 10, 10), 17),
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Chart'),
),
body: ChartPricesItem(data),
));
}
}
class ChartPricesItem extends StatefulWidget {
final List<LinearPrices> data;
ChartPricesItem(this.data);
static List<charts.Series<LinearPrices, DateTime>> _createSeries(
List<LinearPrices> data) {
return [
charts.Series<LinearPrices, DateTime>(
id: 'Prices',
colorFn: (_, __) => charts.MaterialPalette.deepOrange.shadeDefault,
domainFn: (LinearPrices sales, _) => sales.timestamp,
measureFn: (LinearPrices sales, _) => sales.price,
data: data,
)
];
}
#override
_ChartPricesItemState createState() => _ChartPricesItemState();
}
class _ChartPricesItemState extends State<ChartPricesItem> {
DateTime _time;
double _price;
// Listens to the underlying selection changes, and updates the information relevant
void _onSelectionChanged(charts.SelectionModel model) {
final selectedDatum = model.selectedDatum;
DateTime time;
double price;
// We get the model that updated with a list of [SeriesDatum] which is
// simply a pair of series & datum.
if (selectedDatum.isNotEmpty) {
time = selectedDatum.first.datum.timestamp;
price = selectedDatum.first.datum.price;
}
// Request a build.
setState(() {
_time = time;
_price = price;
});
}
#override
Widget build(BuildContext context) {
final simpleCurrencyFormatter =
charts.BasicNumericTickFormatterSpec.fromNumberFormat(
NumberFormat.compactSimpleCurrency());
var behaviors;
// Check if the user click over the line.
if (_time != null && _price != null) {
behaviors = [
charts.RangeAnnotation([
charts.LineAnnotationSegment(
_time,
charts.RangeAnnotationAxisType.domain,
labelDirection: charts.AnnotationLabelDirection.horizontal,
labelPosition: charts.AnnotationLabelPosition.margin,
labelStyleSpec:
charts.TextStyleSpec(fontWeight: FontWeight.bold.toString()),
middleLabel: '\$$_price',
),
]),
];
}
var chart = charts.TimeSeriesChart(
ChartPricesItem._createSeries(widget.data),
animate: false,
// Include timeline points in line
defaultRenderer: charts.LineRendererConfig(includePoints: true),
selectionModels: [
charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: _onSelectionChanged,
)
],
// This is the part where you paint label when you click over the line.
behaviors: behaviors,
// Sets up a currency formatter for the measure axis.
primaryMeasureAxis: charts.NumericAxisSpec(
tickFormatterSpec: simpleCurrencyFormatter,
tickProviderSpec:
charts.BasicNumericTickProviderSpec(zeroBound: false)),
/// Customizes the date tick formatter. It will print the day of month
/// as the default format, but include the month and year if it
/// transitions to a new month.
///
/// minute, hour, day, month, and year are all provided by default and
/// you can override them following this pattern.
domainAxis: charts.DateTimeAxisSpec(
tickFormatterSpec: charts.AutoDateTimeTickFormatterSpec(
day: charts.TimeFormatterSpec(
format: 'd', transitionFormat: 'dd/MM/yyyy'),
minute: charts.TimeFormatterSpec(
format: 'mm', transitionFormat: 'dd/MM/yyyy HH:mm'))),
);
var chartWidget = Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
height: 200.0,
child: chart,
),
);
final children = <Widget>[chartWidget];
// If there is a selection, then include the details.
if (_time != null) {
children.add(Padding(
padding: EdgeInsets.only(top: 4.0),
child: Text(DateFormat('dd/MM/yyyy hh:mm').format(_time),
style: Theme.of(context).textTheme.bodyText1)));
}
return SingleChildScrollView(
child: Column(
children: <Widget>[
const SizedBox(height: 8),
Text("Product Prices", style: Theme.of(context).textTheme.headline5),
Column(children: children),
],
),
);
}
}
/// Sample linear data type.
class LinearPrices {
final DateTime timestamp;
final double price;
LinearPrices(this.timestamp, this.price);
}
This is the result: