0

The code below creates an AutoComplete field and a MaterialButton that should work together, but they're not.

The AutoComplete needs to be able to:

  1. Take direct user input from typing or list selection
  2. Be updated by a click of the MaterialButton

Problem is that I can’t get both of these things to work in the same widget.

When I set TextFormField(controller: field2TextEditingController, ...) the AutoComplete displays the drop-down categoryList properly, but then the MaterialButton can not update the value.

And when I set TextFormField(controller: _category2Controller, ...) the MaterialButton can update the value, but the Auto Complete no longer displays the drop-down categoriesList.

Do you have any idea how I can get both of these things to work simultaneously?

Here's my code:

    dependencies:
      flutter_textfield_autocomplete: ^1.0.2
    
    
      String category2 = "";
    
      TextEditingController _category2Controller = TextEditingController();
    
      List<String> categoriesList = [];
      categoriesList = ["Breakfast", "Dessert", "Dinner", "Groceries", "Gas", "Insurance", "Lunch", "Vacation", "Vehicle Repair", "Wine"];
    
    
    Autocomplete<String>(
                            optionsBuilder: (TextEditingValue textEditingValue) {
                              return categoriesList
                                  .where((theString) => theString
                                  .toLowerCase()
                                  .startsWith(
                                  textEditingValue.text.toLowerCase()))
                                  .toList();
                            },
                            fieldViewBuilder: (BuildContext context,
                                TextEditingController field2TextEditingController,
                                FocusNode fieldFocusNode,
                                VoidCallback onFieldSubmitted) {
                              return TextFormField(
                                controller: _category2Controller,           // DOES NOT DISPLAY DROP DOWN LIST
                                // controller: field2TextEditingController,    // CANNOT ACCEPT VALUE FROM BUTTON
                                focusNode: fieldFocusNode,
                                decoration:
                                  const InputDecoration(labelText: 'Category 2'),
                                onChanged: (value) {
                                  category2 = value.toString();
                                  _category2Controller..text = value.toString()
                                    ..selection = TextSelection.collapsed(offset: _category2Controller.text.length);
                                },
                              );
                            },
                            displayStringForOption: (value) => value,
                            onSelected: (value) {
                              _category2Controller.text = value.toString();
                              category2 = value.toString();
                            },
                          ),
    
    
    MaterialButton(
                                padding: const EdgeInsets.all(10),
                                color: Colors.blueAccent,
                                shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(5)),
                                child: const Text(
                                  "Zachary's Pizza - Dinner",
                                  style: TextStyle(color: Colors.white, fontSize: 15),
                                ),
                                onPressed: () {
                                                               _category2Controller.text = "Dinner";
                                  category2 = "Dinner";
                                },
                              ),

Any comments and/or suggestions are greatly appreciated!

SqueezeOJ
  • 441
  • 7
  • 17

2 Answers2

1

Try something like this:

TextEditingController? _categoryController;

MaterialButton(
  child: const Text('Groceries'),
  onPressed: () {
    _categoryController?.text = "Groceries";
    // Navigator.pop(context);
  },
),
Autocomplete<String>(
  optionsBuilder: (TextEditingValue textEditingValue) {
    return categoriesList
        .where((theString) => theString
            .toLowerCase()
            .startsWith(textEditingValue.text.toLowerCase()))
        .toList();
  },
  fieldViewBuilder: (
    BuildContext context,
    TextEditingController fieldTextEditingController,
    FocusNode fieldFocusNode,
    VoidCallback onFieldSubmitted,
  ) {
    _categoryController ??= fieldTextEditingController; // initialize _categoryController if null
    return TextFormField(
      controller: _categoryController, // use _categoryController everywhere to update the TextFormField
      focusNode: fieldFocusNode,
      decoration: const InputDecoration(labelText: 'Category'),
    );
  },
  displayStringForOption: (value) => value,
)
Ante Bule
  • 1,834
  • 1
  • 2
  • 8
  • This does update the AutoComplete, but now my AutoComplete doesn't work right. It doesn't display the list of options when cursor is in that field. I'm going to try to troubleshoot that. – SqueezeOJ Dec 19 '22 at 16:51
0

Based on this Stackoverflow post I decided to use RawAutocomplete instead. This widget allows me to pass my own TextEditingController, which made it easy to assign values to the RawAutocomplete from both inside & outside of that widget itself.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Raw AutoComplete Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _textEditingController = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  final GlobalKey _autocompleteKey = GlobalKey();

  final List<String> _animals = <String>['aardvark', 'ape', 'baboon', 'bobcat', 'chameleon', 'cat', 'dog', 'elephant', 'fox', 'zebra'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Raw AutoComplete Demo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[

            MaterialButton(   // Button assigns value to RawAutocomplete
              child: const Text('Jaguar'),
              onPressed: () {
                _textEditingController.text = "Jaguar";
              },
            ),

            MaterialButton(   // Button clears any value in RawAutocomplete
              child: const Text('Clear'),
              onPressed: () {
                _textEditingController.text = "";
              },
            ),

            RawAutocomplete<String>(
              key: _autocompleteKey,
              focusNode: _focusNode,
              textEditingController: _textEditingController,
              optionsBuilder: (TextEditingValue textEditingValue) {
                return _animals.where((String option) {
                  return option.contains(textEditingValue.text.toLowerCase());
                }).toList();
              },
              optionsViewBuilder: (BuildContext context,
                  AutocompleteOnSelected<String> onSelected,
                  Iterable<String> options) {
                return Material(
                  elevation: 4.0,
                  child: ListView(
                    children: options
                        .map((String option) => GestureDetector(
                              onTap: () {
                                onSelected(option);
                              },
                              child: ListTile(
                                title: Text(option),
                              ),
                            ))
                        .toList(),
                  ),
                );
              },
              fieldViewBuilder: (
                BuildContext context,
                TextEditingController fieldTextEditingController,
                FocusNode fieldFocusNode,
                VoidCallback onFieldSubmitted,
              ) {
                return TextFormField(
                  controller: fieldTextEditingController,
                  focusNode: fieldFocusNode,
                  decoration: const InputDecoration(labelText: 'Choose Animal'),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

SqueezeOJ
  • 441
  • 7
  • 17