Retrieve specific value from Hive Box - flutter

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()),
),
),
],
);

Related

Getting this RangeError (index): Invalid value: Valid value range is empty: 1 while trying to populate json data to ListView.builder

I have an API and what I am trying to do is to display the 'CourseTitle' from the JSON into the ExpansionTile title and the corresponding 'Title', 'EndDate', 'QuizStatus' in the children of ExpansionTile.
If data is not available for corresponding 'Title', 'EndDate', 'QuizStatus' etc., only 'CourseTitle' should be added to the ExpansionTile title, and children should be empty.
The first tile is built as expected but the remaining screen shows a red screen with RangeError (index): Invalid value: Valid value range is empty: 1.
I'm aware that this is because of empty data from JSON but couldn't solve the issue.
Here's JSON response:
{
"QuizzesData": [
{
"request_status": "Successful",
"CourseCode": "ABC101",
"CourseTitle": "ABC Course",
"UnReadStatus": [],
"Quizzes": [
{
"QuizID": "542",
"Title": "Test Quiz 01",
"StartDate": "Oct 20, 2022 12:00 AM",
"EndDate": "Oct 31, 2022 11:59 PM",
"IsDeclared": "False",
"Questions": "5",
"TotalMarks": "5",
"MarksObtained": "1",
"MarksObtainedTOCheckQuizResult": "1",
"QuizIsDeclared": "Un-Declared",
"StudentSubmitStatus": "true",
"IsRead": "1",
"QuizStatus": "Attempted"
}
]
},
{
"CourseCode": "DEF101",
"CourseTitle": "DEF Course",
"UnReadStatus": [],
"Quizzes": []
},
{
"CourseCode": "GHI101",
"CourseTitle": "GHI Course",
"UnReadStatus": [],
"Quizzes": []
},
{
"CourseCode": "JKL101",
"CourseTitle": "JKL Course",
"UnReadStatus": [],
"Quizzes": []
},
]
}
Here's the API data:
var listofdata ;
Future quizListingApi() async {
final response = await http.get(Uri.parse('json url'));
if(response.statusCode == 200){
listofdata = jsonDecode(response.body.toString());
}
else{
print(response.statusCode);
}
and the build method:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(child: FutureBuilder(
future: quizListingApi(),
builder: (context, AsyncSnapshot snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Text('Loading');
}
else{
return ListView.builder(
itemCount: listofdata['QuizzesData'].length,
itemBuilder: (context, index){
return Card(
child: Column(
children: [
ExpansionTile(
title: Text(listofdata['QuizzesData'][index]['CourseTitle']),
children: [
Text(listofdata['QuizzesData'][index]['Quizzes'][index]['Title']),
Text(listofdata['QuizzesData'][index]['Quizzes'][index]['EndDate']),
Text(listofdata['QuizzesData'][index]['Quizzes'][index]['QuizStatus']),
],
),
],
),
);
}
);
}
},
))
],
),
);
}
I also tried answers from other similar threads but couldn't find the solution for this specific type of problem.
Your Quizzes's index are not same as your listofdata, so you need and other for loop for your Quizzes's items:
ExpansionTile(
title: Text(listofdata['QuizzesData'][index]['CourseTitle']),
children: List<Widget>.generate(listofdata['QuizzesData'][index]['Quizzes'].length, (i) => Column(
children: [
Text(listofdata['QuizzesData'][index]['Quizzes'][i]
['Title']),
Text(listofdata['QuizzesData'][index]['Quizzes'][i]
['EndDate']),
Text(listofdata['QuizzesData'][index]['Quizzes'][i]
['QuizStatus']),
],
),),
),

Getting Error on implementing Dismissible on flutter list

