jansemrau
jansemrau

Reputation: 251

Flutter pass TextEditingController as argument to class

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

Answers (4)

Dr Younss AIT MOU
Dr Younss AIT MOU

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 fieldViewBuilderis already creating a controller for you to use. Here is a implementation attempt:

1. The parent:

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;
          },
        ),
      ],
    );
  }
}

2. The Child

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

Prabhakaran
Prabhakaran

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

Jay Limbani
Jay Limbani

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

CHP
CHP

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

Related Questions