List of TextFields clearing when they're updated - Flutter - flutter

I am creating a simple grocery list creator in Flutter. I am trying to go about this by having a plus button that will add ingredient text fields when you press it. Here is what I have done:
body: Container(
padding: EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 30.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text(
'Ingredients ',
style: GoogleFonts.biryani(fontSize: 15.0)),
IconButton(
icon: new Icon(Icons.add),
onPressed: () {
setState(() {
countings++;
});
debugPrint('$countings');
},
)
],
),
SizedBox(height: 10.0),
ListOfIngsWidget(countings, key: UniqueKey())
],
),
)
And here is the ListOfIngsWidget:
class ListOfIngsWidget extends StatefulWidget {
final int countIngs;
const ListOfIngsWidget(this.countIngs, {Key key}) : super(key: key);
#override
_ListOfIngsState createState() => _ListOfIngsState();
}
class _ListOfIngsState extends State<ListOfIngsWidget> {
List<TextEditingController> _controllerList = [];
List<TextEditingController> _numControllerList = [];
List<Widget> _ingList = [];
#override
void initState() {
for (int i = 1; i <= widget.countIngs; i++) {
TextEditingController controller = TextEditingController();
TextField textField = TextField(
controller: controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Ingredient $i',
),
);
TextEditingController numcontroller = TextEditingController();
TextField numField = TextField(
controller: numcontroller,
decoration: InputDecoration(
border: OutlineInputBorder(), hintText: '#', labelText: '#'),
keyboardType: TextInputType.number,
);
_ingList.add(Row(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: SizedBox(
width: 250,
child: textField,
)),
Text('x', style: GoogleFonts.biryani(fontSize: 15)),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: SizedBox(
width: 75,
child: numField,
))
],
));
_controllerList.add(controller);
_numControllerList.add(numcontroller);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return new Container(
child: Flexible(
child: ListView(children: _ingList),
),
);
}
}
The only problem is that if you press the plus button after you have already entered values into one of the textFields, it will clear the field. I kind of understand why this is happening, but is there a way to work around this?

I might be missing some proper disposal of textControllers but here's the gist. As for further reading into keys and why they're necessary, I'd read this medium post
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
final _controllerList = <TextEditingController>[];
final _numControllerList = <TextEditingController>[];
/*
If the user had previous ingredients (say from a db), then you
would fill _controllerList and _numControllerList here using
the old ingredients to populate.
#override
void initState() {
for (ingredient in previousIngredients) {
final controller = TextEditingController(text: ingredient.text);
final numController = TextEditingController();
_controllerList.add(controller);
_numControllerList.add(numController);
}
super.initState();
}
*/
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 30.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text('Ingredients'),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
_controllerList.add(TextEditingController());
_numControllerList.add(TextEditingController());
});
},
),
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
if (_controllerList.isEmpty) return;
setState(() {
_controllerList.removeLast();
_numControllerList.removeLast();
});
},
)
],
),
SizedBox(height: 10.0),
ListOfIngsWidget(_controllerList, _numControllerList),
],
),
),
);
}
}
class ListOfIngsWidget extends StatelessWidget {
ListOfIngsWidget(this.controllerList, this.numControllerList)
: assert(controllerList.length == numControllerList.length);
final List<TextEditingController> controllerList;
final List<TextEditingController> numControllerList;
#override
Widget build(BuildContext context) {
final _ingList = <Widget>[];
for (var i = 0; i < controllerList.length; i++) {
final textField = TextField(
controller: controllerList[i],
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Ingredient $i',
),
);
final numField = TextField(
controller: numControllerList[i],
decoration: InputDecoration(
border: OutlineInputBorder(), hintText: '#', labelText: '#'),
keyboardType: TextInputType.number,
);
_ingList.add(
Row(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: SizedBox(
width: 250,
child: textField,
)),
Text('x', style: GoogleFonts.biryani(fontSize: 15)),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: SizedBox(
width: 75,
child: numField,
),
)
],
),
);
}
return Container(
child: Flexible(
child: ListView(children: _ingList),
),
);
}
}

Related

Update value TextField using TextEditingController

