我尝试做一个自动建议输入,从后端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
的最后一个字符时,它会立即显示叠加。我如何解决这个问题?
2条答案
按热度按时间isr3a4wc1#
在TextField的onChanged方法中调用textEditingController.notifyListeners()并忽略警告
范例
ozxc1zmp2#
实际上,
TextField
的onChanged
中的setState
不会触发optionsBuilder
回调。相反,你应该直接使用optionsBuilder
来获取await并返回更新后的List
。忘记使用有状态列表作为optionsBuilder
的源吧,现在还不行。您需要:
1.将
TextField
的onChanged
函数移到_optionsBuilder
方法中。1.将
_optionsBuilder
方法设置为async
。1.直接
return
在_optionsBuilder
中获取List
,而不是依赖于有状态的_options
字段。