Am working on an app that has a form that uses ListViewBuilder for the list of the items and returns a widget that shows a TextField for each item in the list. Is there a way to access the TextField Controller of any item from the form widget itself to get the new value?
Widget used in the ListView builder
class BrandNamesWidget extends StatelessWidget {
BrandNamesWidget({Key? key, required this.brandName, required this.index})
: super(key: key);
final String brandName;
final int index;
final TextEditingController _brandNameController =
new TextEditingController();
@override
Widget build(BuildContext context) {
_brandNameController.text = brandName;
final deviceDimensions = Provider.of<Dimension>(context);
double height = deviceDimensions.getDeviceHeight();
double width = deviceDimensions.getDeviceWidth();
return TextField(
controller: _brandNameController,
cursorColor: Colors.black,
style: TextStyle(
color: AppInfo.MAIN_COLOR,
letterSpacing: 1.5,
),
keyboardType: TextInputType.text,
decoration: InputDecoration(
constraints: BoxConstraints(maxWidth: width * 0.45),
labelText: 'Brand name: ' index.toString(),
prefixIcon: Icon(Icons.medication),
labelStyle: TextStyle(
fontSize: height * 0.03,
color: AppInfo.MAIN_COLOR,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
),
),
);
}
}
Form Widget
.
.
Container(
child: ListView.builder(
itemCount: _brandnames.length,
itemBuilder: (BuildContext ctx, int index) {
return BrandNamesWidget(
brandName: _brandnames[index].toString(),
index: index);
}),
),
// Want to access the _brandNameController.text here in a function to update firestore
.
.
I have checked online for solutions and the only similar question I found was from this stackoverflow question (Is there a way to access variables in a state in Flutter/Dart?)
CodePudding user response:
One way to do it is instantiating a TextEditingController from the outside in the itemBuilder and inject it inside the BrandNamesWidget, hold their references in a collection, that way you can grab their content from the outside like this Gist I created for you. This is what your new class would look like:
class BrandNamesWidget extends StatelessWidget {
final TextEditingController? controller;
final String? brandName;
final int? index;
BrandNamesWidget({ this.brandName, required this.index, this.controller });
@override
Widget build(BuildContext context) {
double height = 100;
double width = MediaQuery.of(context).size.width;
return TextField(
controller: controller!,
keyboardType: TextInputType.text,
decoration: InputDecoration(
constraints: BoxConstraints(maxWidth: width * 0.45),
labelText: 'Brand name: ' index.toString(),
prefixIcon: const Icon(Icons.medication),
labelStyle: TextStyle(
fontSize: height * 0.03,
color: Colors.grey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
),
),
);
}
}
And this is the parent widget holding on to the TextEditingController references, housing your ListView.builder:
class MyWidget extends StatelessWidget {
List<String> _brandnames = ['Toyota', 'Honda', 'BMW', 'Mercedes'];
List<TextEditingController> controllers = [];
@override
Widget build(BuildContext context) {
controllers = [];
return Column(
children: [
Expanded(
child: Container(
child: ListView.builder(
itemCount: _brandnames.length,
itemBuilder: (BuildContext ctx, int index) {
TextEditingController ctrl = TextEditingController();
controllers.add(ctrl);
return BrandNamesWidget(
brandName: _brandnames[index].toString(),
controller: ctrl,
index: index);
}),
)
),
TextButton(
onPressed: () {
for(var ctrl in controllers) {
print(ctrl.text);
}
},
child: Text('Click me to get their values')
)
]
);
}
}
If you input stuff on the text fields shown, and you click on the button I provided, you'll get their content from a function, thus being able to update Firestore or anything you want. Check it out and let me know.