Flutter: Can't add item to list with callback - flutter

In my this file I receive list of Properties from API and generate like this interface:
Problem:
When I tried enter volume and check result then receive null and new property not added to list. I add new property using setProperty callback in my code. All enteren properties will be saved in List<PropertyData> selectedProperties; variable.
Code:
class FarmerPropertyScreen extends StatefulWidget {
final bool isEdit;
FarmerPropertyScreen({Key key, this.isEdit}) : super(key: key);
#override
_FarmerPropertyScreenState createState() => _FarmerPropertyScreenState();
}
class _FarmerPropertyScreenState extends State<FarmerPropertyScreen> {
List<PropertyModel> properties;
ApiDataService apiDataService = getItInstance<ApiDataService>();
List<PropertyData> selectedProperties;
Future getProperties() async {
if (properties == null) {
var res = await apiDataService.getProperties();
if (res != null) {
print("Properties received from API");
setState(() {
properties = res;
});
}
}
}
#override
void initState() {
getProperties();
super.initState();
}
#override
void dispose() {
super.dispose();
}
void setProperty(double volume, int id) {
print("CALLBACK INFO");
print("ID");
print(id);
print("VOLUME");
print(volume);
if (selectedProperties != null && selectedProperties.length > 0) {
print("REMOVED TO UNIQUE LIST");
selectedProperties.removeWhere((item) => item.id == id);
}
if (volume > 0) {
print("ADDED NEW PROPERTY");
setState(() {
selectedProperties.add(PropertyData(id: id, volume: volume));
});
}
}
List<Widget> propertiesList() {
List<Widget> children = [];
if (properties != null && properties.length > 0) {
for (int i = 0; i < properties.length; i++) {
children.add(
FarmerObject(
id: properties[i].id,
title: properties[i].title,
volume: 0.0,
enabled: i == 0 ? true : false,
callback: setProperty,
),
);
if (i != properties.length - 1) {
children.add(
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
);
}
if (i == properties.length - 1) {
children.add(SizedBox(height: 45.h));
children.add(
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
minWidth: 206.w,
padding: EdgeInsets.symmetric(vertical: 12.h),
disabledColor: Colors.black12,
disabledTextColor: Colors.blueGrey,
onPressed: () {
print("CHECK ADDED PROPERTIES");
print(selectedProperties);
return null;
},
color: primaryColor,
child: Text(
'Check',
style: TextStyle(
color: Color(0xFF2B2B2B),
fontWeight: FontWeight.w500,
fontSize: 15.sp,
),
),
),
);
children.add(
SizedBox(height: MediaQuery.of(context).size.height * 0.06),
);
}
}
}
return children;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: true,
title: Text(widget.isEdit == true ? 'Edit' : 'Continue'),
),
body: Column(
children: [
widget.isEdit == true
? Container()
: Container(
padding: EdgeInsets.only(
top: 30.h,
left: 25.w,
right: 25.w,
),
child: Column(
children: [
Container(
alignment: Alignment.center,
child: Text(
'Step - 3',
style: TextStyle(
color: Color(0xFF2B2B2B),
fontSize: 14.sp,
),
),
),
SizedBox(height: 35.h),
SignUpHeader('What do you have?', 'give'),
SizedBox(height: 15.h),
],
),
),
Expanded(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
top: 10.h,
left: 25.w,
right: 25.w,
),
child: Column(
children: propertiesList(),
),
),
),
),
],
),
);
}
}
class FarmerObject extends StatefulWidget {
final int id;
final String title;
final double volume;
final bool enabled;
final Function(double, int) callback;
FarmerObject({
this.id,
this.volume,
this.callback,
this.title,
this.enabled,
});
#override
_FarmerObjectState createState() => _FarmerObjectState();
}
class _FarmerObjectState extends State<FarmerObject> {
bool status = false;
int divisions;
var priceController = new MoneyMaskedTextController(
decimalSeparator: '.', thousandSeparator: ',');
#override
void initState() {
setState(() {
if (widget.volume != null) {
priceController.updateValue(widget.volume);
}
status = widget.enabled;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 10, right: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.25),
spreadRadius: 0,
blurRadius: 2,
offset: Offset(0, 0),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(widget.title),
Switch(
value: status,
activeColor: primaryColor,
inactiveThumbColor: Color(0xFFAFAFAF),
onChanged: (bool val) {
setState(() {
status = val;
});
if (val == false || priceController.numberValue == 0) {
widget.callback(0.0, widget.id);
} else {
print("Setted again");
print(priceController.numberValue);
widget.callback(priceController.numberValue, widget.id);
}
})
],
),
status == true
? Column(
children: [
FadeInRight(
duration: Duration(milliseconds: 800),
child: Container(
// alignment: Alignment.centerRight,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.circular(5.0),
boxShadow: [
BoxShadow(
color: Colors.white,
// spreadRadius: 1,
// blurRadius: 3,
),
],
),
child: TextFormField(
validator: (value) {
return null;
},
controller: priceController,
onChanged: (val) {
print("VALUE CHANGED!");
double enteredPrice = priceController.numberValue;
widget.callback(enteredPrice, widget.id);
},
keyboardType: TextInputType.number,
textAlign: TextAlign.start,
maxLines: 1,
maxLength: 18,
maxLengthEnforced: true,
decoration: InputDecoration(
isDense: true,
hintText: 'Enter volume',
contentPadding: EdgeInsets.all(0),
counterText: '',
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: new BorderRadius.circular(10.0),
borderSide: BorderSide.none,
),
suffixIcon: Padding(
padding: EdgeInsets.all(15),
child: Text(
'volume',
textAlign: TextAlign.right,
style: TextStyle(
color: Color(0xFFAFAFAF),
fontSize: 14.sp,
),
),
),
),
),
),
),
],
)
: SizedBox(height: 0),
],
),
);
}
}
class PropertyData {
final int id;
final double volume;
PropertyData({this.id, this.volume});
}
Maybe my logic is wrong in this file to save new items in the list?

