'Flutter 2.0 - Autocomplete widget goes out of screen from right side

How do I make the autocomplete box be the same size with the TextField which doesn't have a specific width, it takes up the maximum width.

              Autocomplete(
                optionsBuilder: (TextEditingValue textEditingValue) {
                  if (textEditingValue.text == '') {
                    return ['aa', 'bb', 'cc', 'aa', 'bb', 'cc'];
                  }
                  return ['aa', 'bb', 'cc', 'aa', 'bb', 'cc']
                      .where((String option) {
                    return option
                        .toString()
                        .contains(textEditingValue.text.toLowerCase());
                  });
                },
                onSelected: (option) {
                  print(option);
                },
              ),

enter image description here



Solution 1:[1]

I got the same problem while trying to make my own autocomplete widget based on RawAutocomplete and I used a layout builder to get the perfect container width size inside my optionsViewBuilder:

LayoutBuilder(
  builder: (context, constraints) => RawAutocomplete<String>(
    focusNode: focusNode,
    fieldViewBuilder: fieldViewBuilder,
    optionsViewBuilder: (context, onSelected, options) => Align(
      alignment: Alignment.topLeft,
      child: Material(
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(bottom: Radius.circular(4.0)),
        ),
        child: Container(
          height: 52.0 * options.length,
          width: constraints.biggest.width, // <-- Right here !
          child: ListView.builder(
            padding: EdgeInsets.zero,
            itemCount: options.length,
            shrinkWrap: false,
            itemBuilder: (BuildContext context, int index) {
              final String option = options.elementAt(index);
              return InkWell(
                onTap: () => onSelected(option),
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Text(option),
                ),
              );
            },
          ),
        ),
      ),
    ),
    optionsBuilder: (textEditingValue) =>
        suggestions.where((element) => element.contains(textEditingValue.text.trim().toUpperCase())),
    textEditingController: controller,
  ),
)

Result: Expected result on my component

Solution 2:[2]

I ran into this issue a lot too, and the options builder is very specific about where it wants the size or padding to be placed in the hierarchy. Here's my working example:

optionsViewBuilder: (context, onAutoCompleteSelect, options) {
  return Align(
    alignment: Alignment.topLeft,
       child: Material(
         color: Theme.of(context).primaryColorLight,
           elevation: 4.0,
           // size works, when placed here below the Material widget
           child: Container(
              // I have the text field wrapped in a container with
              // EdgeInsets.all(20) so subtract 40 from the width for the width
              // of the text box. You could also just use a padding widget
              // with EdgeInsets.only(right: 20)
              width: MediaQuery.of(context).size.width - 40,
                child: ListView.separated(
                   shrinkWrap: true,
                   padding: const EdgeInsets.all(8.0),
                   itemCount: options.length,
                   separatorBuilder: (context, i) {
                     return Divider();
                   },
                   itemBuilder: (BuildContext context, int index) {
                     // some child here
                   },
                 )
              ),
           )
        );
     }

Solution 3:[3]

As pointed out here, at the time of writing this, the best option seems to be to alter the BoxConstraints width and height included in the default _AutoCompleteOptions code (that can be found here), and pass it as the optionsViewBuilder argument of Autocomplete. This requires that you are able to calculate the required width in advance, though.

Pasting my code, just for reference:

Autocomplete<Animal>(
  optionsBuilder: (textEditingValue) {
    if (textEditingValue.text == '') {
      return const Iterable<Animal>.empty();
    }
    return animalsList.where((Animal option) {
      return "${option.id}".contains(textEditingValue.text.toLowerCase()) ||
          option.nome!.toLowerCase().contains(textEditingValue.text.toLowerCase()) ||
          option.ownerCF!.toLowerCase().contains(textEditingValue.text.toLowerCase());
    });
  },
  optionsViewBuilder: (context, onSelected, options) {
    return Align(
      alignment: Alignment.topLeft,
      child: Material(
        elevation: 4.0,
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxHeight: 200, maxWidth: 600), //RELEVANT CHANGE: added maxWidth
          child: ListView.builder(
            padding: EdgeInsets.zero,
            shrinkWrap: true,
            itemCount: options.length,
            itemBuilder: (BuildContext context, int index) {
              final Animal option = options.elementAt(index);
              return InkWell(
                onTap: () {
                  onSelected(option);
                },
                child: Builder(builder: (BuildContext context) {
                  final bool highlight = AutocompleteHighlightedOption.of(context) == index;
                  if (highlight) {
                    SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) {
                      Scrollable.ensureVisible(context, alignment: 0.5);
                    });
                  }
                  return Container(
                    color: highlight ? Theme.of(context).focusColor : null,
                    padding: const EdgeInsets.all(16.0),
                    child: Text(option.getInfo() + " - " + option.getOwnerInfo()),
                  );
                }),
              );
            },
          ),
        ),
      ),
    );
  },
  displayStringForOption: (option) => option.getInfo() + " - " + option.getOwnerInfo(), 
  onSelected: (option) => selected = option,
);

Solution 4:[4]

I have seen source code autocomplete.dart, there is missing parameter width, but height exists, I have added width by hand, and it worked.

