Reputation: 251
I try to pass a TextEditingController from a parent Class to a child Class. I don't know, how to do so.
This is my class, where I want to use the TextEditingController which I want to give as an argument.
class AutocompleteField extends StatelessWidget {
const AutocompleteField({Key? key}) : super(key: key);
static const List<String> _kOptions = <String>[
'Superdry',
'A&F',
'H&M',
];
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return TextFormField(
controller: textEditingController,
focusNode: focusNode,
onFieldSubmitted: (str) => onFieldSubmitted(),
decoration: const InputDecoration(
border: UnderlineInputBorder(),
contentPadding: EdgeInsets.only(left: 12.0),
));
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
);
}
}
I call it in the parent class like this.
Padding(
padding: const EdgeInsets.only(left: 12.0, top: 12.0),
child: Text(
"Marke",
style: TextStyle(
color: Colors.grey[800],
),
)),
AutocompleteField(labelTextController),
The labelTextController is a TextEditingController which I defined in the parent class and want to call in the class of the AutoComplete widget, to update the state. Do you have any suggestions?
Thank you very much!
Upvotes: 0
Views: 1245
Reputation: 1117
I think to the real challange here is how to use the controller, rather than simply passing it between the parent and the child. That is a very interesting question. You basically need to lift the controller up from the child to it's parent rather than the other way arround. That is, the fieldViewBuilder
is already creating a controller for you to use. Here is a implementation attempt:
The parent has to be a state-aware widget. For the sake of simplicity, we will go with a simple StatefullWidget
here. But you can use other state management strategies for a more centralized solution. Refer to the comments in the code for a bit more details.
class ParentWidget extends StatefulWidget {
const ParentWidget({Key? key}) : super(key: key);
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
// This is the controller that will help us update the child's text filed
late TextEditingController _controller;
@override
Widget build(BuildContext context) {
return ListView(
children: [
// This button is here just for testing that we are, indeed,
// updating the textFiled with whatever value we pass to the
// controller
ElevatedButton(
onPressed: () {
_controller.text = 'Hello World';
},
child: const Text("Updated Text")),
// Here we call the child's builder function to get a reference
// to the textEditingController. Check section 2 (the Child) for
// more details
AutocompleteField(
builder: (TextEditingController ctrlr) {
_controller = ctrlr;
},
),
],
);
}
}
The child is already defining a TextEditingController
we just need to lift up it's reference to the parent. We do that by creating a builder
function. Again, check the comments for more details.
You can call this function whatever you want. I just picked
builder
by reflex :s
class AutocompleteField extends StatelessWidget {
// Here we define the builder that will lift the TextEditingController
// up to the parent
final Function(TextEditingController)? builder;
const AutocompleteField({
this.builder,
Key? key,
}) : super(key: key);
static const List<String> _kOptions = <String>[
'Superdry',
'A&F',
'H&M',
'Hello',
'World',
];
@override
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
fieldViewBuilder: (
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted,
) {
// If the builder is defined, we use it here to get a reference
// to the textEditingController defined in the fieldViewBuilder
if (builder != null) {
builder!(textEditingController);
}
return TextFormField(
controller: textEditingController,
focusNode: focusNode,
onFieldSubmitted: (str) => onFieldSubmitted(),
decoration: const InputDecoration(
border: UnderlineInputBorder(),
contentPadding: EdgeInsets.only(left: 12.0),
),
);
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
);
}
}
Cheers
Upvotes: 1
Reputation: 1544
Your AutocompleteField should look something like this
class AutoCompleteField extends StatefulWidget {
const AutoCompleteField({Key? key, required this.textEditingController}) : super(key: key);
final TextEditingController textEditingController; // declare a variable as parameter for the class
@override
State<AutoCompleteField> createState() => _AutoCompleteFieldState();
}
class _AutoCompleteFieldState extends State<AutoCompleteField> {
@override
Widget build(BuildContext context) {
// access like this
print(widget.textEditingController);
return Container();
}
}
Incase of stateless widget
class AutocompleteFiled extends StatelessWidget {
const AutocompleteFiled({Key? key, required this.textEditingController}) : super(key: key);
final TextEditingController textEditingController;
@override
Widget build(BuildContext context) {
print(textEditingController);
return Container();
}
}
And in Parent Class call like this
Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 12.0, top: 12.0),
child: Text(
"Marke",
style: TextStyle(
color: Colors.grey[800],
),
)),
AutocompleteField(textEditingController: labelTextController,)
],
)
Upvotes: 0
Reputation: 189
you have to pass your controller from parent to child class follow this step:-
first of all add one parameter like this into child class
class `AutocompleteField` extends StatelessWidget {
final TextEditingController labelTextController;
const AutocompleteField({required this.labelTextController, Key? key})
: super(key: key);
static const List<String> _kOptions = <String>[
'Superdry',
'A&F',
'H&M',
];
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return TextFormField(
controller: textEditingController,
focusNode: focusNode,
onFieldSubmitted: (str) => onFieldSubmitted(),
decoration: const InputDecoration(
border: UnderlineInputBorder(),
contentPadding: EdgeInsets.only(left: 12.0),
));
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
);
}
}
and then pass controller when you call it.
AutocompleteField(controllername);
Upvotes: 0
Reputation: 202
You have to declare Text editing Controller in AutocompleteField class like below
class AutocompleteField extends StatelessWidget {
final TextEditingController labelTextController;
const AutocompleteField({required this.labelTextController, Key? key})
: super(key: key);
static const List<String> _kOptions = <String>[
'Superdry',
'A&F',
'H&M',
];
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return TextFormField(
controller: textEditingController,
focusNode: focusNode,
onFieldSubmitted: (str) => onFieldSubmitted(),
decoration: const InputDecoration(
border: UnderlineInputBorder(),
contentPadding: EdgeInsets.only(left: 12.0),
));
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
);
}
}
and You can pass your controller as shown below
AutocompleteField(
labelTextController: labelTextController,
);
Upvotes: 0