7

如何使自动完成框与TextField没有特定宽度的自动完成框大小相同,它占用了最大宽度。

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

在此处输入图像描述

4

7 回答 7

14

我在尝试基于 RawAutocomplete 制作自己的自动完成小部件时遇到了同样的问题,我使用布局构建器在我的 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,
  ),
)

结果: 我的组件的预期结果

于 2021-04-20T16:35:26.460 回答
1

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,
);
于 2021-12-29T15:14:05.967 回答
1

我也经常遇到这个问题,并且选项构建器非常具体地说明了它希望将大小或填充放置在层次结构中的什么位置。这是我的工作示例:

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

于 2021-04-06T20:52:15.263 回答
0

我有同样的问题并通过使用ValueListenableBuilder.

小部件的宽度Material取决于返回的小部件的宽度field ViewBuilder(默认为 a TextFormField),因此一种解决方案是TextFormField在一帧完成后获取宽度并通知您的自定义optionsViewBuilder

示例代码:

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: 'alice@example.com'),
    User(name: 'Bob', email: 'bob@example.com'),
    User(name: 'Charlie', email: 'charlie123@gmail.com'),
  ];

  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);
}
于 2021-06-15T09:32:19.467 回答
0

我看过源代码autocomplete.dart,缺少参数宽度,但高度存在,我手动添加了宽度,它起作用了。

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,
于 2022-01-27T12:53:49.993 回答
0

这是 Autocomplete 小部件的一个已知错误,正在此处跟踪该错误: https ://github.com/flutter/flutter/issues/78746

所以目前唯一的解决方案是创建自己的自定义小部件。我编写了自己的小部件并作为一个包发布以供重用,并且还在维护中。

你可以在这里找到它:https ://pub.dev/packages/searchfield

于 2021-09-03T11:18:41.270 回答
0

您是否尝试将整个自动完成小部件放在一个大小合适的盒子中?如果尺寸小部件不起作用,这可能是一个错误。

于 2021-04-03T05:30:44.390 回答