start date and end date validation in Flutter - flutter

In the below code I need to put validation like if the startdate is null give error to user or display the date chosen. I'm not sure how to do it.
String _displayText(String begin, DateTime? date) {
if (date != null) {
return '$begin Date: ${date.toString().split(' ')[0]}';
} else {
return 'Choose The Date';
}
}
In the below it will display the date
Text(
_displayText('Start', startdate),
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black),
),

To validate the date, we are checking the date and controlling validator using date not text. Like
String? endDateValidator(value) {
if (startDate != null && endData == null) {
return "select Both data";
}
if (endData == null) return "select the date";
if (endData!.isBefore(startDate!)) {
return "End date must be after startDate";
}
return null; // optional while already return type is null
}
Inside the form TextFormFiled will be
TextFormField(
controller: endDateController,
readOnly: true,
decoration: const InputDecoration(
hintText: 'Choose The Date',
),
onTap: () async {
endData = await pickDate();
endDateController.text = _displayText("start", endData);
setState(() {});
},
validator: endDateValidator,
),
Test on DartPad.
Widget to play
class DateValidationInForm extends StatefulWidget {
DateValidationInForm({Key? key}) : super(key: key);
#override
State<DateValidationInForm> createState() => _DateValidationInFormState();
}
class _DateValidationInFormState extends State<DateValidationInForm> {
/// more about validation https://docs.flutter.dev/cookbook/forms/validation
final _formKey = GlobalKey<FormState>();
String _displayText(String begin, DateTime? date) {
if (date != null) {
return '$begin Date: ${date.toString().split(' ')[0]}';
} else {
return 'Choose The Date';
}
}
final TextEditingController startDateController = TextEditingController(),
endDateController = TextEditingController();
DateTime? startDate, endData;
Future<DateTime?> pickDate() async {
return await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1999),
lastDate: DateTime(2999),
);
}
String? startDateValidator(value) {
if (startDate == null) return "select the date";
}
String? endDateValidator(value) {
if (startDate != null && endData == null) {
return "select Both data";
}
if (endData == null) return "select the date";
if (endData!.isBefore(startDate!)) {
return "End date must be after startDate";
}
return null; // optional while already return type is null
}
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: startDateController,
decoration: const InputDecoration(
hintText: 'Choose The Date',
),
onTap: () async {
startDate = await pickDate();
startDateController.text = _displayText("start", startDate);
setState(() {});
},
readOnly: true,
validator: startDateValidator,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black),
),
TextFormField(
controller: endDateController,
readOnly: true,
decoration: const InputDecoration(
hintText: 'Choose The Date',
),
onTap: () async {
endData = await pickDate();
endDateController.text = _displayText("start", endData);
setState(() {});
},
validator: endDateValidator,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black),
),
ElevatedButton(
onPressed: () {
_formKey.currentState?.validate();
},
child: Text("submit"),
)
],
),
);
}
}
Check cookbook/forms/validation to learn more.

Related

Put request in flutter ,does not update the object when I change only one field on edit screen