The issue is in List<PropertyData> selectedProperties;
By default, the value in selectedProperties will be null. So, when you are trying to add, it basically fails.
Replace it with, List<PropertyData> selectedProperties = [];

Related

setState not updating the value in the screen using Flutter desktop

So I'm trying to update the value of the drop down list using the setState, but when I select a value from the list, the value on the screen won't be able to change
import 'package:demo_app_admin/Custom/custom_Font.dart';
import 'package:flutter/material.dart';
import '/Custom/Custom_Raised_Bottun.dart';
import '/Custom/Header.dart';
import '/Custom/inputField.dart';
class LoginView extends StatefulWidget {
#override
_LoginViewState createState() => _LoginViewState();
}
class _LoginViewState extends State<LoginView> {
#override
Widget build(BuildContext context) {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final isKeyboard = MediaQuery.of(context).viewInsets.bottom != 0;
String _dropdownvalue = "Global admin";
List<String> _items = <String> ["Global admin", "Institution admin"];
return Scaffold(
body: Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(begin: Alignment.topCenter, colors: [
Colors.teal[700]!,
Colors.teal[200]!,
Colors.teal[400]!
]),
),
child: Column(
children: <Widget>[
const SizedBox(
height: 80,
),
if (!isKeyboard)
Header(
padding: const EdgeInsets.all(20),
crossAxisAlignment: CrossAxisAlignment.start,
head: "Welcome",
headTextStyle:
const TextStyle(color: Colors.white, fontSize: 40),
sizedBoxHeight: 10,
subtitle: "Sign In to Your Admin Account",
subtitleTextStyle:
const TextStyle(color: Colors.white, fontSize: 18),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(60),
topRight: Radius.circular(60),
)),
child: Padding(
padding: const EdgeInsets.all(30),
child: SingleChildScrollView(
reverse: true,
padding: EdgeInsets.all(32),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
const SizedBox(
height: 40,
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10)),
child: Column(
children: <Widget>[
InputField(
labelText: 'Email',
padding: EdgeInsets.all(10),
borderSide: BorderSide(color: Colors.grey),
hintText: "Enter your email",
color: Colors.grey,
inputBorder: InputBorder.none,
obscureText: false,
enableSuggestion: true,
autoCorrect: true,
onSaved: (value) {},
// (value){controller.email = value!;},
validator: (value) {
if (value == null) {
print("ERROR");
}
},
),
InputField(
labelText: 'Password',
padding: EdgeInsets.all(10),
borderSide: BorderSide(color: Colors.grey),
hintText: "Enter your password",
color: Colors.grey,
inputBorder: InputBorder.none,
obscureText: true,
enableSuggestion: false,
autoCorrect: false,
onSaved: (value) {},
// (value){ controller.password = value!;},
validator: (value) {
if (value == null) {
print("ERROR");
}
},
),
],
),
),
const SizedBox(
height: 40,
),
here is the issue
DropdownButton<String>(
value: _dropdownvalue,
icon: Icon(Icons.keyboard_arrow_down),
items: _items.map((String _items) {
return DropdownMenuItem<String>(
value: _items,
child: CustomFont(text: _items),
);
}).toList(),
onChanged: (value) {
setState(() => _dropdownvalue = value!);
}),
const SizedBox(
height: 40,
),
CustomRaisedButton(
width: 200,
height: 50.0,
margin: const EdgeInsets.symmetric(horizontal: 50),
color: Colors.teal.shade500,
borderRadius: BorderRadius.circular(10),
text: "LOGIN",
textColor: Colors.white,
fontSize: 17,
fontWeight: FontWeight.bold,
function: () {}
// {
// _formKey.currentState?.save();
// if(_formKey.currentState?.validate() !=null){
// controller.signInWithEmailAndPassword();
// }
// },
),
SizedBox(
height: 10,
),
],
),
),
),
),
))
],
),
),
);
}
}
The issue is here, you are declaring variables inside build. Therefore, variables get reset to default value on every build. Means setState.
Declare variables outside the build method.
class _LoginViewState extends State<LoginView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _dropdownvalue = "Global admin";
List<String> _items = <String>["Global admin", "Institution admin"];
#override
Widget build(BuildContext context) {
final isKeyboard = MediaQuery.of(context).viewInsets.bottom != 0;
return Scaffold(
More about StatefulWidget.

Access input data from dynamically created FormTextFields with validation in Flutter

I created a custom FormBuilderTextField with built in validation and am able to build them when called through LisView.Builder. My problem now is that I am unable to access the data within each generated FormBuilderTextField and pass that data to another page. I have a GlobalKey<FormState>() on the page with the ListView.Builder but cant seem to access that data. Any advice?
Custom TextField
TextFieldEntry({
required this.name,
});
final String name;
final myDecorationField = InputDecoration(
labelText: 'textFieldName',
labelStyle: TextStyle(fontSize: 20, color: Colors.pink),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(0),
bottomRight: Radius.circular(0),
),
borderSide: BorderSide(
width: 1,
color: Colors.pink,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(0),
bottomRight: Radius.circular(0),
),
borderSide: BorderSide(
color: Colors.pink,
width: 1,
),
),
);
#override
State<TextFieldEntry> createState() => _TextFieldEntryState();
}
class _TextFieldEntryState extends State<TextFieldEntry> {
Input model = Input();
var _validator;
var _keyboard;
var _myUnit;
Widget build(BuildContext context) {
var _onSaved = (value) {
model.title = value;
};
if (widget.name == 'height') {
final heightValidate = FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.numeric(context),
FormBuilderValidators.max(context, 200),
FormBuilderValidators.min(context, 30)
]);
_validator = heightValidate;
_keyboard = TextInputType.number;
_myUnit = MyUnit();
} else {
final weightValidate = FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.numeric(context),
FormBuilderValidators.max(context, 450),
FormBuilderValidators.min(context, 30)
]);
_validator = weightValidate;
_keyboard = TextInputType.number;
_myUnit = MyUnit(imperial: 'lbs', metric: 'kg');
}
return Padding(
padding: const EdgeInsets.all(2.0),
child: Column(
children: [
Container(
constraints: BoxConstraints(maxWidth: 375, maxHeight: 50),
child: Row(
children: [
Expanded(
child: FormBuilderTextField(
name: widget.name,
decoration: widget.myDecorationField
.copyWith(labelText: widget.name),
onSaved: _onSaved,
validator: _validator,
keyboardType: _keyboard,
),
),
Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.pink, width: 2)),
child: Row(
children: [
Container(
padding: EdgeInsets.all(5),
child: Center(
child: ValueListenableBuilder<Units>(
valueListenable: _myUnit,
builder: (context, unit, _) => AutoSizeText(
_myUnit.unitType,
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500),
)),
),
),
Builder(
builder: (context) {
if (widget.name == 'height' ||
widget.name == 'weight' ||
{
return Container(
constraints:
BoxConstraints(maxHeight: 50, maxWidth: 60),
child: TextButton(
onPressed: () {
Units unit = _myUnit.unit;
_myUnit.unit = unit == Units.unit1
? Units.unit2
: Units.unit1;
},
child: Center(
child: Icon(
Icons.loop,
size: 30,
color: Colors.white,
),
),
),
);
} else {
return Text('');
}
},
),
],
),
),
],
),
),
SizedBox(
height: 12,
)
],
),
);
}
}
ListView.Builder Page
var title;
Input({this.title});
}
class BMI extends StatefulWidget {
static const String id = 'BMI';
#override
State<BMI> createState() => _BMIState();
}
class _BMIState extends State<BMI> {
final _formKey = GlobalKey<FormState>();
List<Input> _bmiInputs = [
Input(title: 'height'),
Input(title: 'weight'),
Input(title: 'gender'),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: 'BMI',
),
drawer: MainDrawer(),
backgroundColor: Theme.of(context).colorScheme.secondary,
body: SingleChildScrollView(
child: Column(children: [
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxHeight: 500, maxWidth: 375),
child: ListView.builder(
itemCount: _bmiInputs.length,
itemBuilder: (BuildContext inp, index) {
return FittedBox(
child: TextFieldEntry(
name: _bmiInputs[index].title,
),
);
},
),
),
ElevatedButton(
child: Text('Calculate'),
onPressed: () {
_formKey.currentState?.save();
if (_formKey.currentState!.validate()) {}
print('Calculate');
// need to access Data here to pass to the calculator
// BMICalc bmiCalc;
// bmiCalc = BMICalc(
// height: ______,
// weight: ______,
// gender: _______,
// );
Navigator.push(context,
MaterialPageRoute(builder: (context) => ResultsScreen(bmi: bmiCalc.calculateBMI(),)));
})
],
),
),
])));
}
}
you use the onSaved as a parameter, you can get Data back to the first Widget
in the FormBuilderTextField it will be at the onSaved
ListView.Builder
return FittedBox(
child: TextFieldEntry(
name: _bmiInputs[index].title,
onSaved: (value) {
_bmiInputs[index].title = value;
}),
);
TextFieldEntry
typedef OnSaved(value);
TextFieldEntry({
required this.name, required this.onSaved
});
final String name;
final OnSaved onSaved;
...
...
FormBuilderTextField(
name: widget.name,
decoration: widget.myDecorationField
.copyWith(labelText: widget.name),
onSaved: widget.onSaved,
validator: _validator,
keyboardType: _keyboard,
),

