Reputation: 27
I have implemented a TextField that allows user input in my flutter app. Now, I would like to save the user input data to a variable on pressing a button in order to use it later on. As I see it, in order to achieve this, I have to create a specific instance of the stateful widget and of its state so that when I call the widget to extract the variable, it does not create a new version of the widget with an empty TextField. The textfields in question are ProjectNameField and ProjectDescriptionField. Here is my implementation:
ProjectDescriptionField savedDescription = ProjectDescriptionField();
ProjectNameField savedName = ProjectNameField();
AddPage savedPage = AddPage();
_AddPageState savedPageState = _AddPageState();
List<String> image_list_encoded = [];
class AddPage extends StatefulWidget {
final _AddPageState _addPageState = _AddPageState();
AddPage({Key? key}) : super(key: key);
@override
_AddPageState createState() => _AddPageState();
}
class _AddPageState extends State<AddPage> {
List<Asset> images = <Asset>[];
String _error = NOERROR;
@override
Widget build(BuildContext context) {
if (images.isEmpty)
return AppLayout(logicalHeight * 0.15);
else
return AppLayout(logicalHeight * 0.01);
}
List<Asset> getImages() {
return images;
}
Widget AppLayout(double distanceFromImages) {
return Scaffold(
appBar: AppBar(
leading: BackButton(color: Colors.white),
title: Text(CREATEPROJECT),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.check,
color: Colors.white,
),
onPressed: () {
SavedData _savedData = SavedData(
savedName._nameFieldState.myController.value.text,
savedDescription
._descriptionFieldState.myController.value.text,
savedPage._addPageState.getImages());
print(_savedData.saved_Project_Description);
print(_savedData.saved_Project_Name);
print(_savedData.saved_images.toString());
saveNewProject(_savedData);
},
),
],
),
body: Column(children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
child: Center(
child: Container(
height: logicalHeight * 0.05,
width: logicalWidth * 0.9,
child: savedName)),
),
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: ElevatedButton(
child: Text(PICKIMAGES),
onPressed: loadAssets,
)),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: getWidget())),
Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
child: Center(child: Container(child: savedDescription)),
),
]),
);
}
Widget getWidget() {
if (images.length > 0) {
return Container(
height: logicalHeight * 0.4,
width: logicalWidth * 0.8,
child: ImagePages());
} else {
return Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.15, 0, 0),
child: Container(child: Text(NOPICTURESSELECTED)));
}
}
PageView ImagePages() {
final PageController controller = PageController(initialPage: 0);
List<Widget> children = [];
images.forEach((element) {
children.add(Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.01, 0, logicalWidth * 0.01, 0),
child: AssetThumb(
asset: element,
width: 1000,
height: 1000,
)));
});
return PageView(
scrollDirection: Axis.horizontal,
controller: controller,
children: children,
);
}
}
class ProjectNameField extends StatefulWidget {
ProjectNameFieldState _nameFieldState = ProjectNameFieldState();
@override
ProjectNameFieldState createState() {
return _nameFieldState;
}
}
class ProjectNameFieldState extends State<ProjectNameField> {
final myController = TextEditingController();
@override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: myController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: PROJECTNAME,
),
maxLength: 30,
);
}
}
class ProjectDescriptionField extends StatefulWidget {
ProjectDescriptionFieldState _descriptionFieldState =
ProjectDescriptionFieldState();
@override
ProjectDescriptionFieldState createState() {
return _descriptionFieldState;
}
}
class ProjectDescriptionFieldState extends State<ProjectDescriptionField> {
final myController = TextEditingController();
@override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: myController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: PROJECTDESCRIPTION,
),
minLines: 1,
maxLines: 5,
maxLength: 5000,
);
}
}
class SavedData {
String saved_Project_Name = "";
String saved_Project_Description = "";
List<Asset> saved_images = [];
SavedData(String saved_Project_Name, String saved_Project_Description,
List<Asset> saved_images) {
this.saved_Project_Name = saved_Project_Name;
this.saved_Project_Description = saved_Project_Description;
this.saved_images = saved_images;
}
}
I believe the problem lies here (ProjectNameField and ProjectDescriptionField are essentially the same, with only minor differences):
class ProjectNameField extends StatefulWidget {
ProjectNameFieldState _nameFieldState = ProjectNameFieldState();
@override
ProjectNameFieldState createState() {
return _nameFieldState;
}
}
class ProjectNameFieldState extends State<ProjectNameField> {
final myController = TextEditingController();
@override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: myController,
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: PROJECTNAME,
),
maxLength: 30,
);
}
}
When I let createState() return _nameFieldState instead of ProjectNameFieldState(), the following error occurs:
The createState function for ProjectDescriptionField returned an old or invalid state instance: ProjectDescriptionField, which is not null, violating the contract for createState.
'package:flutter/src/widgets/framework.dart':
Failed assertion: line 4681 pos 7: 'state._widget == null'
However, when I return ProjectNameFieldState(), then this code
onPressed: () {
SavedData _savedData = SavedData(
savedName._nameFieldState.myController.value.text,
savedDescription
._descriptionFieldState.myController.value.text,
savedPage._addPageState.getImages());
print(_savedData.saved_Project_Description);
print(_savedData.saved_Project_Name);
print(_savedData.saved_images.toString());
saveNewProject(_savedData);
}
does not save the project name. How can I get rid of this error and save the project name and project description? Thank you!
Upvotes: 0
Views: 1597
Reputation: 355
You can use the Form
and TextFormFiled
Widgets to easily save the value in your variable and also validate the form fields.
Here is the code snippet:
Declare a GlobalKey
for the Form
Widget
final _formKey = GlobalKey<FormState>();
In your Scaffold
body where you will putting your TextFormFiled
add the Form
Widget as the parent for all the fields
Form(
key: _formKey,
child: Column(children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
child: Center(
child: Container(
height: logicalHeight * 0.05,
width: logicalWidth * 0.9,
child: TextFormField(
controller: projectNameController,
decoration: InputDecoration(
hintText: PROJECTNAME,
border: UnderlineInputBorder()),
maxLength: 30
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectName = value),
)),
),
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: ElevatedButton(
child: Text(PICKIMAGES),
onPressed: loadAssets,
)),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: getWidget())),
Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
child: Center(child: Container(child: TextFormField(
controller:projectDescriptionController ,
decoration: InputDecoration(
hintText: PROJECTDESCRIPTION,
border: UnderlineInputBorder()),
minLines: 1,
maxLines: 5,
maxLength: 5000,
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectDescription = value))),
),
]),
),
Now in your IconButton
's onPressed
validate the form and use the save function to save the TextFormField
values in your variable.
final form = _formKey.currentState;
if (form.validate()) {
form.save();
}
Now here in the above code form.validate()
will invoke the validator
and form.save()
will invoke onSaved
property of the TextFormField
Here is the complete code:
import 'package:flutter/material.dart';
class AddPage extends StatefulWidget {
AddPage({Key? key}) : super(key: key);
@override
_AddPageState createState() =>
_AddPageState();
}
class _AddPageState extends State<AddPage> {
final _formKey = GlobalKey<FormState>();
String savedProjectName;
String savedProjectDescription;
final projectNameController = TextEditingController();
final projectDescriptionController = TextEditingController();
double logicalHeight; //your logical height
double logicalWidth; //your logical widgth
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: BackButton(color: Colors.white),
title: Text(CREATEPROJECT),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.check,
color: Colors.white,
),
onPressed: () {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
}
},
),
],
),
body:Form(
key: _formKey,
child: Column(children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
child: Center(
child: Container(
height: logicalHeight * 0.05,
width: logicalWidth * 0.9,
child: TextFormField(
controller: projectNameController,
decoration: InputDecoration(
hintText: PROJECTNAME,
border: UnderlineInputBorder()),
maxLength: 30,
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectName = value),
)),
),
Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: ElevatedButton(
child: Text(PICKIMAGES),
onPressed: loadAssets,
)),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
child: getWidget())),
Padding(
padding: EdgeInsets.fromLTRB(
logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
child: Center(child: Container(child: TextFormField(
controller:projectDescriptionController ,
decoration: InputDecoration(
hintText: PROJECTDESCRIPTION,
border: UnderlineInputBorder()),
minLines: 1,
maxLines: 5,
maxLength: 5000,
validator: (value) {
if (value.isEmpty) {
return 'This is a required field';
}
return null;
},
onSaved: (value) => savedProjectDescription = value),)),
),
]),
),
);
}
}
Upvotes: 1