Flutter : How to calculate value from textfields in listview.builder - dart

I want to retrieve and calculate input values from the textfield in listview .. how to achieve that?

You should define a list of TextEditingController with the same length of your listview, then assign each TextField in your list an object of this array, which will enable you to retrieve the value whether when editing or when completed. This is an example of what I've said :
List<TextEditingController> textFieldControllers ;
in your list :
textFieldControllers[index] = new TextEditingController() ;
new TextField(controller: textFieldControllers[index]);
after that you can retrieve the value by :
textFieldControllers[index].text

Brilliant. Added some stuff below to suit my use case.
First define a List like so List<TextEditingController> textFieldControllers=[];
then iterate through the the list used in creating the widgets in the List.builder like so.
for (var i = 0; i < typesItem.length; i++) {
textFieldControllers.add(TextEditingController());
}
where typesItem is the List item used in creating the Listbuilder widgets so as to maintain same lenght with your widgets dynamically.You can run this code on your initState method
there after goto your List.builder widget and initialise your controller like so.
textFieldControllers[index] = TextEditingController() ;
and set your TextField controllers in your return method like so
textFieldControllers[index]
You can therefore get all your value by using
textFieldControllers[index].text;
You can manipulate to suit use your case as you want

List<TextEditingController> machPresent=[];
machPresent.add(TextEditingController(text: ''));
Container(
height: mainScreenHeight * 0.06,
width: mainScreenWidth * 0.3,
child: TextFormField(
keyboardType: TextInputType.number,
controller: machPresent[index],
enabled: true,
textAlign: TextAlign.center,
),
),
),
Press an action from a button
Container(
height: mainScreenHeight * 0.06,
width: mainScreenWidth * 0.3,
child: RaisedButton(
onPressed: () {
submitForm(index);},
child: Text('Submit',textAlign: TextAlign.center),
),
),
dynamic submitForm(int index) {
var machValue=machPresent[index].text;
var f= machValue;
print(f);
}
Result
I/flutter ( 7914): fff888- -- -- -Select Status
I/flutter ( 7914): sourav- -- -- -Select Status

Related

Jetpack Compose - avoid unnecessary recomposition