I want to update value in TextField but it become like this
And this my issue code
Widget build(BuildContext context) {
final certificatesData = Provider.of < Certificates > (context);
final cerData = certificatesData.certData;
if (cerData != null) {
print("test in");
inspectionTypeDis = cerData['inspectionType'];
_clientDataL = clientDis;
if (_clientDataL != null) {
getClientEmailL(_clientDataL);
}
}
if (emailClientDataL != null) {
_emailClientDataL = clientEmailDis;
}
return Form(
key: _formKey,
child: AlertDialog(
title: Container(
color: Color.fromARGB(255, 75, 185, 159),
child: Text('Edit',
textAlign: TextAlign.center, style: TextStyle(color: Color.fromARGB(255, 250, 251, 250))),
padding: const EdgeInsets.all(17),
margin: const EdgeInsets.all(0),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: < Widget > [
SizedBox(
width: 630,
height: 100,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: < Widget > [
Container(
width: 310,
height: 20,
// color: Colors.purple[600],
child: ListTile(
title: Text('Inspection Type'),
subtitle: TextField(
controller: TextEditingController(text: inspectionTypeDis),
onChanged: (text) {
inspectionTypeDis = text;
},
decoration: const InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
),
),
),
),
),
]),
],
));
}
Detail Code
class EditCertificateInspection extends StatefulWidget with InputValidationMixin {
EditCertificateInspection({
Key key
}): super(key: key);
#override
_EditCertificateInspection createState() => _EditCertificateInspection();
}
class InputValidationMixin {}
class _EditCertificateInspection extends State < EditCertificateInspection > {
final navigatorKey = GlobalKey < NavigatorState > ();
final _formKey = GlobalKey < FormState > ();
Widget build(BuildContext context) {
final certificatesData = Provider.of < Certificates > (context);
final cerData = certificatesData.certData;
if (cerData != null) {
print("test in");
inspectionTypeDis = cerData['inspectionType'];
_clientDataL = clientDis;
if (_clientDataL != null) {
getClientEmailL(_clientDataL);
}
}
if (emailClientDataL != null) {
_emailClientDataL = clientEmailDis;
}
return Form(
key: _formKey,
child: AlertDialog(
title: Container(
color: Color.fromARGB(255, 75, 185, 159),
child: Text('Edit',
textAlign: TextAlign.center, style: TextStyle(color: Color.fromARGB(255, 250, 251, 250))),
padding: const EdgeInsets.all(17),
margin: const EdgeInsets.all(0),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: < Widget > [
SizedBox(
width: 630,
height: 100,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: < Widget > [
Container(
width: 310,
height: 20,
child: ListTile(
title: Text('Inspection Type'),
subtitle: TextField(
controller: TextEditingController(text: inspectionTypeDis),
onChanged: (text) {
inspectionTypeDis = text;
},
decoration: const InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
),
),
),
),
),
]),
],
));
},
}
I will suggest creating a state variable for TextEditingController for statefulwidget and to set the text. use
TextEditingController.fromValue(TextEditingValue(text: inspectionTypeDis));
Widget structure can be
final TextEditingController controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Consumer<Certificates>(builder: (context, value, child) {
final cerData = value....;
if (cerData != null) {
// your logic
controller.text = "";
}
return TextFormField(
controller: controller,
);
});
}
What you could to is to change your TextField for a TextFormField with an initialValue.
TextFormField(
initialValue: cerData['inspectionType'],
onChanged: (text) {
inspectionTypeDis = text;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4))),
),
EDIT
Here is a fully example using a StatefulWidget using the snippet I provide above.
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
String _inspectionTypeDis = 'initialValue';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TextFormField(
initialValue: _inspectionTypeDis,
onChanged: (text) {
_inspectionTypeDis = text;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4))),
),
),
TextButton(
child: const Text('Print value'),
onPressed: () => print(_inspectionTypeDis)),
],
),
);
}
}

Adding values from multiple textFields