Try correcting the name to the name of an existing method, or defining a method named 'fetchCity'

In order to get weather information about an entered city, I Created a fetch city function as bellow :
class CityModel {
final String? name;
final String? lat;
final String? lon;
var cityJSON;
CityModel({
this.name,
this.lat,
this.lon});
String get city{
return city;
}
Future<CityModel?> fetchCity(String cityName) async{
if (cityJSON == null) {
var link ="https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/cities.json";
var response = await http.get(Uri.parse(link));
if (response.statusCode == 200) {
cityJSON = json.decode(response.body);
}
}
for (var i = 0; i < cityJSON.length; i++) {
if (cityJSON[i]["name"].toString().toLowerCase() == cityName.toLowerCase()) {
return CityModel(
name: cityJSON[i]["name"].toString(),
lat: cityJSON[i]["latitude"].toString(),
lon: cityJSON[i]["longitude"].toString(),
);
}
}
return null;
}
}
then, I tried to call it in another file as shown bellow :
class CurrentWeather extends StatefulWidget {
final Function() updateData;
CurrentWeather(this.updateData);
#override
State<CurrentWeather> createState() => _CurrentWeatherState();
}
class _CurrentWeatherState extends State<CurrentWeather> {
bool searchBar = false;
bool updating = false;
var focusNode = FocusNode();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
if(searchBar)
setState(() {
searchBar = false;
});
},
child: GlowContainer(
height: MediaQuery.of(context).size.height - 250,
padding: EdgeInsets.only(top: 20, left: 30, right: 30, bottom: 20),
margin: EdgeInsets.all(2),
glowColor: Color(0xff00A1FF).withOpacity(0.8),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(60),
bottomRight: Radius.circular(60),
),
color: Color(0xff00A1FF),
spreadRadius: 5,
child: Column(
children: [
Container(
///////////// If the user click the Search bar
child: searchBar?
TextField(
focusNode: focusNode,
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10),),
fillColor: Color(0xff030317),
filled: true,
hintText: "Enter a city Name",
),
textInputAction: TextInputAction.search,
onSubmitted: (value) async{
//My call is here //////////
CityModel temp = await fetchCity(value);
if (temp == null) {
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
backgroundColor: Color(0xff030317),
title: Text("City not found "),
content: Text("Please check the city name"),
actions: [
TextButton(
onPressed: (){
Navigator.of(context).pop();
},
child: Text("ok")),
],
);
}
);
searchBar = false;
return;
}
city = temp.city;
lat = temp.lat!;
lon = temp.lon!;
updating = true;
setState(() {
});
widget.updateData;
searchBar = false;
updating = false;
setState(() {
});
},
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(CupertinoIcons.square_grid_2x2,
color: Colors.white,
),
Row(
children: [
Icon(CupertinoIcons.map_fill, color: Colors.white,),
GestureDetector(
onTap: (){
searchBar = true;
setState(() {
});
focusNode.requestFocus();
},
child: Text("" + city, style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
),
],
),
Icon(Icons.more_vert, color: Colors.white,),
],
),
),
Container(
margin: EdgeInsets.only(top: 10),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(width: 0.2, color: Colors.white),
borderRadius: BorderRadius.circular(30),
),
child: Text(
updating ? 'updating' : 'updated', style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Container(
height: 300,
child: Stack(
children: [
Image(
width:200 ,
height:200 ,
image: AssetImage(currentTemp!.image!),
fit: BoxFit.fill,
),
SizedBox(height: 10,),
Positioned(
bottom: 0,
right: 0,
left: 0,
child:Center(
child: Column(
children: [
GlowText(currentTemp!.current!.toString(),
style: TextStyle(
fontSize: 100,
fontWeight: FontWeight.bold,
),
),
Text(currentTemp!.name!,
style: TextStyle(fontSize: 20,),
),
Text(currentTemp!.day!,
style: TextStyle(fontSize: 18,),
),
],
),
),
),
],
),
),
Divider(color: Colors.white,),
SizedBox(height: 5,),
ExtraWeather(currentTemp!),
],
),
),
);
}
}
I'm getting this error:
The method 'fetchCity' isn't defined for the type '_CurrentWeatherState'.
Try correcting the name to the name of an existing method, or defining a method named 'fetchCity'.
in Advance thank you for your help
Since you declared your method inside CityModel you have to create an instance of it to call fetchCity.
final CityModel cityModel = CityModel();
final CityModel? city = awaitfetchCity('MyCity');
If you don't want to create an instance of CityModel you can either make fetchCity a global method or a static.
static Future<CityModel?> fetchCity(String cityName) async{ ... }
// Call the method
final CityModel? city = await CityModel.fetchCity('MyCity');