When making a put request and I edit all the field the object is updated. But if I only update one field on the edit screen than the object is not update. I think this is because the unchanged controllers do not keep the value that is already given to them in the Set state method.
Can anyone give me some hints how to solve this ?
import 'package:fin_app/apiservice/variables.dart';
import 'package:fin_app/screens/reminder/reminder_screen.dart';
import 'package:fin_app/screens/login_screen/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../models/user.dart';
import '../monthly_expense/expense_app_theme.dart';
const String myhomepageRoute = '/';
const String myprofileRoute = 'profile';
class ReminderEdit extends StatefulWidget {
final String id;
final String title;
final String date;
final int amount;
const ReminderEdit({Key? key, required this.id, required this.title,required this.date,required this.amount}) : super(key: key);
#override
_ReminderEditState createState() => _ReminderEditState();
}
class _ReminderEditState extends State<ReminderEdit> {
final GlobalKey<FormState> _formKey = GlobalKey();
String id="";
final TextEditingController _titleInput = TextEditingController();
final TextEditingController _dateInput = TextEditingController();
final TextEditingController _amountInput = TextEditingController();
Future<Reminder>? _futureReminder;
Future<void> edit(String id,String title,String date,int amount) async {
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
};
final editReminder = jsonEncode({
'title': title,
'date': date,
'amount': amount
});
if (_titleInput.text.isNotEmpty &&
_dateInput.text.isNotEmpty ) {
var response = await http.put(
Uri.parse("$baseUrl/reminder/me/$id/details"),
body: editReminder,
headers: requestHeaders);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Profile edited succesfully.")));
} else {
print(response.statusCode);
print(id);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Could not edit the reminder.")));
}
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Please fill out the field.")));
}
}
#override
void initState() {
_dateInput.text = ""; //set the initial value of text field
super.initState();
setState(() {
id = widget.id;
_titleInput.text = widget.title;
_dateInput.text = widget.date;
_amountInput.text=widget.amount.toString();
});
}
void navigate() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AlarmPage()),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ExpenseAppTheme.background,
body: Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: <Widget>[
const Align(
alignment: Alignment.topLeft,
child: Text("Edit reminder",
style: TextStyle(
fontSize: 24,
)),
),
const SizedBox(
height: 20,
),
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
TextFormField(
controller: _titleInput,
decoration: const InputDecoration(
labelText: 'Title',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
borderSide:
BorderSide(color: Colors.grey, width: 0.0),
),
border: OutlineInputBorder()),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null ||
value.isEmpty ||
value.contains(RegExp(r'^[a-zA-Z\-]'))) {
return 'Use only text!';
}
},
),
const SizedBox(
height: 20,
),
TextFormField(
controller:_dateInput, //editing controller of this TextField
decoration: InputDecoration(
icon: Icon(Icons.calendar_today), //icon of text field
labelText: "Enter Date" //label text of field
),
readOnly:
true, //set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
2000), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('yyyy-MM-dd').format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
//you can implement different kind of Date Format here according to your requirement
setState(() {
_dateInput.text =
formattedDate; //set output date to TextField value.
});
} else {
print("Date is not selected");
}
},
),
const SizedBox(
height: 35,
),
TextFormField(
controller: _amountInput,
decoration: const InputDecoration(
labelText: 'Amount',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
borderSide:
BorderSide(color: Colors.grey, width: 0.0),
),
border: OutlineInputBorder()),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Amount must not be empty';
} else if (value.contains(RegExp(r'^[0-9]*$'))) {
return 'Amount must only contain numbers';
}
},
), const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(60)),
onPressed: () {
edit(widget.id,_titleInput.text,_dateInput.text,int.parse(_amountInput.text));
navigate();
},
child: const Text("Submit"),
),
],
),
),
],
),
),
);
}
FutureBuilder<Reminder> buildFutureBuilder() {
return FutureBuilder<Reminder>(
future: _futureReminder,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Expense added');
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
);
}
}

how to make a button disable in flutter