I have a list of tiles created with the 'tolist' method, each has a textField and controller.I want to get the sum of the values of all textFields into a variable and display as text.``
here is my code: `
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> myList = [
'Materials',
'Labour',
'Plant and Equipment',
'Subcontractor'
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
),
body: SingleChildScrollView(
child: Column(
children: [
ExpansionTile(
maintainState: true,
title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('Test Code'),
Text('sum of all here',//sum of all values from each textfield here
style: TextStyle(fontSize: 16),),
],
),
children: myList.map((cost) {
return MyListTile(cost);
}).toList(),
),
],
),
));
}
}
and MyListTile code :``
class MyListTile extends StatefulWidget {
String title;
MyListTile(this.title) : super();
#override
State<MyListTile> createState() => _MyListTileState();
}
class _MyListTileState extends State<MyListTile> {
final TextEditingController _myController = TextEditingController();
double materialCost = 0.0;
#override
Widget build(BuildContext context) {
return ListTile(
subtitle: Row(
children: [
Container(
margin: const EdgeInsets.only(top: 5, bottom: 5, right: 0, left: 0),
child: SizedBox(
height: 35,
width: 150,
child: TextField(
textAlignVertical: TextAlignVertical.center,
controller: _myController,
showCursor: true,
keyboardType: TextInputType.number,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 10),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15)),
disabledBorder: const OutlineInputBorder(),
filled: true,
labelText: 'Cost sum',
labelStyle: TextStyle(color: Colors.grey[500]),
hintText: 'Enter Cost',
hintStyle: TextStyle(color: Colors.grey[500]),
suffixIcon: InkWell(
child: const Icon(
Icons.clear,
),
onTap: () {
_myController.clear();
},
),
// isCollapsed: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15))),
),
),
),
Container(
margin: const EdgeInsets.all(3),
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
border: Border.all(color: Colors.white10, width: 1),
borderRadius: BorderRadius.circular(12)),
child: InkWell(
onTap: () {
setState(() {
materialCost = double.parse(_myController.text);
});
},
child: const Icon(
Icons.done,
),
),
)
],
),
trailing: Column(
children: [
Container(
margin: const EdgeInsets.all(3),
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(10)),
child: Text(
materialCost.toString(),
style: const TextStyle(
// color: mainColorShade,
fontSize: 14,
fontWeight: FontWeight.bold),
),
)
],
),
title: Text(
widget.title,
),
);
;
}
}
I have tried to find a solution from allover the internet and I can not get any
example
create textControllers for each of your textfields and pass it to your textfield inside your listTile:
class MyHomePage extends StatefulWidget {
...
}
class _MyHomePageState extends State<MyHomePage> {
List<String> myList = [
'Materials',
'Labour',
'Plant and Equipment',
'Subcontractor'
];
// look here: list of controllers for your need change it for your liking
List<TextEditingController> controllers = [
TextEditingController(),
TextEditingController(),
TextEditingController(),
TextEditingController(),
];
// look here: local state to store your sum of textfields
String sum = "";
#override
void initState() {
super.initState();
// look here: this will change sum value whenever either of the textfield's value changed
for (var i = 0; i < myList.length; i++) {
controllers[i].addListener(() {
setState(() {
sum = getSum(controllers);
});
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
),
body: SingleChildScrollView(
child: Column(
children: [
ExpansionTile(
maintainState: true,
title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Test Code'),
// look here: this is your sum text
Text(sum,style: TextStyle(fontSize: 16),),
],
),
children: [
// look here: pass the controllers to your mylistTile widgets
for (var i = 0; i < myList.length; i++)
MyListTile(
title: cost,
controller: controllers[i],
),
],
),
],
),
));
}
// if you want to change the sum result, change it here
String getSum(List<TextEditingController> controllers) {
return controllers.map((e) => "${e.text} ").toString();
}
}
Don't forget to do this in your MyListTile widget, otherwise you can't pass the controllers
class MyListTile extends StatefulWidget {
MyListTile({
required this.title,
required this.controller
}) : super();
final String title;
final TextEditingController controller;
#override
State<MyListTile> createState() => _MyListTileState();
}
Use widget.controller in your MyListTile instead of _myController
class _MyListTileState extends State<MyListTile> {
final TextEditingController _myController = TextEditingController();
double materialCost = 0.0;
#override
Widget build(BuildContext context) {
return ListTile(
subtitle: Row(
children: [
Container(
margin: const EdgeInsets.only(top: 5, bottom: 5, right: 0, left: 0),
child: SizedBox(
height: 35,
width: 150,
child: TextField(
textAlignVertical: TextAlignVertical.center,
// look here:
controller: widget.controller,
...
// rest of your code here

How to make a floating search bar

The ones I found in different sites all have a search icon on the header and the search bar only appears when it is clicked but I want a search bar that is already there but also with a search button that is connected to it
Illustration:
Try this code(also with the logic to use the Search Functionality). If you like it.
Full Code
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool hasFocus = false;
FocusNode focus = FocusNode();
TextEditingController searchController = TextEditingController();
#override
void initState() {
super.initState();
focus.addListener(() {
onFocusChange();
});
searchController.addListener(() {
filterClints();
});
}
#override
void dispose() {
searchController.dispose();
focus.removeListener(onFocusChange);
super.dispose();
}
void onFocusChange() {
if (focus.hasFocus) {
setState(() {
hasFocus = true;
});
} else {
setState(() {
hasFocus = false;
});
}
}
#override
Widget build(BuildContext context) {
bool isSearching = searchController.text.isNotEmpty;
return Scaffold(
body: Column(
children: [
Container(
child: Padding(
padding: const EdgeInsets.only(
left: 5,
right: 5,
top: 0,
bottom: 7,
),
child: TextField(
focusNode: focus,
controller: searchController,
// style: TextStyle(fontSize: 14, ),
decoration: InputDecoration(
hintText: "Search",
label: const Text("Search customers & places"),
contentPadding: const EdgeInsets.symmetric(vertical: 2),
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(50)),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
),
),
prefixIcon: const Icon(
Icons.search,
),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (hasFocus)
InkWell(
onTap: () {},
child: const Icon(
Icons.clear,
color: Colors.grey,
),
),
const SizedBox(
width: 10,
),
PopupMenuButton(
icon: const Icon(Icons.more_vert_sharp,
color: Colors.grey),
itemBuilder: (context) => [
const PopupMenuItem(),
PopupMenuItem(),
],
onSelected: (value) {},
)
],
),
),
),
),
),
],
),
);
}
}
Preview
You can change the values prefixIcon/suffixIcon in textField to suit your needs.

Sliding Text Form Fields in Flutter

I want to make text fields like this which transition horizontally exactly like this when I click next. How can I achieve it?
Link of the 4 sec video:
https://firebasestorage.googleapis.com/v0/b/my-chat-app-524e3.appspot.com/o/WhatsApp%20Video%202022-01-14%20at%207.42.17%20AM.mp4?alt=media&token=6eeddcf9-b145-437f-b049-121acd1209b1
well we can make simply animated that with transfrom, or just use pageview class
check this out :
import 'package:flutter/material.dart';
class SlidingTextField extends StatefulWidget {
const SlidingTextField({Key? key}) : super(key: key);
#override
_SlidingTextFieldState createState() => _SlidingTextFieldState();
}
class _SlidingTextFieldState extends State<SlidingTextField> {
late PageController _pageController;
late List<TextEditingController> _tecList;
#override
void initState() {
super.initState();
_pageController = PageController();
_tecList = List.generate(3, (index){
return TextEditingController();
});
}
#override
void dispose() {
for (var tec in _tecList) {
tec.dispose();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Material(
child: Column(
children: [
Expanded(
child: Container(
color: Colors.cyan,
),
),
Padding(
padding: const EdgeInsets.all(25.0),
child: Container(
height: 120,
child: PageView.builder(
controller: _pageController,
itemCount: 3,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text(
"Details ${index+1}",
style: const TextStyle(color: Colors.grey),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: TextField(
decoration: InputDecoration(
enabledBorder: _oInputBorder(),
focusedBorder: _oInputBorder()),
textInputAction: TextInputAction.done,
controller: _tecList[index],
onSubmitted: (text) {
int nextPage = index + 1;
if (nextPage < 3) {
_pageController.animateToPage(nextPage,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
}
},
),
)
],
);
},
),
),
),
Expanded(
child: Container(
color: Colors.yellow,
),
)
],
),
);
}
InputBorder _oInputBorder() {
return OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
borderSide: BorderSide(width: 1, color: Colors.grey.withOpacity(0.5)));
}
}
result :

How to show TextField when button is pressed?

How can i show text fields based on user inputs when the button is pressed?
You can find in the screen shot i'm trying to do.
here is the code:
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final textController = TextEditingController();
void showTextFields(){
TextField();
}
return Scaffold(
appBar: AppBar(
),
body: Center(
child: Column(
children: [
SizedBox(height: 50,),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20.0),
child: TextField(
controller: textController,
decoration: InputDecoration(
labelText: 'Text fields number'
),
),
),
SizedBox(height: 10,),
ElevatedButton(
onPressed: showTextFields,
child: Text('Press to show text field')
),
],
),
),
);
}
}
I hope my help will be of use to you, greetings
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey _parentKey = GlobalKey();
int countItems = 0;
late TextEditingController textController;
#override
void initState() {
super.initState();
textController = TextEditingController();
}
#override
Widget build(BuildContext context) {
void showTextFields() {
setState(() {
countItems = int.parse(textController.text);
});
}
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
const SizedBox(
height: 50,
),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20.0),
child: TextField(
controller: textController,
decoration:
const InputDecoration(labelText: 'Text fields number'),
),
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: showTextFields,
child: const Text('Press to show text field')),
const SizedBox(
height: 10,
),
(countItems > 0)
? Flexible(
child: ListView.builder(
itemCount: countItems,
itemBuilder: (BuildContext context, int index) {
return TextField(
style: TextStyle(color: Colors.white),
cursorColor: Colors.white,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: "TextField " + index.toString(),
hintStyle: TextStyle(color: Colors.grey[400]),
),
);
}),
)
: const SizedBox()
],
),
),
);
}
}
You can this code, Firstly you must to change stateless to StatefullWidget
and then examine to blow code
class ShowTextFieldValue extends StatefulWidget {
const ShowTextFieldValue({ Key? key }) : super(key: key);
#override
_ShowTextFieldValueState createState() => _ShowTextFieldValueState();
}
class _ShowTextFieldValueState extends State<ShowTextFieldValue> {
#override
Widget build(BuildContext context) {
final textController = TextEditingController();
String value = "";
return Scaffold(
appBar: AppBar(
),
body: Center(
child: Column(
children: [
SizedBox(height: 50,),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20.0),
child: TextField(
controller: textController,
decoration: InputDecoration(
labelText: 'Text fields number'
),
),
),
SizedBox(height: 200,width: 200,
child: Center( child: Text(value)),
),
ElevatedButton(
onPressed: (){
setState(() {
value= textController.text;
});
},
child: Text('Press to show text field')
),
],
),
),
);
}
}