type '(dynamic) => Null' is not a subtype of 'TextEditingController?'

I've been trying to accept user input in an extracted TextFormField method(Name, contact and email) and then save them to my firestore database using TextEditingController.
Here lies the case after accepting the input, the name and email controller always displays the same input. After saving, even for test purposes, the database always shows that only null values have been returned from the controllers...
Please help me solve this problem...
Snippet of the code:
class Profile extends StatefulWidget {
final String email;
Profile({Key key, this.email}) : super(key: key);
get user => AuthService();
#override
_ProfileState createState() => _ProfileState(email);
}
class _ProfileState extends State<Profile> {
String email;
String contain;
_ProfileState(this.email);
bool showPhone = false;
//Text editing controller
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
// initState
#override
void initState() {
super.initState();
_emailController.addListener(() {
final String text = _emailController.text;
_emailController.value = _emailController.value.copyWith(
text: text,
selection:
TextSelection(baseOffset: text.length, extentOffset: text.length),
composing: TextRange.empty,
);
});
_phoneController.addListener(() {
final String contact = _phoneController.text;
_phoneController.value = _phoneController.value.copyWith(
text: contact,
selection: TextSelection(
baseOffset: contact.length,
extentOffset: contact.length,
),
composing: TextRange.empty,
);
});
_nameController.addListener(() {
final String contact = _nameController.text;
_nameController.value = _nameController.value.copyWith(
text: contact,
selection: TextSelection(
baseOffset: contact.length,
extentOffset: contact.length,
),
composing: TextRange.empty,
);
});
}
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
_emailController.dispose();
_phoneController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
final _formKey = GlobalKey<FormState>();
final doc = Database();
final ThemeData themeData = Theme.of(context);
String name, email, contact;
return loading
? Loading()
: SafeArea(
child: StreamBuilder<UserData>(
stream: Database(uid: doc.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Scaffold(
appBar: AppBar(
backgroundColor: Aqua,
elevation: 1,
title: Text(
'Edit Profile Info',
style: themeData.textTheme.headline1.copyWith(
color: White,
fontSize: 22,
letterSpacing: 0.75,
),
),
),
body: new Container(
height: window.physicalSize.height * 0.85,
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 25,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Center(
child: new Stack(
children: [
addVertical(20),
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
boxShadow: [
BoxShadow(
blurRadius: 15,
spreadRadius: 5,
color: Cyan.withOpacity(0.5),
offset: Offset(0, 15),
),
],
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
"assets/images/IMG2.jpg"),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
color: Cyan,
),
child: IconButton(
icon: Icon(
Icons.edit,
color: Dark_Blue,
),
onPressed:
() {}, // change profile picture
),
),
),
],
),
),
addVertical(30),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 25, left: 35, right: 35, bottom: 10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildTextFormField(
"Full Name",
userData.fname ??
"Enter Full Name here...",
false,
false ?? null,
),
addVertical(15),
buildTextFormField(
"Contact",
userData.contact ?? "020 *** ****",
true,
false ?? null,
),
addVertical(15),
buildTextFormField(
"E-mail",
userData.email ?? "emaple123#gmail.com",
false,
true ?? null,
),
],
),
),
),
),
addVertical(35),
Padding(
padding: const EdgeInsets.only(
left: 35, right: 35, top: 25),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
primary: White,
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"CANCEL",
style: themeData.textTheme.bodyText1
.copyWith(
letterSpacing: 1.25,
fontWeight: FontWeight.bold,
fontSize: 18,
color: Mid_Blue,
),
),
),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.currentUser;
// User person = result.user;
if (result != null) {
await Database(uid: doc.uid)
.updateData(
"$name",
"$email",
"$contact",
);
Toast.show(
"Your details have been successfully updated!",
context,
duration: Toast.LENGTH_LONG,
gravity: Toast.BOTTOM,
);
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
} else {
Toast.show(
"Please try again later",
context,
gravity: Toast.BOTTOM,
duration: Toast.LENGTH_LONG,
);
}
}
},
style: ElevatedButton.styleFrom(
elevation: 2,
primary: Colors.green[300],
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
), //!elevatedButton background
),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 18,
letterSpacing: 2.2,
color: White,
),
),
),
],
),
),
],
//),
),
),
),
);
} else {
return Loading();
}
},
),
);
}
Widget buildTextFormField(
String labelText,
String placeholder,
bool isPhone,
bool isEmail,
) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
child: TextField(
onSubmitted: isPhone || isEmail
? (value) {
value = _phoneController.toString() != null
? _emailController.toString() != null
: _nameController;
}
: null,
controller: isEmail || isPhone
? (value) {
value = _emailController.toString() != null
? _nameController.toString() != null
: _phoneController;
}
: _emailController,
obscureText: isPhone ? showPhone : false,
decoration: InputDecoration(
suffixIcon: isPhone
? IconButton(
onPressed: () => setState(() => showPhone = !showPhone),
icon: Icon(
Icons.remove_red_eye,
color: Red,
),
)
: null, //!This makes the icon appear only for the password field
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Aqua,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
hintText: placeholder,
hintStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Cyan,
),
),
keyboardType:
isPhone ? TextInputType.phone : TextInputType.emailAddress,
),
);
}
}
This is the function that builds name textField
buildTextFormField(
"Full Name",
userData.fname ?? "Enter Full Name here...",
false, // isPhone
false ?? null, // isEmail
),
From here we can see that isPhone = false; isEmail = false;
This is the function for the and controller in TextFormField:
controller: isEmail || isPhone
? (value) {
value = _emailController.toString() != null
? _nameController.toString() != null
: _phoneController;
}
: _emailController,
Since isEmail and isPhone are both false, it returns _emailController for name Textfield. So name is being updated on email textfield.
The function for onSubmitted() returns null if isEmail & isPhone are both false. Therefore, for name field, it will return null as the value (probably why you are getting null in the database).
I have tried to better organise the code below:
class MyHomePage extends StatefulWidget {
final String email;
MyHomePage({Key key, this.email}) : super(key: key);
get user => AuthService();
#override
_MyHomePageState createState() => _MyHomePageState(email);
}
class _MyHomePageState extends State<MyHomePage> {
String email;
// not used in this class
String contain;
_MyHomePageState(this.email);
bool showPhone = false;
//Text editing controller
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
_emailController.dispose();
_phoneController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
final _formKey = GlobalKey<FormState>();
final doc = Database();
final ThemeData themeData = Theme.of(context);
return loading
? Loading()
: SafeArea(
child: StreamBuilder<UserData>(
stream: Database(uid: doc.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
_emailController.text = userData.email;
_phoneController.text = userData.phone;
_nameController.text = userData.name;
return Scaffold(
appBar: AppBar(
backgroundColor: Aqua,
elevation: 1,
title: Text(
'Edit Profile Info',
style: themeData.textTheme.headline1.copyWith(
color: White,
fontSize: 22,
letterSpacing: 0.75,
),
),
),
body: new Container(
height: window.physicalSize.height * 0.85,
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 25,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Center(
child: new Stack(
children: [
addVertical(20),
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
boxShadow: [
BoxShadow(
blurRadius: 15,
spreadRadius: 5,
color: Cyan.withOpacity(0.5),
offset: Offset(0, 15),
),
],
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
"assets/images/IMG2.jpg"),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
color: Cyan,
),
child: IconButton(
icon: Icon(
Icons.edit,
color: Dark_Blue,
),
onPressed:
() {}, // change profile picture
),
),
),
],
),
),
addVertical(30),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 25, left: 35, right: 35, bottom: 10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildTextFormField(
"Full Name",
"Enter Full Name here...",
false,
_nameController,
),
addVertical(15),
buildTextFormField(
"Contact",
"020 *** ****",
true,
_phoneController,
),
addVertical(15),
buildTextFormField(
"E-mail",
"example123#gmail.com",
false,
_nameController,
),
],
),
),
),
),
addVertical(35),
Padding(
padding: const EdgeInsets.only(
left: 35, right: 35, top: 25),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
primary: White,
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"CANCEL",
style: themeData.textTheme.bodyText1
.copyWith(
letterSpacing: 1.25,
fontWeight: FontWeight.bold,
fontSize: 18,
color: Mid_Blue,
),
),
),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.currentUser;
// User person = result.user;
if (result != null) {
await Database(uid: doc.uid)
.updateData(
_nameController.text,
_emailController.text,
_phoneController.text,
);
Toast.show(
"Your details have been successfully updated!",
context,
duration: Toast.LENGTH_LONG,
gravity: Toast.BOTTOM,
);
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
} else {
Toast.show(
"Please try again later",
context,
gravity: Toast.BOTTOM,
duration: Toast.LENGTH_LONG,
);
}
}
},
style: ElevatedButton.styleFrom(
elevation: 2,
primary: Colors.green[300],
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
), //!elevatedButton background
),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 18,
letterSpacing: 2.2,
color: White,
),
),
),
],
),
),
],
//),
),
),
),
);
} else {
return Loading();
}
},
),
);
}
Widget buildTextFormField(
String labelText,
String placeholder,
bool isPhone,
TextEditingController controller,
) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
child: TextField(
// onSubmitted: isPhone || isEmail
// ? (value) {
// value = _phoneController.toString() != null
// ? _emailController.toString() != null
// : _nameController;
// }
// : null,
controller: controller,
obscureText: isPhone ? showPhone : false,
decoration: InputDecoration(
suffixIcon: isPhone
? IconButton(
onPressed: () => setState(() => showPhone = !showPhone),
icon: Icon(
Icons.remove_red_eye,
color: Red,
),
)
: null, //!This makes the icon appear only for the password field
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Aqua,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
hintText: placeholder,
hintStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Cyan,
),
),
keyboardType:
isPhone ? TextInputType.phone : TextInputType.emailAddress,
),
);
}
}

