creating textbox from list in flutter - 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];
}

Related

Retrieve specific value from Hive Box

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

How to handle selected item for dynamically generated ListView in Flutter

final variationMap = HashMap<String, List>();
In this map, I have
key -> ["color"] = value -> ["White", "Black"];
key -> ["ram"] = value -> ["128GB", "256GB"];
Based on this information I have designed the below UI.
**I want -> If I select white, white will be selected and black will remain unselected. And If I select black white will become unselected.
The same goes for Ram. Selecting one will make the other unselected. Two list view selections will work independently. **
For a single list view, we can achieve this using a selectedIndex variable.
Here is the API response. Here attribute value can be multiple. But I need to show one value in UI. So after some logic, I store the label and value into a map.
"productVariation": [
{
"price": 406089.25,
"qty": 449,
"variationAttribute": [
{
"attribute_value": "White",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "128GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
},
{
"price": 292561.69,
"qty": 246,
"variationAttribute": [
{
"attribute_value": "White",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "256GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
},
{
"price": 951456.88,
"qty": 828,
"variationAttribute": [
{
"attribute_value": "Black",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "128GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
},
{
"price": 930735.09,
"qty": 321,
"variationAttribute": [
{
"attribute_value": "Black",
"attributeDetails": {
"attributeLabel": [
{
"label": "Color"
}
]
}
},
{
"attribute_value": "256GB",
"attributeDetails": {
"attributeLabel": [
{
"label": "Ram"
}
]
}
}
]
}
]
Here is the UI code. This code is for the bottom sheet dialog.
variationView() {
final widgets = <Widget>[];
var i = 1; // maintain vertical dot line between variation
for (var key in widget.controller.variationMap.keys) {
final list = widget.controller.variationMap[key];
widgets.add(
GlobalText(
str: "Select $key",
fontSize: 18,
fontWeight: FontWeight.w300,
),
);
widgets.add(
const SizedBox(
height: 20,
),
);
widgets.add(
SizedBox(
height: 60,
child: ListView.builder(
itemCount: list!.length,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, index) {
return GestureDetector(
onTap: () {
setState(() {
isSelectedIndex = index;
isSelectedIndexForListView = i;
});
},
child:Container(
margin: EdgeInsets.only(right: 11),
padding: EdgeInsets.all(4),
width: 60,
height: 55,
decoration: BoxDecoration(
color: Color(0xfff8f8f8),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isSelectedIndex == index && isSelectedIndexForListView == i
? Colors.black
: Color(0xffe2e2e2),
width: 1,
),
),
child: Center(
child: GlobalText(
str: list[index],
color: Color(0xff535960),
fontSize: 13,
fontWeight: FontWeight.w400,
maxLines: 2,
),
),
),
);
},
),
),
);
if (i < widget.controller.variationMap.keys.length) {
widgets.add(
const SizedBox(
height: 30,
),
);
}
i++;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widgets,
);
}
I have tried multiple ways but failed to hold or manage the state of the selected item.
In this code, I have tried to hold the index of the list view and another for item selected index. but When I select a ram, So same index color also goes selected and vice versa.
I have also tried using Unique key. But failed to solve the problem.
First you can create a model class for Value which will have to fields one for the value name another for checking if it's selected or not.
class Value{
String valueName;
bool isSelected;
}
Then create another class which will have one field of String type that is the label and another field of type List of Value object.
class Model {
String label;
List<Value> valueList;
}
From your controller or viewmodel class or the class you are using to update the states you will just have to update the value of isSelected field.

Flutter Form Builder loses initialValue when no input exists for field

When using FormBuilder initialValue, it seems the values are not saved when they are not part of an input. Here is an example code snipped
FormBuilder(
initialValue: {
"id": "MyId",
"name": "Name",
},
key: _formKey,
child: Column(
children: [
FormBuilderTextField(
name: "name",
),
TextButton(
onPressed: () {
_formKey.currentState?.save();
if (_formKey.currentState?.validate() == true) {
print(_formKey.currentState!.value);
}
},
child: Text(
"Save",
),
)
],
),
)
When pressing save and inspecting the values of the form state it has only name but is missing the id attribute.
Is this intended behavior and if so, is it possible to make it keep the id field?

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;
}