I'am trying to create a profile form . I want to disable or enable the button based on the validation. I tried to check the currentstate of form validated or not. it is working fine but when i type on one of the textFormField , I am getting error message on all of the textFormField .
Here is the code i'am working.
import 'dart:html';
import 'package:flutter/material.dart';
class ProfileForm extends StatefulWidget {
const ProfileForm({Key? key}) : super(key: key);
#override
State<ProfileForm> createState() => _ProfileFormState();
}
class _ProfileFormState extends State<ProfileForm> {
final _formKey = GlobalKey<FormState>();
DateTime selectedDate = DateTime.now();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _dobController = TextEditingController();
final _currentLocationController = TextEditingController();
final _locationIndiaController = TextEditingController();
bool btndisable = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
RegExp emailValidator = RegExp(
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$');
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
onChanged: () {
notdisbled();
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Let’s create a profile for you",
style: AppTextStyleSecondary.headline2sec,
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.only(right: 100.0),
child: Text(
"Please enter your personal details",
),
),
const SizedBox(
height: 16,
),
Text("Your name"),
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
onChanged: (val) {
},
validator: (val) {
if (val.toString().length < 2) {
return "Please enter your name";
}
return null;
},
controller: _nameController,
decoration: const InputDecoration(
isDense: true,
contentPadding:
EdgeInsets.symmetric(horizontal: 10, vertical: 12)),
),
Text(labelText: "Date of Birth"),
InkWell(
child: TextFormField(
onTap: () {
_selectDate(context);
},
validator: (val) {
if (val.toString().isEmpty) {
return "This field is required";
} else {
return null;
}
},
readOnly: true,
controller: _dobController,
),
),
Text("Email address"),
TextFormField(
controller: _emailController,
onChanged: (val) {},
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
String email = val!.trim();
if (emailValidator.hasMatch(email)) {
return null;
} else {
return "Enter valid email address";
}
},
decoration: const InputDecoration(
isDense: true,
contentPadding:
EdgeInsets.symmetric(horizontal: 10, vertical: 12)),
),
Text(labelText: "Your location where you currently live"),
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
if (val.toString().isEmpty) {
return "This field is required";
} else {
return null;
}
},
onChanged: (val) {
},
controller: _currentLocationController,
),
Text("Your location in India"),
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
if (val.toString().isEmpty) {
return "This field is required";
} else {
return null;
}
},
onChanged: (val) {},
controller: _locationIndiaController,
),
const SizedBox(
height: 25,
),
Container(
width: double.infinity,
height: 45,
decoration: BoxDecoration(
color: btndisable ? Colors.blue : Colors.red,
borderRadius: BorderRadius.circular(8),
),
child: TextButton(
onPressed: (){
if(_formKey.currentState!.validate()){
Navigator.of(context).pushNamedAndRemoveUntil(
'HomeScreen', (Route<dynamic> route) => false);
}
}
child: Text("Continue"),
),
),
const SizedBox(
height: 20,
),
],
),
),
)),
);
}
Future<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1970),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate) {
setState(() {
selectedDate = picked;
_dobController.text =
"${selectedDate.day}/${selectedDate.month}/${selectedDate.year}";
});
}
}
void notdisbled() {
if (_formKey.currentState!.validate() == false) {
setState(() {
btndisable = false;
});
} else {
setState(() {
btndisable = true;
});
}
}
}
for disable the button change to this :
child: TextButton(
onPressed: _formKey.currentState != null && _formKey.currentState.validate() ? () {
Navigator.of(context).pushNamedAndRemoveUntil(
'HomeScreen', (Route<dynamic> route) => false);
}: null,
child: AppText(
text: "Continue",
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColor.white),
),
but when you use
formKey.currentState!.validate()
when you type any thing, flutter try call it every time so your form widget run all validator func in all its textfield. so it is natural for all textfield to get error message

The method 'changeeventdescription' was called on null