Flutter - TextField validation don't works

What I need to do is when the onPressed is called, I get the Textfield error when I don't enter text.
class _ExampleDialogTextState extends State<ExampleDialogText> {
FocusNode focusNode = FocusNode();
final textController = TextEditingController();
bool noText = false;
String nameList = "";
#override
void initState() {
super.initState();
nameList = "";
focusNode.addListener(() {
if (!focusNode.hasFocus) {
setState(() {
noText = nameList.length == 0;
});
FocusScope.of(context).requestFocus(focusNode);
}
});
}
TextField(
focusNode: focusNode,
autofocus: true,
controller: textController,
style: TextStyle(
color: Colors.black, fontSize: 14),
decoration: InputDecoration(
counterText: '',
errorText:
noText ? 'Value Can\'t Be Empty' : null,)
RaisedButton(
onPressed: () {
setState(() {
nameList.isEmpty
? noText = true
: noText = false;
});
},)
}
But still, with that code it doesn't work for me. Attached here is the entire class
code
Thank you!
Your Code is correct but you can not update state in ShowDialog Widget, so you have to return Statetful Widget in ShowDialog.
I added whole code which i change.
import 'package:flutter/material.dart';
class Consts {
Consts._();
static const double padding = 16.0;
static const double buttonPadding = 5.0;
}
class DeleteWidget extends StatefulWidget {
const DeleteWidget({Key key}) : super(key: key);
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueAccent,
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialogNameList();
},
backgroundColor: Colors.orange,
child: Icon(
Icons.add,
color: Colors.purple,
size: 40,
),
),
);
}
showDialogNameList() {
return showDialog(
context: context,
builder: (context) {
return CustomeDialog1();
});
}
}
class CustomeDialog1 extends StatefulWidget {
CustomeDialog1({Key key}) : super(key: key);
#override
_CustomeDialog1State createState() => _CustomeDialog1State();
}
class _CustomeDialog1State extends State<CustomeDialog1> {
FocusNode focusNode = FocusNode();
final textController = TextEditingController();
bool noText = false;
String nameList = "";
#override
void initState() {
super.initState();
nameList = "";
focusNode.addListener(() {
if (!focusNode.hasFocus) {
setState(() {
noText = nameList.length == 0;
});
FocusScope.of(context).requestFocus(focusNode);
}
});
}
#override
Widget build(BuildContext context) {
var screenHeight = MediaQuery.of(context).size.height;
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(Consts.padding),
),
elevation: 0.0,
child: Container(
height: screenHeight / 3,
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(
top: Consts.padding,
bottom: Consts.padding,
left: Consts.padding,
right: Consts.padding,
),
margin: EdgeInsets.only(top: 0),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(Consts.padding),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: const Offset(0.0, 10.0),
),
],
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
TextField(
focusNode: focusNode,
autofocus: true,
controller: textController,
cursorColor: Colors.white,
style: TextStyle(color: Colors.black, fontSize: 14),
decoration: InputDecoration(
counterText: '',
errorText: noText ? 'Value Can\'t Be Empty' : null,
hintText: 'List Name',
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green),
),
labelStyle: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
onChanged: (String text) {
nameList = text;
},
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 150.0,
height: 45.0,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
onPressed: () {
setState(() {
nameList.isEmpty
? noText = true
: noText = false;
});
},
padding:
EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
color: Color(0xFF2DA771),
child: Text('Add',
style: TextStyle(
color: Colors.white,
fontFamily: 'Roboto',
fontSize: 16)),
),
),
],
),
)
],
),
),
)
],
),
));
}
}