I'm creating a custom slider control for my app but I can't avoid unnecessary recomposition without adding some ugly hacks...
CustomSlider1 is a component that recomposes all its children when the value changes; CustomSlider2 is what I came up with that does not... but the code doesn't seem right, so could anyone tell me what I'm doing wrong in CustomSlider1 and if CustomSlider2 is indeed correct?
The difference between the 2 components is basically that I read the value through a lambda and also added the Slider component inside a Scoped composable.
I'm using recomposeHighlighter to show recompositions.
Here's a gif showing how both behaves when I change its value:
Here's the code:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestTheme {
Column {
var value by remember {
mutableStateOf(50f)
}
CustomSlider1("Custom Slider", value, 50f, true, { value = it }, 0f..100f, 5)
Spacer(modifier = Modifier.padding(10.dp))
CustomSlider2("Custom Slider 2", { value }, 50f, true, { value = it }, 0f..100f, 5)
}
}
}
}
}
#Composable
fun CustomSlider1(
label: String,
value: Float,
defaultValue: Float,
enabled: Boolean = true,
onValueChange: (Float) -> Unit,
valueRange: ClosedFloatingPointRange<Float>,
steps: Int = 0,
) {
Column(
modifier = Modifier.recomposeHighlighter()
) {
Text(
text = label,
color = if (enabled) Color.Unspecified else LocalContentColor.current.copy(alpha = 0.5f),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.recomposeHighlighter()
)
Row {
Slider(
value = value,
valueRange = valueRange,
steps = steps,
enabled = enabled,
onValueChange = onValueChange,
modifier = Modifier
.recomposeHighlighter()
.weight(1f)
)
IconButton(
onClick = { onValueChange(defaultValue) },
enabled = enabled,
colors = IconButtonDefaults.iconButtonColors(contentColor = MaterialTheme.colorScheme.primary),
modifier = Modifier.recomposeHighlighter()
) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = null,
modifier = Modifier.recomposeHighlighter()
)
}
}
}
}
#Composable
fun CustomSlider2(
label: String,
value: () -> Float,
defaultValue: Float,
enabled: Boolean = true,
onValueChange: (Float) -> Unit,
valueRange: ClosedFloatingPointRange<Float>,
steps: Int = 0,
) {
Column(
modifier = Modifier.recomposeHighlighter()
) {
Text(
text = label,
color = if (enabled) Color.Unspecified else LocalContentColor.current.copy(alpha = 0.5f),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.recomposeHighlighter()
)
Row {
Scoped { //had to do this to avoid recompositions...
Slider(
value = value.invoke(),
valueRange = valueRange,
steps = steps,
enabled = enabled,
onValueChange = onValueChange,
modifier = Modifier
.recomposeHighlighter()
.weight(1f)
)
}
IconButton(
onClick = { onValueChange(defaultValue) },
enabled = enabled,
colors = IconButtonDefaults.iconButtonColors(contentColor = MaterialTheme.colorScheme.primary),
modifier = Modifier.recomposeHighlighter()
) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = null,
modifier = Modifier.recomposeHighlighter()
)
}
}
}
}
#Composable
fun Scoped(content: #Composable () -> Unit) = content()
First thing you do to prevent recompositions creating Scope to create recomposition scope to limit recomposition since Column and Row are inline functions that do not create scopes.
Second thing with lambdas. In compose lambdas are unique they defer state read from composition phase of frame to layout or draw phases that's why you don't have recompositions.
Composition->Layout( measure and Layout)->Draw are the phases when a (re)composition is triggered by using lambdas you don't invoke composition phase.
For lambdas and state deferring you can check out official document or question below
// Here, assume animateColorBetween() is a function that swaps between
// two colors
val color by animateColorBetween(Color.Cyan, Color.Magenta)
Box(Modifier.fillMaxSize().background(color))
Here, the box's background color is switching rapidly between two
colors. This state is thus changing very frequently. The composable
then reads this state in the background modifier. As a result, the box
has to recompose on every frame, since the color is changing on every
frame.
To improve this, we can use a lambda-based modifier–in this case,
drawBehind. That means the color state is only read during the draw
phase. As a result, Compose can skip the composition and layout phases
entirely–when the color changes, Compose goes straight to the draw
phase.
val color by animateColorBetween(Color.Cyan, Color.Magenta) Box( Modifier
.fillMaxSize()
.drawBehind {
drawRect(color)
} )
Android Jetpack Compose - Composable Function get recompose each time Text-field value changes
For scoped composition you can check out this question or other answer linked to it

Flutter : Sort widget according to a sorted list of values

I currently have an app that displays nearby hospitals within a 1.5 km radius and it looks like this:
The trouble I am having is that I could not figure out how to sort the card based on their calculated distance from lowest to highest.
I made a List<double> doubleList = []; to store the list of calculated distances and sorted it with doubleList.sort();.
Here is the code snippet:
return new ListView.builder(
shrinkWrap: true,
itemCount: jsonresponse.length,
itemBuilder: (context, index){
String name = jsonresponse[index]["Name"];
String lat = jsonresponse[index]["Latitude"];
String lng = jsonresponse[index]["Longitude"];
var distance = new GreatCircleDistance.fromDegrees(latitude1: double.parse(widget.lat), longitude1: double.parse(widget.lng), latitude2: double.parse(lat), longitude2: double.parse(lng));
var totaldistance = distance.haversineDistance().toStringAsFixed(2);
double distanceDouble = double.parse(totaldistance);
if(distanceDouble < 1500.00){
doubleList.add(distanceDouble);
doubleList.sort((a, b){
return a.compareTo(b);
});
print(doubleList);
return new Column(
children: <Widget>[
new Card(
child: new ListTile(
title: new Text(name),
subtitle: new Text("$distanceDouble m away"),
trailing: new IconButton(
icon: new Icon(Icons.info),
onPressed: (){
makeDialog(lat, lng);
}),
),
)
],
);
}else{
return new Container();
}
});
This is the output of the sorted distance values when printed:
I/flutter ( 4286): [987.8]
I/flutter ( 4286): [987.8, 1014.87]
I/flutter ( 4286): [987.8, 1014.87, 1352.22]
I/flutter ( 4286): [128.26, 987.8, 1014.87, 1352.22]
I/flutter ( 4286): [128.26, 987.8, 1014.87, 1260.73, 1352.22]
I/flutter ( 4286): [122.91, 128.26, 987.8, 1014.87, 1260.73, 1352.22]
I/flutter ( 4286): [122.91, 128.26, 987.8, 1014.87, 1260.73, 1303.36, 1352.22]
I/flutter ( 4286): [122.91, 128.26, 987.8, 1014.87, 1232.47, 1260.73, 1303.36, 1352.22]
I/flutter ( 4286): [122.91, 128.26, 987.8, 1014.87, 1232.47, 1232.47, 1260.73, 1303.36, 1352.22]
My question is:
How do I make sure the widget will follow the order of the sorted distance values?
ListView.builder is taking an itemBuilder wich is responsible of building one item. careful here, your sorting each time you build an item.
You need to sort your jsonresponse before calling List.builder or jsonresponse[index] won't be correct.
You can try something like this
jsonresponse.sort((a, b) {
var distance1 = new GreatCircleDistance.fromDegrees(latitude1: double.parse(widget.lat), longitude1: double.parse(widget.lng), latitude2: double.parse(a.lat), longitude2: double.parse(a.lng));
var totaldistance1 = distance1.haversineDistance().toStringAsFixed(2);
double distanceDouble1 = double.parse(totaldistance1);
var distance2 = new GreatCircleDistance.fromDegrees(latitude1: double.parse(widget.lat), longitude1: double.parse(widget.lng), latitude2: double.parse(b.lat), longitude2: double.parse(b.lng));
var totaldistance2 = distance2.haversineDistance().toStringAsFixed(2);
double distanceDouble2 = double.parse(totaldistance2);
return (distanceDouble1 - distanceDouble2).toInt();
});
before return new Column (I set toInt because double is not suitable for comparator)

Can I format a number before it is rendered in <Text>

I am rendering a <Text> node in Flutter app something like:
We have total ${_summary['bookCount']} books.
_summary is retrieved via a remote API and bookCount is one of the returned JSON field. It normally is more than 1,000.
If I display the book count like that, it is a plain 1234. I would like to make it be shown as 1,234.
Currently, I have to manually modify that field using some formatter but this is cumbersome.
I am looking for something like:
We have total ${myNumberFormat(_summary['bookCount'])} books.
grammar, where myNumberFormat is a function.
In my previous programming in PHP and Twig, this can be done with a filter.
Much appreciate your input.
Update
#raju-bitter
This solution is what I know and is absolutely correct. What I am looking for is an inline "filter".
With this solution, a few things I don't like, the most of which is that, I have to split my one liner of text into several few segments:
We have {XXX} books, accumulating to {YYY} pages, and {ZZZ} word counts.
This sentence will be broken to 7 parts at least so that each number text section can be formatted via a formatter and then wrapped in a surrounding <Text>.
I am trying to see if there are more straightforward ways to do so.
There is a Dart package for formatting numbers, the Dart intl package. To use the package, add the following line to the Dart dependencies: pubspec.yaml file:
intl: ^0.17.0
Here's what my dependencies look like with the line:
dependencies:
flutter:
sdk: flutter
intl: ^0.17.0
Click packages get in IntelliJ, or run flutter packages get from the command line.
Make sure your class imports the intl package:
import 'package:intl/intl.dart' as intl;
In your code, you can use NumberFormat class to do the formatting:
final formatter = intl.NumberFormat.decimalPattern().format(1234) // formatted number will be: 1,234
Full stateful widget example:
class NumberFormatExample extends StatefulWidget {
#override
_NumberFormatExampleState createState() => new _NumberFormatExampleState();
}
class _NumberFormatExampleState extends State<NumberFormatExample> {
final formatter = intl.NumberFormat.decimalPattern();
int theValue = 1234;
#override
Widget build(BuildContext context) {
return new Text(formatter.format(theValue));
}
}
An update for above answers:
First add intl package into your pubspec.yaml file, just after flutter sdk (like below):
dependencies:
flutter:
sdk: flutter
intl: ^0.16.0
If you use flutter_localizations, intl must be above of that.
Now you can use NumberFormat class.
Some examples:
print(NumberFormat.currency().format(123456)); // USD123,456.00
print(NumberFormat.currency(locale: 'eu').format(123456)); // 123.456,00 EUR
print(NumberFormat.currency(name: 'EURO').format(123456)); // EURO123,456.00
print(NumberFormat.currency(locale: 'eu', symbol: '?').format(123456)); // 123.456,00 ?
print(NumberFormat.currency(locale: 'eu', decimalDigits: 3).format(123456)); // 123.456,000 EUR
print(NumberFormat.currency(locale: 'eu', customPattern: '\u00a4 #,##.#').format(123456)); // EUR 12.34.56,00
Reference & More information: https://www.woolha.com/tutorials/dart-formatting-currency-with-numberformat#supported-locales
NumberFormat Class Dart API: https://api.flutter.dev/flutter/intl/NumberFormat-class.html
In for loop you can try:
Text('Index ${i}'), //Index 0
Format with operation:
Text('Total: \$${price * quantity}'), //$20
With Decimal point:
Text('\$${cart.totalAmount.toStringAsFixed(2)}'), //2.34
Using intl package,
double number = 1234;
String output = NumberFormat.decimalPattern().format(number); // 1,234
There is a Flutter package that allows you to format text input with predefined patterns.
First add the following line to the Dart dependencies: pubspec.yaml file:
pattern_formatter: ^1.0.2```
then import it in your dart code
import 'package:pattern_formatter/pattern_formatter.dart';
Example: Thousand group
TextField(
keyboardType: TextInputType.number,
inputFormatters: [
ThousandsFormatter()
],
)
Decimal Number
TextField(
keyboardType: TextInputType.number,
inputFormatters: [
ThousandsFormatter(allowFraction: true)
],
)
Card number grouping
TextField(
keyboardType: TextInputType.number,
inputFormatters: [
CreditCardFormatter(),
],
)
Date format
TextField(
keyboardType: TextInputType.number,
inputFormatters: [
DateInputFormatter(),
],
)
Using the intl package will limit you to format numbers without decimals. You can make it easy with a function for counting positions and add the thousands separator where it's needed.
String numf(String n) {
var numArr = n.split('');
String revStr = "";
int thousands = 0;
for (var i = numArr.length - 1; i >= 0; i--) {
if (numArr[i].toString() == ".") {
thousands = 0;
} else {
thousands++;
}
revStr = revStr + numArr[i].toString();
if (thousands == 3 && i > 0) {
thousands = 0;
revStr = revStr + ',';
}
}
return revStr.split('').reversed.join('');
}

Showing or hiding a button based on contents of a field in a database

I am trying to show or hide a button based on the contents of a particular database field. I have the field defined as 'text', as the field may contain something like 1-1 or 1-20 or 0. If the field (arref1) is not equal to 0 then I want to show my button. Otherwise hide the button. Here is what I have so far but it dosen't work:
var resultRule = db.execute('SELECT arref1 FROM genrules WHERE title="'+client+'"');
if (resultRule!='0') {
var appliedRule1 = Ti.UI.createButton({
title:' Applied Rule ' + rows.fieldByName('arref1') + '',
bottom:120,
left: 80,
width: 'auto',
height:40,
borderRadius:5,
textAlign: 'center',
color:'#FFFFFF',
backgroundColor:'#7b4336',
backgroundSelectedColor:'#cc9933',
backgroundImage: 'NONE',
font:{fontSize:'20dp'}
});
win.add(appliedRule1);
appliedRule1.addEventListener('click',function(e)
{
var win = Ti.UI.createWindow({
url: 'appliedruledetail.js',
title:' Applied Rule' + rows.fieldByName('arref1') + ''
});
var client = '' + rows.fieldByName('genrule') + '' ;
win.client = client;
Ti.UI.currentTab.open(win);
}
);
} else{};
db.execute returns a Titanium.DB.ResultSet (which is an object) not a string.
Here is how you use a result set:
// Get the row with execute, returns a resultSet
var row = db.execute('SELECT * FROM genrules WHERE title="'+client+'"');
// Make sure its a valid row
if (row.isValidRow()){
// Now get the field from the current row
if(row.fieldByName('arref1') != '0') {
// Add your button here
}
}
row.close(); // Close the result set since were done with it
I heavily borrowed from this example in the documentation

ATCTContent created with wrong id and title

I've made an add-on with several ATCTContent, all created with paster addcontent contenttype. All but one, GearContent work as expected. Only when I create instances of GearContent they receive names like gear, gear-1, etc. ignoring the title. In default view, the H1 tag is always 'Gear' but the title bellow it is right.
Trying to change the ids and titles on the folder content view doesn't do anything. There's no error message.
Same thing with the catalog. GearContent's Title metadata is 'Gear' for all instances. It works for all other types.
GearContent is only addable inside GearFolder. Other contents have similar restrictions and work fine. I'm using plone 4.0.4.
What can I do to make new instances get the title right?
Below content/gearcontent.py:
"""Definition of the Gear Content content type
"""
from zope.interface import implements
from Products.Archetypes import atapi
from Products.ATContentTypes.content import base
from Products.ATContentTypes.content import schemata
# -*- Message Factory Imported Here -*-
from levity7.gears import gearsMessageFactory as _
from levity7.gears.interfaces import IGearContent
from levity7.gears.config import PROJECTNAME
GearContentSchema = schemata.ATContentTypeSchema.copy() + atapi.Schema((
# -*- Your Archetypes field definitions here ... -*-
atapi.StringField(
'title',
storage=atapi.AnnotationStorage(),
widget=atapi.StringWidget(
label=_(u"Title"),
description=_(u"Name for this content."),
),
required=True,
),
atapi.ReferenceField(
'activities',
storage=atapi.AnnotationStorage(),
widget=atapi.ReferenceWidget(
label=_(u"Adventure Activities"),
description=_(u"Select all activities that apply to this content."),
),
required=True,
relationship='gearcontent_activities',
allowed_types=('Gear Activity'), # specify portal type names here ('Example Type',)
multiValued=True,
),
atapi.ReferenceField(
'category',
storage=atapi.AnnotationStorage(),
widget=atapi.ReferenceWidget(
label=_(u"Category"),
description=_(u"Select a category for this content."),
),
required=True,
relationship='gearcontent_category',
allowed_types=('Gear Category'), # specify portal type names here ('Example Type',)
multiValued=False,
),
atapi.ImageField(
'image',
storage=atapi.AnnotationStorage(),
widget=atapi.ImageWidget(
label=_(u"Image"),
description=_(u"A picture of this content."),
),
validators=('isNonEmptyFile'),
),
atapi.StringField(
'imageLink',
storage=atapi.AnnotationStorage(),
widget=atapi.StringWidget(
label=_(u"Image Link"),
description=_(u"An URL to the image of this content."),
),
validators=('isURL'),
),
atapi.TextField(
'description',
storage=atapi.AnnotationStorage(),
widget=atapi.RichWidget(
label=_(u"Description"),
description=_(u"Description for the content."),
),
required=True,
),
atapi.StringField(
'reviewLink',
storage=atapi.AnnotationStorage(),
widget=atapi.StringWidget(
label=_(u"Review Link"),
description=_(u"Link to Review page."),
),
validators=('isURL'),
),
atapi.StringField(
'diyLink',
storage=atapi.AnnotationStorage(),
widget=atapi.StringWidget(
label=_(u"DIY Link"),
description=_(u"Link to DIY page."),
),
validators=('isURL'),
),
atapi.TextField(
'commentary',
storage=atapi.AnnotationStorage(),
widget=atapi.TextAreaWidget(
label=_(u"commentary"),
description=_(u"commentarys for the content. These will not be displayed."),
),
),
atapi.TextField(
'purchaseHtml',
storage=atapi.AnnotationStorage(),
widget=atapi.TextAreaWidget(
label=_(u"Purchase HTML Code"),
description=_(u"HTML used to display or add this item to the Cart."),
),
),
atapi.IntegerField(
'score',
storage=atapi.AnnotationStorage(),
widget=atapi.IntegerWidget(
label=_(u"Life This Value"),
description=_(u"Initial value of 'Life This'"),
),
default=_(u"0"),
validators=('isInt'),
),
))
# Set storage on fields copied from ATContentTypeSchema, making sure
# they work well with the python bridge properties.
GearContentSchema['title'].storage = atapi.AnnotationStorage()
GearContentSchema['description'].storage = atapi.AnnotationStorage()
schemata.finalizeATCTSchema(GearContentSchema, moveDiscussion=False)
class GearContent(base.ATCTContent):
"""Gear Content"""
implements(IGearContent)
meta_type = "GearContent"
schema = GearContentSchema
title = atapi.ATFieldProperty('title')
description = atapi.ATFieldProperty('description')
# -*- Your ATSchema to Python Property Bridges Here ... -*-
title = atapi.ATFieldProperty('title')
activities = atapi.ATReferenceFieldProperty('activities')
category = atapi.ATReferenceFieldProperty('category')
image = atapi.ATFieldProperty('image')
imageLink = atapi.ATFieldProperty('imageLink')
description = atapi.ATFieldProperty('description')
reviewLink = atapi.ATFieldProperty('reviewLink')
diyLink = atapi.ATFieldProperty('diyLink')
commentary = atapi.ATFieldProperty('commentary')
purchaseHtml = atapi.ATFieldProperty('purchaseHtml')
score = atapi.ATFieldProperty('score')
atapi.registerType(GearContent, PROJECTNAME)
Thanks.
Remove your "title" field. It's already defined in ATContentTypeSchema. You're effectively re-implementing it, but without the baseline functionality like automatic content object naming. Yours is masking the one defined in Archetypes.
The problem was in the field category. It seems that's a reserved name. (in the above it appears 'categoty'; it was a typo).

Resources