I have looked at many answers to other questions but I think I am missing something still. I am developing a flutter app and the page that I am having trouble with is a page where I create meetings for a calendar.
When I look at the debug log I see that the eventProvider is null. I copied the structure from another screen in my app and everything works fine there. What have I missed?
Here is the code from the screen:
final eventRef = FirebaseFirestore.instance.collection(('events'));
class AddEventScreen extends StatefulWidget {
static const String id = 'add_event_screen';
final Event event;
AddEventScreen([this.event]);
#override
_AddEventScreenState createState() => _AddEventScreenState();
}
class _AddEventScreenState extends State<AddEventScreen> {
final _db = FirebaseFirestore.instance;
final eventNameController = TextEditingController();
final eventStartTimeController = TextEditingController();
final eventDurationController = TextEditingController();
final eventDateController = TextEditingController();
final eventDescriptionController = TextEditingController();
#override
void dispose() {
eventNameController.dispose();
eventStartTimeController.dispose();
eventDurationController.dispose();
eventDateController.dispose();
eventDescriptionController.dispose();
super.dispose();
}
bool showSpinner = false;
String eventName;
String eventStartTime;
String eventDuration;
String eventData;
String eventDescription;
getCurentAgencyEvents() async {
if (globals.agencyId == null) {
eventNameController.text = "";
eventStartTimeController.text = "";
eventDurationController.text = "";
eventDateController.text = "";
eventDescriptionController.text = "";
} else {
final DocumentSnapshot currentEvent =
await eventRef.doc(globals.agencyId).get();
// existing record
// Updates Controllers
eventNameController.text = currentEvent.data()["name"];
eventStartTimeController.text = currentEvent.data()['address1'];
eventDurationController.text = currentEvent.data()['address2'];
eventDateController.text = currentEvent.data()['city'];
eventDescriptionController.text = currentEvent.data()['state'];
// Updates State
new Future.delayed(Duration.zero, () {
final eventProvider =
Provider.of<EventProvider>(context, listen: false);
eventProvider.loadValues(widget.event);
});
}
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final eventProvider = Provider.of<EventProvider>(context);
final _firestoreService = FirestoreService();
DateTime _date = new DateTime.now();
TimeOfDay _time = new TimeOfDay.now();
Duration initialtimer = new Duration();
DateTime _selectedDate = DateTime.now();
TimeOfDay _timePicked = TimeOfDay.now();
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/Appbar_logo.png',
fit: BoxFit.cover, height: 56),
],
),
),
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Event Editor',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w800,
),
),
SizedBox(
height: 30.0,
),
TextField(
controller: eventNameController,
keyboardType: TextInputType.text,
textAlign: TextAlign.center,
onChanged: (value) {
eventProvider.changeeventname(value);
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Event Name', labelText: 'Event Name'),
),
SizedBox(
height: 8.0,
),
TextField(
controller: eventDurationController,
textAlign: TextAlign.center,
onChanged: (value) {
eventProvider.changeeventduration(value);
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Duration', labelText: 'Duration'),
),
SizedBox(
height: 8.0,
),
TextField(
controller: eventDateController,
keyboardType: TextInputType.datetime,
textAlign: TextAlign.center,
onTap: () async {
DateTime _datePicked = await showDatePicker(
context: context,
initialDate: _selectedDate,
//firstDate: new DateTime.now().add(Duration(days: -365)),
firstDate: new DateTime(2020),
lastDate: new DateTime(2022));
if (_date != null && _date != _datePicked) {
setState(() {
eventDateController.text =
DateFormat("MM/dd/yyyy").format(_datePicked);
eventProvider.changeeventdate(_datePicked);
_selectedDate = _datePicked;
//DateFormat("MM/dd/yyyy").format(_date));
});
}
},
onChanged: (value) {
eventProvider.changeeventdate(_date);
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Date',
labelText: 'Date',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: eventStartTimeController,
keyboardType: TextInputType.text,
textAlign: TextAlign.center,
onTap: () async {
TimeOfDay _timePicked = await showTimePicker(
context: context,
initialTime: new TimeOfDay.now());
//if (_timePicked != null) {
setState(() {
eventStartTimeController.text = _timePicked.format(context);
});
//}
},
onChanged: (value) {
eventProvider.changeeventstarttime(_timePicked);
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Start Time',
labelText: 'Start Time',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: eventDescriptionController,
keyboardType: TextInputType.text,
textAlign: TextAlign.center,
onChanged: (value) {
eventProvider.changeeventdescription(value);
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Description', labelText: 'Description'),
),
SizedBox(
height: 8.0,
),
RoundedButton(
title: 'Save Event',
colour: Colors.blueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
globals.newEvent = true;
eventProvider.saveEvent();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => AppointmentCalendarScreen()));
setState(() {
showSpinner = false;
});
} catch (e) {
// todo: add better error handling
print(e);
}
},
),
SizedBox(
height: 8.0,
),
(widget != null)
? RoundedButton(
title: 'Delete',
colour: Colors.red,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
//agencyProvider.deleteAgency(globals.currentUid);
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => AppointmentCalendarScreen()));
setState(() {
showSpinner = false;
});
} catch (e) {
// todo: add better error handling
print(e);
}
},
)
: Container()
],
),
),
),
),
);
}
}
Here is the debug log and I get the same message for each input control:
======== Exception caught by widgets ===============================================================
The following NoSuchMethodError was thrown while calling onChanged:
The method 'changeeventname' was called on null.
Receiver: null
Tried calling: changeeventname("Test event")
Here is the code for the EventProvider:
class EventProvider with ChangeNotifier {
final firestoreService = FirestoreService();
FirebaseFirestore _db = FirebaseFirestore.instance;
final eventRef = FirebaseFirestore.instance.collection(('events'));
String _eventName;
TimeOfDay _eventStartTime;
String _eventDuration;
DateTime _eventDate;
String _eventDescription;
//Getters
String get eventName => _eventName;
TimeOfDay get eventStartTime => _eventStartTime;
String get eventDuration => _eventDuration;
DateTime get eventDate => _eventDate;
String get eventDescription => _eventDescription;
final EventProvider eventProvider = new EventProvider();
//Setters
changeeventname(String value) {
_eventName = value;
notifyListeners();
}
changeeventstarttime(TimeOfDay value) {
_eventStartTime = value;
notifyListeners();
}
changeeventduration(String value) {
_eventDuration = value;
notifyListeners();
}
changeeventdate(DateTime value) {
_eventDate = value;
notifyListeners();
}
changeeventdescription(String value) {
_eventDescription = value;
notifyListeners();
}
loadValues(Event event) {
_eventName = event.eventName;
_eventStartTime = event.eventStartTime;
_eventDuration = event.eventDuration;
_eventDate = event.eventDate;
_eventDescription = event.eventDescription;
}
saveEvent() {
var newEvent = Event(
eventName: _eventName,
eventStartTime: _eventStartTime,
eventDuration: _eventDuration,
eventDate: _eventDate,
eventDescription: _eventDescription);
// If the agency is a new agency retrieve the agency
// document ID and save it to a new agent document
if (globals.newEvent == true) {
String id = _db.collection('event').doc().id;
globals.agencyId = id;
//firestoreService.saveNewEvent(newEvent);
eventProvider.saveEvent();
globals.newEvent = false;
} else {
firestoreService.saveEvent(newEvent);
}
}
}
I found the issue. I just needed to clean up the code.