class Autocomplete<T extends Object> extends StatelessWidget {
/// Creates an instance of [Autocomplete].
const Autocomplete({
Key? key,
required this.optionsBuilder,
this.displayStringForOption = RawAutocomplete.defaultStringForOption,
this.fieldViewBuilder = _defaultFieldViewBuilder,
this.onSelected,
this.optionsMaxHeight = 200.0,
this.optionsMaxWidth = 285.0 // I added this line
this.optionsViewBuilder,
this.initialValue,

/// The default value is set to 200.
final double optionsMaxHeight;
final double optionsMaxWidth; // Added this line

 @override
Widget build(BuildContext context) {
return RawAutocomplete<T>(
  displayStringForOption: displayStringForOption,
  fieldViewBuilder: fieldViewBuilder,
  initialValue: initialValue,
  optionsBuilder: optionsBuilder,
  optionsViewBuilder: optionsViewBuilder ?? (BuildContext context, AutocompleteOnSelected<T> onSelected, Iterable<T> options) {
    return _AutocompleteOptions<T>(
      displayStringForOption: displayStringForOption,
      onSelected: onSelected,
      options: options,
      maxOptionsHeight: optionsMaxHeight,
      maxOptionsWidth: optionsMaxWidth, // Added this line
    );
  },
  onSelected: onSelected,
);
}


// The default Material-style Autocomplete options.
 class _AutocompleteOptions<T extends Object> extends StatelessWidget {
  const _AutocompleteOptions({
  Key? key,
  required this.displayStringForOption,
  required this.onSelected,
  required this.options,
required this.maxOptionsHeight,
required this.maxOptionsWidth // Added this line
}) : super(key: key);

final AutocompleteOptionToString<T> displayStringForOption;

final AutocompleteOnSelected<T> onSelected;

final Iterable<T> options;
final double maxOptionsHeight;
final double maxOptionsWidth;

@override
Widget build(BuildContext context) {
return Align(
  alignment: Alignment.topLeft,
  child: Material(
    elevation: 4.0,
    child: ConstrainedBox(
      constraints: BoxConstraints(maxHeight: maxOptionsHeight, maxWidth: maxOptionsWidth), // Added maxWidth which was missing here
      child: ListView.builder(
        padding: EdgeInsets.zero,
        shrinkWrap: true,

Solution 5:[5]

Did you try to put the whole Autocomplete widget in a sized box? If the sizing widgets are not working this probably maybe a bug.

Solution 6:[6]

I have the same issue and resolved it by using a ValueListenableBuilder.

The width of Material widget is depends on width of the widget returned by field ViewBuilder (default is a TextFormField), so one solution is get width of TextFormField after one frame completed and notify your custom optionsViewBuilder

sample code:

import 'package:flutter/material.dart';

class AutocompleteText extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => AutocompleteTextState();
}

class AutocompleteTextState extends State<AutocompleteText> {
  final ValueNotifier<double?> optionsViewWidthNotifier = ValueNotifier(null);

  static const List<User> _userOptions = <User>[
    User(name: 'Alice', email: '[email protected]'),
    User(name: 'Bob', email: '[email protected]'),
    User(name: 'Charlie', email: '[email protected]'),
  ];

  static String _displayStringForOption(User option) => option.name;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Padding(
      padding: EdgeInsets.all(20),
      child: Autocomplete<User>(
        displayStringForOption: _displayStringForOption,
        optionsBuilder: (TextEditingValue textEditingValue) {
          if (textEditingValue.text == '') {
            return const Iterable<User>.empty();
          }
          return _userOptions.where((User option) {
            return option
                .toString()
                .contains(textEditingValue.text.toLowerCase());
          });
        },
        onSelected: (User selection) {
          print('You just selected ${_displayStringForOption(selection)}');
        },
        fieldViewBuilder: (BuildContext context,
            TextEditingController textEditingController,
            FocusNode focusNode,
            VoidCallback onFieldSubmitted) {
          return OrientationBuilder(builder: (context, orientation) {
            WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
              optionsViewWidthNotifier.value =
                  (context.findRenderObject() as RenderBox).size.width;
            });
            return TextFormField(
              controller: textEditingController,
              focusNode: focusNode,
              onFieldSubmitted: (String value) {
                onFieldSubmitted();
              },
            );
          });
        },
        optionsViewBuilder: (
          BuildContext context,
          AutocompleteOnSelected<User> onSelected,
          Iterable<User> options,
        ) {
          return ValueListenableBuilder<double?>(
              valueListenable: optionsViewWidthNotifier,
              builder: (context, width, _child) {
                return Align(
                  alignment: Alignment.topLeft,
                  child: Material(
                    elevation: 4.0,
                    child: SizedBox(
                      width: width,
                      height: 200.0,
                      child: ListView.builder(
                        padding: EdgeInsets.zero,
                        itemCount: options.length,
                        itemBuilder: (BuildContext context, int index) {
                          final User option = options.elementAt(index);
                          return InkWell(
                            onTap: () {
                              onSelected(option);
                            },
                            child: Padding(
                              padding: const EdgeInsets.all(16.0),
                              child: Text(_displayStringForOption(option)),
                            ),
                          );
                        },
                      ),
                    ),
                  ),
                );
              });
        },
      ),
    ));
  }

  @override
  void dispose() {
    optionsViewWidthNotifier.dispose();
    super.dispose();
  }
}

@immutable
class User {
  const User({
    required this.email,
    required this.name,
  });

  final String email;
  final String name;

  @override
  String toString() {
    return '$name, $email';
  }

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is User && other.name == name && other.email == email;
  }

  @override
  int get hashCode => hashValues(email, name);
}

Solution 7:[7]

This is a known bug for Autocomplete widget, the bug is being tracked here: https://github.com/flutter/flutter/issues/78746

So the only solution at the moment is to create your own customized widget. I wrote my own widget and published as a package for reuse and is also being maintained.

you can find it here: https://pub.dev/packages/searchfield

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 codey-boi
Solution 3
Solution 4
Solution 5 Ibrahim Alqayyas
Solution 6 ???
Solution 7 Mahesh Jamdade