I am trying to implement Dismissible to swipe and remove the item from the list in flutter, but I am getting the below error on implementation of the same
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of
type 'String'
at this line of the code key: Key(item)
How should I resolve it ?
ListView.separated(
separatorBuilder: (context, index){
return Divider();
},
controller: _scrollController,
itemCount: noteItems,
shrinkWrap: true,
itemBuilder: (context, index) {
final item = firstdata[index];
return
Dismissible(
direction: DismissDirection.endToStart,
key: Key(item),
onDismissed: (direction) {
setState(() {
firstdata.removeAt(index);
});
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text("$item dismissed")));
},
background: Container(color: Colors.red)
,
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 7.0, 8.0, 0.0),
child: Column(
children: <Widget>[
ListTile(
leading:ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset('images/appstore.png', width: 50, height: 50)
) ,
title:
Row(children: [
Flexible(
child: firstdata[index]['id']!= null?AutoSizeText(
firstdata[index]['id'],
maxLines: 2,
style: TextStyle(fontWeight: FontWeight.bold),) :Text(''),
),
],),
),
],
),
),
);
},
),
The JSON data structure for the list view is here below
{
"error": "false",
"notification": [
{
"rn": "1",
"id": "224",
"company_details": {
"code": "2",
}
},
{
"rn": "2",
"id": "219",
"company_details": {
"code": "3",
}
},
{
"rn": "3",
"id": "213",
"company_details": {
"code": "3",
}
},
{
"rn": "4",
"id": "209",
"company_details": {
"code": "4",
}
},
{
"rn": "5",
"id": "204",
"company_details": {
"code": "3",
}
},
{
"rn": "6",
"id": "199",
"company_details": {
"code": "3",
}
},
{
"rn": "7",
"id": "193",
"company_details": {
"code": "3",
}
}
],
}
How should I implement the same and get it resolved?
As stated in the other answer, the Key function expects a string to create a key based on that. If you can identify an item based on one of its parameters (for example id), then you could use item.id and it would be fine.
However, to make sure it will be truly unique key for any combination of parameters (in your case id, rn and company_details) you can use ObjectKey:
Replace the following line:
key: Key(item)
With the following:
key:ObjectKey(item)
This way Flutter can identify your item's parameters and create a key based on the combination of them.
Other options include ValueKey and UniqueKey.
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
means that it crashed because it was expecting a String and flutter did not find one.
This means that:
key: Key(item)
Key(item)-> Is not a String. I donĀ“t know how are you creating Key/where is it.
My best guess is to try to find some method like...:
`key: Key(item).aMethodthatgivesaString()`
`key: Key(item).toString()`
Let me know if this was useful.

Creating a Dropdown menu in Flutter

What I am trying to do in my app is to add a dropdown based on the list contents. I have something like this:
[
{
id: val,
displayName: Enter value,
type: string,
value: "any"
},
{
id: si,
displayName: Source,
type: list,
value: [
MO
],
data: [
{id: 1, displayId: MO},
{id: 2, displayId: AO},
{id: 3, displayId: OffNet}
]
}
]
Currently there are 2 entries. What I want to do is display a dropdown containing those options (Enter value and Source) as 2 entries of dropdown:
If Enter value is selected a text box next to it should be displayed, since it has a type of string.
If Source option in dropdown is selected another dropdown containing those entries (MO, AO, Offnet) should be present as a dropdown value, since it has a type of list.
In short, based on the selection of the 1st dropdown a widget to be displayed (either text box or another dropdown) should be chosen.
If anyone knows or previously had done the same please help me with this, Thanks.
I'd make use of StatefulWidget to achieve what you need (if you're not using more advanced state management options). State would be helpful to track user's choices, as well as to decide whether to render a text field or another dropdown (or nothing at all).
I've added a complete working example below. Note that it does not follow best practices in a sense that you would probably want to split it up in separate small widgets for better composability (and readability). However, I've opted for quick-and-dirty approach to fit everything in one place.
Also note that you'd probably want to do some more processing once a user makes a choice. Here, I simply illustrate how to render different widgets based on a user's choice (or more generally, changes in StatefulWidget's state). Hence, this example is used to highlight one principle only.
import 'package:flutter/material.dart';
void main() {
runApp(DropdownExample());
}
class DropdownExample extends StatefulWidget {
#override
_DropdownExampleState createState() => _DropdownExampleState();
}
class _DropdownExampleState extends State<DropdownExample> {
String type;
int optionId;
final items = [
{
"displayName": "Enter value",
"type": "string",
},
{
"displayName": "Source",
"type": "list",
"data": [
{"id": 1, "displayId": "MO"},
{"id": 2, "displayId": "AO"},
{"id": 3, "displayId": "OffNet"}
]
}
];
#override
Widget build(BuildContext context) {
Widget supporting = buildSupportingWidget();
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Dropdown Example")),
body: Center(
child: Container(
height: 600,
width: 300,
child: Row(
children: <Widget>[
buildMainDropdown(),
if (supporting != null) supporting,
],
),
),
),
),
);
}
Expanded buildMainDropdown() {
return Expanded(
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: type,
hint: Text("Select a type"),
items: items
.map((json) => DropdownMenuItem(
child: Text(json["displayName"]), value: json["type"]))
.toList(),
onChanged: (newType) {
setState(() {
type = newType;
});
},
),
),
);
}
Widget buildSupportingWidget() {
if (type == "list") {
List<Map<String, Object>> options = items[1]["data"];
return Expanded(
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: optionId,
hint: Text("Select an entry"),
items: options
.map((option) => DropdownMenuItem(
child: Text(option["displayId"]), value: option["id"]))
.toList(),
onChanged: (newId) => setState(() {
this.optionId = newId;
}),
),
),
);
} else if (type == "string") {
return Expanded(child: TextFormField());
}
return null;
}

creating textbox from list in flutter