Validator works for all fields except one in flutter

I have a screen to add a customer's details. Name, company, mobile no.,email,profile picture. I have added validator to all the fields. Validator is working for all the fields but not for one field, the top one(Name). I dont know why. When I place the field below all the fields then the validator works but I want to keep the field on top. I want Name field to contain some value.
add_person.dart
import 'dart:io';
import 'package:email_validator/email_validator.dart';
import 'package:vers2cts/models/customer_model.dart';
import 'package:vers2cts/models/language.dart';
import 'package:vers2cts/models/languages_widget.dart';
import 'package:vers2cts/models/languages_model.dart';
import 'package:vers2cts/screens/people_list.dart';
import 'package:vers2cts/services/db_service.dart';
import 'package:vers2cts/utils/form_helper.dart';
import 'search_contacts.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:image_picker/image_picker.dart';
class AddPerson extends StatefulWidget {
String uname;
final String appBarTitle;
final CustomerModel customer;
AddPerson(this.uname,this.customer,this.appBarTitle);
#override
State<StatefulWidget> createState() {
return AddPersonState(uname,customer,appBarTitle);
}
}
class AddPersonState extends State<AddPerson> {
GlobalKey<FormState> globalFormKey = GlobalKey<FormState>();
String uname,email,mobnum;
AddPersonState(this.uname,this.customer,this.appBarTitle);
bool engcheckbox=false;
String appBarTitle;
CustomerModel customer=new CustomerModel();
LanguagesModel langs=new LanguagesModel();
DBService dbService=new DBService();
bool showPassword = false;
DateTime _date=DateTime.now();
TextEditingController datefield=TextEditingController();
PickedFile _imageFile;
final ImagePicker _picker=ImagePicker();
TextEditingController custfNameController = TextEditingController();
TextEditingController custlNameController = TextEditingController();
TextEditingController custMobileNoController = TextEditingController();
TextEditingController custCompanyController = TextEditingController();
TextEditingController addrController = TextEditingController();
TextEditingController custEmailController = TextEditingController();
void getImage(ImageSource source) async{
final pickedFile=await _picker.getImage(
source:source);
setState(() {
_imageFile=pickedFile;
customer.cust_photo = _imageFile.path;
});
}
Future<Null> _selectDate(BuildContext context)async {
DateTime _datePicker = await showDatePicker(
context: context,
initialDate: _date,
firstDate: DateTime(1947),
lastDate: DateTime(2030),);
if (_datePicker != null && _datePicker != _date) {
setState(() {
_date = _datePicker;
String formattedDate = DateFormat('dd-MM-yyyy').format(_date);
datefield.text=formattedDate.toString();
print(datefield.text);
});
}
}
#override
Widget build(BuildContext context){
var selectedLanguages = languages.where((element) => element.selected);
TextStyle textStyle=Theme.of(context).textTheme.title;
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var _minimumPadding = 5.0;
custfNameController.text = customer.first_name;
custlNameController.text = customer.last_name;
custMobileNoController.text = customer.mob_num;
custCompanyController.text=customer.org_name;
addrController.text=customer.addr;
custEmailController.text=customer.email_id;
return WillPopScope(
onWillPop: () {
moveToLastScreen();
},
child: Scaffold(
appBar: AppBar(
title: Text(appBarTitle),
elevation: 1,
actions: [
IconButton(
icon: Icon(
Icons.search,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => SearchContacts()));
},
),
],
),
body:Form(
key: globalFormKey,
child: Container(
padding: EdgeInsets.only(left: 16, top: 25, right: 16),
child:
GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: ListView(
children: [
ImageProfile(customer.cust_photo),
SizedBox(
height: 35,
),
buildTextField("Name",custfNameController,
(value) => updatefName(value),(value)=>checkfname(value)),
buildTextField("Mobile",custMobileNoController,
(value) => updateMobile(value),(value)=>checkmobile(value)),
buildTextField("Company",custCompanyController,
(value) => updateCompany(value),(value)=>checkempty(value)),
buildTextField("Email",custEmailController,
(value) => updateEmail(value),(value)=>checkmail(value)),
Text("Address"),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: addrController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(width: 2.0),)),
keyboardType: TextInputType.multiline,
minLines: 5,//Normal textInputField will be displayed
maxLines: 5, // when user presses enter it will adapt to it
onChanged: (value) {
this.customer.addr = value;
},
),
),
SizedBox(
height: height * 0.02,
),
Divider(),
SizedBox(
height: height * 0.02,
),
Row(
children: <Widget>[
Expanded(
child: Text("Show on Call",
style: textStyle,
)
),
SizedBox(
height: height * 0.02,
),
SizedBox(
height: 35,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
OutlineButton(
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
onPressed: () {},
child: Text("CANCEL",
style: TextStyle(
fontSize: 14,
letterSpacing: 2.2,
color: Colors.black)),
),
RaisedButton(
onPressed: () {
setState(() {
_saveCustomer();
});
},
//color: Colors.purpleAccent,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.symmetric(horizontal: 50),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 14,
letterSpacing: 2.2,
color: Colors.white),
),
)
],
)
],
),
),
),
)));
}
Widget bottomSheet(){
return Container(
height: 100,
width: MediaQuery.of(context).size.width ,
margin: EdgeInsets.symmetric(
horizontal:20,
vertical:20,
),
child: Column(
children: <Widget>[
Text("Choose profile photo",
style: TextStyle(fontSize: 20.0),),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton.icon(
onPressed: (){
getImage(ImageSource.camera);
},
icon:Icon(Icons.camera,color: Theme.of(context).primaryColor,), label:Text("camera")),
FlatButton.icon(
onPressed: (){
getImage(ImageSource.gallery);
},
icon:Icon(Icons.photo_library), label:Text("Gallery"))
],
)
],
),
);
}
//This is for 1st 3 Textfields name,mobile,company
Widget buildTextField(String labelText,tController,Function onChanged,Function validator) {
return Padding(
padding: const EdgeInsets.only(bottom: 35.0),
child: TextFormField(
controller:tController,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle:TextStyle(),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
onChanged: (String value) {
return onChanged(value);
},
validator:(String value) {
return validator(value);
},
),
);
}
void moveToLastScreen() {
Navigator.pop(context, true);
}
bool validateAndSave() {
final form = globalFormKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
void _saveCustomer() async {
if (validateAndSave()) {
var result;
var res;
var mob = await dbService.checkcustMobno(mobnum,uname);
var mail = await dbService.checkcustEmail(email,uname);
if (mob != null) {
FormHelper.showAlertDialog(
context, 'Error',
'Customer with this mobile number already exists');
}
else if (mail != null) {
FormHelper.showAlertDialog(
context, 'Error',
'Customer with this email id already exists');
}else {
if (customer.cust_id != null) { // Case 1: Update operation
result = await dbService.updateCustomer(customer);
} else { // Case 2: Insert Operation
result = await dbService.insertCustomer(customer);
}
if (result != 0) { // Success
moveToLastScreen();
FormHelper.showAlertDialog(
context, 'Status', 'Customer Saved Successfully');
} else { // Failure
FormHelper.showAlertDialog(
context, 'Status', 'Problem Saving Customer');
}
languages.forEach((lang) async {
print("${lang.name} : ${lang.selected}");
if (lang.selected) {
LanguagesModel language = LanguagesModel(lang: lang.name);
print("customer id from lang");
print(customer.cust_id);
await dbService.insertLanguages(language);
}
});
}
}
}
String updatefName(String value) {
customer.first_name = custfNameController.text;
customer.cre_by=uname;
print(uname);
}
String updatelName(String value) {
if(value.isEmpty)
customer.last_name = " ";
else
customer.last_name = custlNameController.text;
}
String updateMobile(value) {
customer.mob_num = custMobileNoController.text;
mobnum=value;
}
String updateEmail(value) {
customer.email_id = custEmailController.text;
email=value;
}
String updateCompany(String value) {
customer.org_name = custCompanyController.text;
}
String checkfname(String value){
if(value.isEmpty)
{
return "First name can't be null";
}
return null;
}
String checkempty(String value){
print("checkempty called");
if(value.isEmpty)
{
return "Can't be null";
}
return null;
}
String checkmobile(String value) {
print("checkmobile called");
if(value.isEmpty)
{
return 'Please enter phone no.';
}
if(value.length<6)
{
return 'Please enter a valid phone no.';
}
return null;
}
String checkmail(String value){
print("checkmail called");
if(value.isEmpty)
{
return 'Please Enter E-mail address';
}
if (!EmailValidator.validate(value)) {
return 'Please enter a valid Email';
}
return null;
}
}
Replace the ListView (that the buildTextFields are its children) with a Column and wrap the Column with a SingleChildScrollView.

