dart 从服务器获取结果后,不会在自动完成中调用optionsViewBuilder

jgovgodb  于 2023-04-27  发布在  其他
关注(0)|答案(2)|浏览(108)

我尝试做一个自动建议输入,从后端API获取结果。下面是我的代码:

import 'dart:async';

import 'package:flutter/material.dart';

import 'package:hello_world/api.dart';
import 'package:hello_world/autocomplete_item.dart';

class Debouncer {
  final int milliseconds;

  VoidCallback? action;
  Timer? _timer;

  Debouncer({this.milliseconds = 250});

  run(VoidCallback action) {
    _timer?.cancel();
    _timer = Timer(Duration(milliseconds: milliseconds), action);
  }
}

class AutoCompleteInput extends StatefulWidget {
  const AutoCompleteInput({
    Key? key,
    this.label = 'Suggest',
    this.textInputAction,
    this.validator,
    this.errorMessage,
  }) : super(key: key);

  final String label;

  final TextInputAction? textInputAction;

  final FormFieldValidator<String>? validator;

  final String? errorMessage;

  @override
  _AutoCompleteInputState createState() => _AutoCompleteInputState();
}

class _AutoCompleteInputState extends State<AutoCompleteInput> {
  final _debouncer = Debouncer(milliseconds: 500);

  List<AutoCompleteItem> _options = [];

  bool _isLoading = false;

  @override
  Widget build(BuildContext context) {
    return Autocomplete<AutoCompleteItem>(
      displayStringForOption: (AutoCompleteItem item) => item.value,
      optionsBuilder: _optionsBuilder,
      fieldViewBuilder: (
        BuildContext context,
        TextEditingController fieldTextEditingController,
        FocusNode fieldFocusNode,
        VoidCallback onFieldSubmitted,
      ) {
        return TextFormField(
          controller: fieldTextEditingController,
          focusNode: fieldFocusNode,
          autocorrect: false,
          maxLength: 50,
          maxLines: 1,
          keyboardType: TextInputType.text,
          textInputAction: widget.textInputAction,
          decoration: InputDecoration(
            labelText: widget.label,
            contentPadding: EdgeInsets.zero,
            errorText: widget.errorMessage,
            counterText: '',
            suffix: _isLoading
                ? Padding(
                    padding: const EdgeInsets.only(right: 1),
                    child: SizedBox(
                      width: 20,
                      height: 20,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    ),
                  )
                : null,
          ),
          validator: widget.validator,
          onFieldSubmitted: (String value) {
            onFieldSubmitted();
          },
          onChanged: (text) {
            _debouncer.run(() async {
              setState(() => _isLoading = true);

              await _fetchResults(text);

              setState(() => _isLoading = false);
            });
          },
        );
      },
      optionsViewBuilder: (
        BuildContext context,
        AutocompleteOnSelected<AutoCompleteItem> onSelected,
        Iterable<AutoCompleteItem> options,
      ) {
        return Align(
          alignment: Alignment.topLeft,
          child: Material(
            elevation: 2,
            child: Container(
              width: 300,
              constraints: BoxConstraints(maxHeight: 250),
              child: ListView.builder(
                padding: EdgeInsets.all(10.0),
                itemCount: options.length,
                shrinkWrap: true,
                itemBuilder: (BuildContext context, int index) {
                  final AutoCompleteItem option = options.elementAt(index);

                  return GestureDetector(
                    onTap: () {
                      onSelected(option);
                    },
                    child: ListTile(
                      dense: true,
                      title: Text(option.value),
                    ),
                  );
                },
              ),
            ),
          ),
        );
      },
      onSelected: (AutoCompleteItem selection) {
        print('${selection.type} => ${selection.value}');
      },
    );
  }

  Future<void> _fetchResults(String text) async {
    // WE PERFORM THE HTTP REQUEST HERE AND THEN ASSIGN THE RESPONSE TO `_options`

    setState(() async {
      _options = await Api.fetchSuggestions(text);
    });
  }

  Iterable<AutoCompleteItem> _optionsBuilder(
      TextEditingValue textEditingValue) {
    if (textEditingValue.text == '') {
      return const Iterable<AutoCompleteItem>.empty();
    }
    return _options;
  }
}

如您所见,我调用_fetchResults方法(从API获取数据)。但是当获取数据并更新状态时,没有建议的覆盖!我检查了_options的长度,它有所有的结果,但setState仍然没有强制Autocomplete小部件重建它的覆盖。虽然当我删除TextFormField的最后一个字符时,它会立即显示叠加。我如何解决这个问题?

isr3a4wc

isr3a4wc1#

在TextField的onChanged方法中调用textEditingController.notifyListeners()并忽略警告
范例

TextField(
    onChanged: (text)async{
            timer?.cancel();
            timer = Timer(const Duration(milliseconds: 700), () async {
            await getData(textEditingController).whenComplete((){
              setState(() {
                // ignore: invalid_use_of_protected_member 
                //invalid_use_of_visible_for_testing_member
                textEditingController.notifyListeners();
              });
            });
          });
          },
         )
ozxc1zmp

ozxc1zmp2#

实际上,TextFieldonChanged中的setState不会触发optionsBuilder回调。相反,你应该直接使用optionsBuilder来获取await并返回更新后的List。忘记使用有状态列表作为optionsBuilder的源吧,现在还不行。
您需要:
1.将TextFieldonChanged函数移到_optionsBuilder方法中。
1.将_optionsBuilder方法设置为async
1.直接return_optionsBuilder中获取List,而不是依赖于有状态的_options字段。

相关问题