I want to create a Weight Diary, where the user can add his weight with the day to a List. So I created a class and a List to save the weight with the day.
class WeightData {
final DateTime date;
final double weight;
const WeightData({
this.date,
this.weight,
});
}
List<WeightData> weights = [
WeightData(date: DateTime.now(), weight: 56),
WeightData(date: DateTime.now(), weight: 55.5),
];
My first question is, is that the right way to do this?
I also want that the user can dynamically add new weights, but I dont know how. My Code looks like this so far:
void newEntry() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
keyboardType: TextInputType.number,
validator: (input) =>
input.trim().isEmpty ? 'Please enter Day' : null,
),
TextFormField(
keyboardType: TextInputType.number,
validator: (input) =>
input.trim().isEmpty ? 'Please enter Weight' : null,
)
],
)),
);
});
}
Because there are to entrys to make (day and weight) I dont know how to add this two objects (is this the right word?) to the list.
Inside the Column add a ElevatedButton and check if _formKey is validated and then add to the list:
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
weights.add(WeightData(date: DateTime.now(), weight: weightController.value),)
}
},
child: Text('Submit'),
),
Get the value using TextEditingController inside the TextFormField:
TextEditingController weightController= TextEditingController();
TextFormField(
keyboardType: TextInputType.number,
controller: weightController //here,
validator: (input) =>
input.trim().isEmpty ? 'Please enter Day' : null,
),
Related
I am having a List<String> of cities below
List<String> cities = ["Nairobi", "Tokyo", "Moscow", "Bogota",
"Helsinki", "Denver", "Stockholm", "Oslo"];
I am taking the cities list and just setting the text to TextFormField using TextEditingController upon a city being selected in showCityPickerDialog which updates the cities string list. Below is the code
TextFormField(
cursorHeight: 20,
enableInteractiveSelection: false,
readOnly: true,
focusNode: FocusNode(),
controller: TextEditingController(
text:
cities.isEmpty ? null : "$cities"),
onTap: () {
showCityPickerDialog();
},
decoration: InputDecoration(
isDense: true,
labelText: "Select Cities",
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
showCityPickerDialog();
},
icon: const Icon(Icons.location_city)),
),
validator: (phoneNo) {},
keyboardType: TextInputType.multiline,
maxLines: null,
)
What i would like to achieve is instead of just setting the text using TextEditingController(text: cities.isEmpty ? null : "$cities") i would like to use Chips like in below code where the number of chips inside the TextFormField will depend on the length of the list
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text('City Initial'),
),
label: Text('City'),
)
Maybe you can add chips inside your TextFormField decoration as prefix Widget like this:
prefix: Row(
children: cities
.map(
(e) => Chip(
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text('City Initial'),
),
label: Text(e),
),
)
.toList(),
)
I have a list like this a = [{'one': 'one', 'two': null, 'three': [{'four': 'four'}]}]
I send it to a function to use it in a post request which in the body should receive a Map, so what I did was this to a[0], the problem is that I get this error The getter 'length' was called on null
I start to review and it treats all the property values as if they were Strings, even the nested list 'three': [{'four': 'four'}], I have tried to send the post in this way http.post (url, body: (recurrence [0] as Map)) but it has not worked, it always gives me the same error, even if in the body I put the properties by hand in the body: {'new property': a [0] [' tres']}, how should one act to solve this problem? Thank you very much for your help
Code:
void _ordersGet() async {
await http.get(url).then((value) {
setState(() {
orders = jsonDecode(value.body);
}
}
orders is sent to a new widget: orderList(orders)
orderList is a listView
ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: orders.length,
itemBuilder: (orders, index) {
return return Card(
elevation: 5,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(orders[index]['facts']),
SizedBox(
width: 4,
),
Text('Cantidad : '),
Text(orders[index]['ITEMS'][0]['jeans']),
SizedBox(
width: 4,
),
IconButton(
onPressed: () => _reorderData(context, orders[index]),
icon: Icon(
Icons.replay_outlined,
color: Theme.of(context).accentColor,
)),
],
),
);
},
);
_reorderData is a function that make a get request, the info in shipped to ReorderModal
ReorderModal it only shows the information and has a button
void _reorderData(BuildContext ctx, order) async {
var data;
var url = 'serverAddress/${order['facts']}';
await http.get(url).then((value) {
data = jsonDecode(value.body);
data[0]['CORPORATION'] = order['corporation'];
showModalBottomSheet(
context: ctx,
builder: (_) {
return ReorderModal(data);
});
}).catchError((onError) {});
}
class ReorderModal extends StatelessWidget {
final List data;
ReorderModal(this.data);
void orderSend(orderInfo) async {
var url = 'serverAddress';
await http.post(url, body: orderInfo[0]).then((value) {
print(jsonDecode(value.body));
}).catchError((onError) {
print(onError);
});
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Column(
children: [
ElevatedButton(
onPressed: () {
orderSend(data);
//print(data);
},
child: Text('ONE Click'))
]),
);
}
}
when i press the ONE Click button execute the function orderSend, orderSend make a post request and the problem described above
This is the simplified code, I know it must be something very simple, but it is giving me a lot of work to solve
I am trying to implement a search function in my app for filtering between many entries in a list of map.
The data structure that I have is:
[{Entry: Accident , Definition: An unexpected event or circumstance without deliberate intent.}, {Entry: Accident Insurance , Definition: Insurance for unforeseen bodily injury.}, {Entry: Accident Only , Definition: An insurance contract that provides coverage, singly or in combination, for death, dismemberment, disability, or hospital and medical care caused by or necessitated as a result of accident or specified kinds of accident.}, {Entry: Accident Only or AD&D , Definition: Policies providing coverage, singly or in combination, for death, dismemberment, disability, or hospital and medical care caused by or necessitated as a result of accident or specified kinds of accidents. Types of coverage include student accident, sports accident, travel accident, blanket accident, specific accident or accidental death and dismemberment (ad&d).} ... etc, etc. ]
These are the contents of the .json file:
[
{
"Entry": "Accident ",
"Definition": "An unexpected event or circumstance without deliberate intent."
},
{
"Entry": "Accident Insurance ",
"Definition": "Insurance for unforeseen bodily injury."
},
[... and looooots of many other "Entry", "Definition" pairs like these]
{
"Entry": "Written Premium ",
"Definition": "The contractually determined amount charged by the reporting entity to the policyholder for the effective period of the contract based on the expectation of risk, policy benefits, and expenses associated with the coverage provided by the terms of the insurance contract."
}
]
Each map entry creates one button with an associated definition.
The user is queried for a search query to get only the button(s) that satisfy the query result.
I include the .dart file that I am trying to implement:
import 'package:flutter/material.dart';
import 'listentries.dart';
import 'destination.dart';
import 'dart:convert';
// ignore: must_be_immutable
class searchScreen extends StatefulWidget {
final String searchTerm;
searchScreen({this.searchTerm});
#override
_SearchScreenState createState() => new _SearchScreenState();
}
class _SearchScreenState extends State<searchScreen> {
#override
Widget build(BuildContext context) {
final widgetElements = new ListEntries(); // From listentries.dart
var searchedItems =
widgetElements; // Copy from widgetElements filter out from here
var query;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: Text(
"Search your term",
style: TextStyle(fontSize: 20),
),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (query) {
//search is done here
// filterSearchResults(query);
},
decoration: InputDecoration(
labelText: 'Search',
hintText: 'Search your term',
suffixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(25.0),
),
),
),
),
),
Expanded(
child: FutureBuilder(
future: DefaultAssetBundle.of(context)
.loadString('assets/data.json'),
builder: (context, snapshot) {
var entries = json.decode(snapshot.data.toString());
final item = entries.where((e) => e['Entry'] == 'Accident'); //Accident will be changed with query
print(item);
print(entries);
return ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
var entrada = entries[index];
//print(entrada);
return Container(
margin: EdgeInsets.symmetric(vertical: 2.0),
color: Colors.transparent,
width: MediaQuery.of(context).size.width,
height: 60,
child: RaisedButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Destination(
entry: entrada['Entry'],
definition: entrada['Definition'],
),
),
);
},
color: Colors.blue[900],
child: Text(
entrada['Entry'],
style: TextStyle(
color: Colors.white,
fontFamily: 'Raleway',
fontSize: 18.0,
),
),
),
);
},
itemCount: entries == null ? 0 : entries.length,
);
},
),
//child: searchedItems,
),
],
),
),
);
}
}
The issue that I am seeing is that the filtered result (item) is empty and it should contain the entry related to "Accident".
Could you give a hand for the implementation of this search functionality?
Thanks in advance
add .toList() to create new list
final item = entries.where((e) => e['Entry'] == 'Accident').toList();
Here is a stripped down code sample describing how to filter the items:
final List<Map<String, String>> items = [
{'Entry': 'Accident', 'Definition': 'Accident description.'},
{'Entry': 'Accident Insurance', 'Definition': 'Insurance description.'},
];
void main() {
final results = items.where((item) => item['Entry'] == 'Accident');
print(results);
// Iterable<Map<String, String>> ({Entry: Accident, Definition: An unexpected event or circumstance without deliberate intent.})
final result = results.first;
print(result);
// Map<String, String> {Entry: Accident, Definition: An unexpected event or circumstance without deliberate intent.}
}
Note that where returns an Iterable. You can use toList() to get a List of Maps.
Here is a Flutter sample application closer to what you did before:
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyWidget()));
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString('assets/entries.json'),
builder: (context, snapshot) {
final items = json.decode(snapshot.data.toString());
final result =
items.where((item) => item['Entry'] == 'Accident').first;
return Column(
children: [
Text('Accident Definition:'),
Text(result['Definition']),
],
);
},
),
);
}
}
The JSON File I used is here:
[
{
"Entry": "Accident",
"Definition": "Accident description."
},
{
"Entry": "Accident Insurance",
"Definition": "Insurance description."
}
]
I hope this meet all flutter developers well.
i'm working on an app and there are several issues i've been going through. here is my question.
i want to switch between selectedLocations and it will bring out forms related to the location out for users to fill.
here are the locations below in pictures and the codes follows.
void switchSelectedCountry(selection) {
events = selection;
scoops = selection;
setState(() {
_selectedLocation = selection;
});
}
formField: FixDropdownButtonFormField(
value: _selectedLocation,
hint: Text('Select'),
items: <String>['Scoops', 'Events',].map((String value) {
return new FixDropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (newValue) {
setState(() {
_selectedLocation = newValue;
});
},
),
),
you can check which value has been selected in drop down and based on that you can show specific form,
for example,
if(dropdownValue == 'events') Column(
children: <Widget>[
TextFormField(
controller: yearController,
decoration: InputDecoration(
labelText: AppLocalizations.of(context).translate(
'year'),
hintStyle: TextStyle(fontSize: 10),
filled: true,
),
keyboardType: TextInputType.number,
),
],
),
if(dropdownValue == 'Scoops') Sizebox(RETURN YOU WIDGET)
Is it possible to write a unit test that verifies that the maxLines property of a TextFormField is set correctly. I can not find a way to access the property:
i create a TextFormField
final field = TextFormField(
initialValue: "hello",
key: Key('textformfield'),
maxLines: 2,
);
then in the test i get access to the form field with tester.widget
final formfield =
await tester.widget<TextFormField>(find.byKey(Key('textformfield')));
but since the maxLines property is passed to the Builder which returns a Textfield, how can i get access to the textfield.
Or is there an completely other ways to verify this?
I don't know if this is a good solution but as i set the value of my TextFormField i can find the EditableText widget directly.
of this widget i can find test the property maxLines.
final EditableText formfield =
tester.widget<EditableText>(find.text('testvalue'));
expect(formfield.maxLines, 2);
The reason you can't see properties such as maxLines or maxLength is because they belong to the TextField class.
Take a look at the documentation of the TextFormField constructor in the source file:
/// Creates a [FormField] that contains a [TextField].
///
/// When a [controller] is specified, [initialValue] must be null (the
/// default). If [controller] is null, then a [TextEditingController]
/// will be constructed automatically and its `text` will be initialized
/// to [initialValue] or the empty string.
///
/// For documentation about the various parameters, see the [TextField] class
/// and [new TextField], the constructor.
Unfortunately you can't retrieve the TextField object from a TextFormField, you'll have to find the TextField object through a finder instead.
Let's assume you have a form with 2 fields - first name and last name. What you need to do is find all widgets of type TextField, add them to a list and then you can loop through each element of the list and run your test. Here's an example:
testWidgets('Form fields have the correct maximum length and number of lines',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Form(
child: Column(
children: <Widget>[
TextFormField(
key: Key('first_name'),
decoration: InputDecoration(hintText: 'First name'),
maxLines: 1,
maxLength: 50,
obscureText: true,
),
TextFormField(
key: Key('last_name'),
decoration: InputDecoration(hintText: 'Last name'),
maxLines: 1,
maxLength: 25,
),
],
),
),
),
));
List<TextField> formFields = List<TextField>();
find.byType(TextField).evaluate().toList().forEach((element) {
formFields.add(element.widget);
});
formFields.forEach((element) {
expect(element.maxLines, 1);
switch (element.decoration.hintText) {
case 'First name':
expect(element.maxLength, 50);
break;
case 'Last name':
expect(element.maxLength, 25);
break;
}
});
});
If you only one field, you could do this instead:
TextField textField = find.byType(TextField).evaluate().first.widget as TextField;
expect(textField.maxLines, 1);
expect(textField.maxLength, 50);
You can test it with an Integration test. The logic would be to type more text into the TextFormField that the one it's expected.
So we can verify the TextFormField is only allowing 2 character, as follow.
e.g. component:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SingleChildScrollView(
child: MyLoginPage(title: 'Flutter Demo Home Page'),
),
),
);
}
}
class MyLoginPage extends StatefulWidget {
MyLoginPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyLoginPageState createState() => _MyLoginPageState();
}
class _MyLoginPageState extends State<MyLoginPage> {
String _email;
String _password;
TextStyle style = TextStyle(fontSize: 25.0);
#override
Widget build(BuildContext context) {
final emailField = TextField(
key: Key('textformfield'),
obscureText: false,
maxLength: 2,
style: style,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
prefixIcon: Icon(FontAwesomeIcons.solidEnvelope),
hintText: "Email",
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red[300], width: 32.0),
borderRadius: BorderRadius.circular(97.0))),
onChanged: (value) {
setState(() {
_email = value;
});
},
);
final passwordField = TextField(
obscureText: true,
style: style,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
prefixIcon: Icon(FontAwesomeIcons.key),
hintText: "Password",
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red[300], width: 32.0),
borderRadius: BorderRadius.circular(25.0))),
onChanged: (value) {
setState(() {
_password = value;
});
},
);
return Center(
child: Column(
children: <Widget>[
Container(
color: Colors.yellow[300],
height: 300.0,
),
emailField,
passwordField,
],
),
);
}
}
The test:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_textfields_up/main.dart';
void main() {
testWidgets('Email should be only 2 characters', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
var txtForm = find.byKey(Key('textformfield'));
await tester.enterText(txtForm, '123');
expect(find.text('123'), findsNothing); // 3 characters shouldn't be allowed
expect(find.text('12'), findsOneWidget); // 2 character are valid.
});
}
Please observe I'm sending 3 character, and the TextFormField should only allow 2.
Hope this help.