Flutter 2.0 -自动完成小部件从右侧退出屏幕

brtdzjyr  于 2022-11-25  发布在  Flutter
关注(0)|答案(8)|浏览(124)

如何使自动完成框与TextField框大小相同,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);
                },
              ),

y4ekin9u

y4ekin9u1#

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

结果:Expected result on my component

lqfhib0f

lqfhib0f2#

我也经常遇到这个问题,选项构建器对于大小或填充在层次结构中的位置非常具体。下面是我的工作示例:

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

de90aj5v3#

正如这里所指出的,在写这篇文章的时候,最好的选择似乎是修改默认_AutoCompleteOptions代码(可以在这里找到)中包含的BoxConstraints宽度和高度,并将其作为AutocompleteoptionsViewBuilder参数传递。
粘贴我的代码,仅供参考:

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

h4cxqtbf4#

我有同样的问题,并通过使用ValueListenableBuilder解决了它。
Material小部件的宽度取决于field ViewBuilder返回的小部件的宽度(默认为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);
}
jei2mxaa

jei2mxaa5#

我看过源代码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,
kse8i1jr

kse8i1jr6#

您是否尝试将整个Autocomplete小部件放入一个大小调整框中?如果大小调整小部件不工作,这可能是一个错误。

kgsdhlau

kgsdhlau7#

这是自动完成小工具的已知错误,在此处跟踪该错误:https://github.com/flutter/flutter/issues/78746
所以目前唯一的解决办法就是创建自己的自定义小部件。我写了自己的小部件,并作为包发布以供重用,也在维护中。
您可以在此处找到它:https://pub.dev/packages/searchfield

jfgube3f

jfgube3f8#

3.3.5中仍发生扑动...
我的解决方法是将Autocomplete Package 在LayoutBuilder中,并使用constraints.maxWidth设置optionsViewBuilder的ListView的宽度。
详细片段如下:https://github.com/flutter/flutter/issues/78746#issuecomment-1326488333

相关问题