Flutter: How to assign other value (object) as Text Editing Controller in Another Field?

I'm building a form that has contact name and phone number fields. User will be able to choose (tap) contact from previously saved contact list, and this should display name and phone numbers at their respective fields.
To achieve that I'm using TypeAheadFormField from Flutter_Form_Builder Package version: 3.14.0 to build my form.
I successfully assign _nameController from local database in the default TypeAheadFormField controller.
But I can't assign _mobileController from the same choice I tapped to FormBuilderTextField.
I managed to get "name'-value with TypeAheadFormField, but everytime I switch the choices from suggestions,
the _mobileController.text didn't update on FormBuilderTextField
My code as follow:
import 'package:myApp/customer.dart';
import 'package:myApp/db_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
class MyForm extends StatefulWidget {
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
DatabaseHelper _dbHelper;
Customer _customer = Customer();
List<Customer> _customerList = [];
final _formKey = GlobalKey<FormBuilderState>();
final _cfKey = GlobalKey<FormBuilderState>();
final _nameController = TextEditingController();
final _inputContactNameController = TextEditingController();
final _inputContactPhoneController = TextEditingController();
var _mobileController = TextEditingController();
#override
void initState() {
super.initState();
_refreshBikeSellerList();
setState(() {
_dbHelper = DatabaseHelper.instance;
});
_mobileController = TextEditingController();
_mobileController.addListener(() {
setState(() {});
});
}
#override
void dispose() {
_mobileController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: FormBuilder(
key: _formKey,
child: Column(
children: [
FormBuilderTypeAhead(
attribute: 'contact_person',
initialValue: _customer,
controller: _nameController,
onChanged: (val) {},
itemBuilder: (context, Customer _customer) {
return ListTile(
title: Text(_customer.name),
subtitle: Text(_customer.mobile),
);
},
selectionToTextTransformer: (Customer c) => c.name,
suggestionsCallback: (query) {
if (query.isNotEmpty) {
var lowercaseQuery = query.toLowerCase();
return _customerList.where((_customer) {
return _customer.name
.toLowerCase()
.contains(lowercaseQuery);
}).toList(growable: false)
..sort((a, b) => a.name
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(
b.name.toLowerCase().indexOf(lowercaseQuery)));
} else {
return _customerList;
}
},
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontSize: 17,
letterSpacing: 1.2,
color: Colors.black,
fontWeight: FontWeight.w300,
),
// controller: guessMotor1,
),
onSuggestionSelected: (val) {
if (val != null) {
return _customerList.map((_customer) {
setState(() {
_mobileController.text = _customer.mobile;
});
}).toList();
} else {
return _customerList;
}
},
),
FormBuilderTextField(
controller: _mobileController,
attribute: 'mobile',
readOnly: true,
style: TextStyle(fontSize: 17),
decoration: InputDecoration(
hintText: 'mobile',
),
),
SizedBox(height: 40),
Container(
child: RaisedButton(
onPressed: () async {
await manageContact(context);
},
child: Text('Manage Contact'),
),
),
],
),
),
);
}
manageContact(BuildContext context) async {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Manage Contact',
textAlign: TextAlign.center,
),
titleTextStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: Colors.black45,
letterSpacing: 0.8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
content: FormBuilder(
key: _cfKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// SizedBox(height: 10),
InkResponse(
onTap: () {},
child: CircleAvatar(
radius: 30,
child: Icon(
Icons.person_add,
color: Colors.grey[100],
),
backgroundColor: Colors.grey[500],
),
),
SizedBox(height: 10),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
maxLength: 20,
controller: _inputContactNameController,
textAlign: TextAlign.start,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
attribute: 'contact_person',
decoration: InputDecoration(
prefixIcon: Icon(
Icons.person_outline,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.name = val;
_formKey
.currentState.fields['contact_person'].currentState
.validate();
});
},
autovalidateMode: AutovalidateMode.always,
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(20),
FormBuilderValidators.minLength(2),
],
),
),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
attribute: 'phone_number',
controller: _inputContactPhoneController,
textAlign: TextAlign.start,
keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(
Icons.phone_android,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.mobile = val;
_formKey.currentState.fields['phone_number'].currentState
.validate();
});
},
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.numeric(),
],
valueTransformer: (text) {
return text == null ? null : num.tryParse(text);
},
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
color: Colors.white,
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
RaisedButton(
color: Colors.grey[400],
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
try {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
if (_customer.id == null)
await _dbHelper.insertBikeContact(_customer);
else
await _dbHelper.updateCustomer(_customer);
_refreshBikeSellerList();
_formKey.currentState.reset();
_inputContactNameController.clear();
_inputContactPhoneController.clear();
Navigator.of(context).pop();
}
} catch (e) {
print(e);
}
},
)
],
),
],
),
),
),
);
}
_refreshBikeSellerList() async {
List<Customer> x = await _dbHelper.getCustomer();
setState(() {
_customerList = x;
});
}
}
Is there any possible way to update _mobileController as I tap?
Any help would be much appreciated.
Thank you in advance.
EDITED:
class where I save the customer data:
class Customer {
int id;
String name;
String mobile;
static const tblCustomer = 'Customer';
static const colId = 'id';
static const colName = 'name';
static const colMobile = 'mobile';
Customer({
this.id,
this.name,
this.mobile,
});
Map<String, dynamic> toMap() {
var map = <String, dynamic>{colName: name, colMobile: mobile};
if (id != null) map[colId] = id;
return map;
}
Customer.fromMap(Map<String, dynamic> map) {
id = map[colId];
name = map[colName];
mobile = map[colMobile];
}
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Customer &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
#override
String toString() {
return name;
}
}
Here is my database:
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'customer.dart';
class DatabaseHelper {
static const _databaseVersion = 1;
static const _databaseName = 'Kiloin.db';
DatabaseHelper._();
static final DatabaseHelper instance = DatabaseHelper._();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
Directory dataDirectory = await getApplicationDocumentsDirectory();
String dbPath = join(dataDirectory.path, _databaseName);
return await openDatabase(
dbPath,
version: _databaseVersion,
onCreate: _onCreateDB,
);
}
_onCreateDB(Database db, int version) async {
await db.execute('''
CREATE TABLE ${Customer.tblCustomer}(
${Customer.colId} INTEGER PRIMARY KEY AUTOINCREMENT,
${Customer.colName} TEXT NOT NULL,
${Customer.colMobile} TEXT NOT NULL
)
''');
}
Future<int> insertBikeContact(Customer customer) async {
Database db = await database;
return await db.insert(Customer.tblCustomer, customer.toMap());
}
Future<List<Customer>> getCustomer() async {
Database db = await database;
List<Map> contact = await db.query(Customer.tblCustomer);
return contact.length == 0
? []
: contact.map((e) => Customer.fromMap(e)).toList();
}
Future<int> updateCustomer(Customer customer) async {
Database db = await database;
return await db.update(Customer.tblCustomer, customer.toMap(),
where: '${Customer.colId}=?', whereArgs: [customer.id]);
}
Future<int> deleteContact(int id) async {
Database db = await database;
return await db.delete(Customer.tblCustomer,
where: '${Customer.colId}=?', whereArgs: [id]);
}
}
The value that you get from onSuggestionSelected is the customer. Use that value to update _mobileController.text.
onSuggestionSelected: (customer) {
if (customer != null) {
setState(() {
_mobileController.text = customer.mobile;
});
}
}