I have List like this (It keeps changing because this is the response of API,
tableValue=[
{
"id": "RegNo",
"displayName": "Enter Register No",
"type": "string",
"value": "1XYZ19AA"
},
{
"id": "name",
"displayName": "Enter Name",
"type": "string",
"value": "KARAN"
},
{
"id": "sub",
"displayName": "choose subjects",
"type": "list",
"value": ["JAVA"],
"data": [
{"id": "1", "dispId": "JAVA"},
{"id": "2", "dispId": "Python"},
{"id": "3", "dispId": "Dart"}
]
}
];
What I want to display is like below,
Based on the List, I want to display all its data,
i.e
Enter Register No --Text_Box here--
Enter Name --Text_Box here--
(How many entries have string type I want to display a text box with its display name and a value defined in the List for that map should be displayed example 1XYZ19AA on the textbox),
If there are n entries with the type string n text box with the display name should be displayed, and I want to have the control over the data entered.
If there are 3 text boxes in the list if the user enters all or only 1 I should be able to access that uniquely.
Question
Can you suggest any way of displaying if its a type list, because elements in a list should have a multi-select option?
Thank you
ListView.builder(
itemCount: tableValue.length,
itemBuilder:(context, index){
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height/10,
child: Row(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width/2,
height: MediaQuery.of(context).size.height/10,
alignment: AlignmentDirectional.centerStart,
child:Text(tableValue[index]['displayName'])
),
Container(
width: MediaQuery.of(context).size.width/2,
height: MediaQuery.of(context).size.height/10,
alignment: AlignmentDirectional.centerStart,
child: TextField(
decoration: InputDecoration.collapsed(
hintText: "blah"
)
)
)
],
)
);
}
)
Here is an example where you can show the data from the table and also you can see how to access the selected values of the TextFields and Checkboxs
Note that you may need to change the type of tableValue like this:
List<Map<String, dynamic>> tableValue = [...]
Map<String, TextEditingController> controllers = {};
Set<String> checks = {};
This would be the body of your screen
ListView(
children: <Widget>[
Column(
children: tableValue
.where((entry) => entry["type"] == "string")
.map((entry) => Row(
children: <Widget>[
Text(entry["displayName"]),
Flexible(
child: TextField(
controller: getController(entry["id"]),
),
)
],
)).toList(),
),
Column(
children: tableValue
.firstWhere(
(entry) => entry["type"] == "list")["data"]
.map<Widget>(
(data) => CheckboxListTile(
title: Text(data["dispId"]),
value: checks.contains(data["id"]),
onChanged: (checked) {
setState(() {
checked ? checks.add(data["id"]) : checks.remove(data["id"]);
});
},
),
).toList(),
),
Text("Texts: ${controllers.values.map((controller) => controller.text)}"),
Text("Checks: ${checks.map((check) => check)}"),
],
)
And this is how you could handle the TextField controllers
TextEditingController getController(String id) {
controllers.putIfAbsent(id, () => TextEditingController());
return controllers[id];
}

Flutter - fill table with dyanmic data

In my class, I have a variable named report, that looks like this:
{
"id": 1,
"type": "my type",
"name": "Report 1",
"client_name": "John",
"website": "john.com",
"creation_time": "2019-03-12T22:00:00.000Z",
"items": [{
"id": 1,
"report_id": 1,
"place": "Kitchen",
"type": "sometype",
"producer": "somepro",
"serial_number": "123123",
"next_check_date": "2019-03-19",
"test_result": "Verified",
"comments": "some comments"
}]
}
I want to show the list of items in a table with flutter.
So far, I just created a static table as follows:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(report['name'])),
body: Container(
child: Table(children: [
TableRow(children: [
Text("TITLE 1"),
Text("TITLE 2"),
Text("TITLE 3"),
]),
TableRow(children: [
Text("C1"),
Text("C2"),
Text("C3"),
])
])
)
);
}
}
Couldn't find any examples of how to fill the table rows (the titles can stay static) with my JSON items list.
Each row should be an item from the items JSON array.
Any idea?
You can map items to TableRows. Don't forget to end with toList().
For example:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(report['name'])),
body: Container(
child: Table(
children: (report['items'] as List)
.map((item) => TableRow(children: [
Text(item['id'].toString()),
Text(item['report_id'].toString()),
Text(item['place']),
// you can have more properties of course
]))
.toList())));
}
If you want the static titles that you mentioned, you could use insert on the list you create above, and then you have:
#override
Widget build(BuildContext context) {
var list = (report['items'] as List)
.map((item) => TableRow(children: [
Text(item['id'].toString()),
Text(item['report_id'].toString()),
Text(item['place']),
//...
]))
.toList();
list.insert(
0,
TableRow(children: [
Text("TITLE 1"),
Text("TITLE 2"),
Text("TITLE 3"),
//...
]));
return Scaffold(
appBar: AppBar(title: Text(report['name'])),
body: Container(child: Table(children: list)));
}
What you are looking for is an ItemBuilder. You can find a good example of flutter ItemBuilders here and here. Examples here use the ListView builder though.