Dart 如何在颤振';s搜索页面小部件?
我需要Google Places使用默认的Flatter的搜索页面提供搜索建议,每当用户开始键入时,我需要提供自动完成建议,我使用Dart 如何在颤振';s搜索页面小部件?,dart,flutter,Dart,Flutter,我需要Google Places使用默认的Flatter的搜索页面提供搜索建议,每当用户开始键入时,我需要提供自动完成建议,我使用FutureBuilder异步实现这一点,现在的问题是,我需要在500毫秒或更长时间内取消发送搜索请求,而不是在用户仍在键入时浪费大量请求 总结一下我到目前为止所做的工作: 1) 在我的小部件中,我调用 showSearch(context: context, delegate: _delegate); 2) 我的代表如下所示: class _LocationSea
FutureBuilder
异步实现这一点,现在的问题是,我需要在500毫秒或更长时间内取消发送搜索请求,而不是在用户仍在键入时浪费大量请求
总结一下我到目前为止所做的工作:
1) 在我的小部件中,我调用
showSearch(context: context, delegate: _delegate);
2) 我的代表如下所示:
class _LocationSearchDelegate extends SearchDelegate<Suggestion> {
@override
List<Widget> buildActions(BuildContext context) {
return <Widget>[
IconButton(
tooltip: 'Clear',
icon: const Icon(Icons.clear),
onPressed: () {
query = '';
showSuggestions(context);
},
)
];
}
@override
Widget buildLeading(BuildContext context) => IconButton(
tooltip: 'Back',
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
close(context, null);
},
);
@override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<Suggestion>>(
future: GooglePlaces.getInstance().getAutocompleteSuggestions(query),
builder: (BuildContext context, AsyncSnapshot<List<Suggestion>> suggestions) {
if (!suggestions.hasData) {
return Text('No results');
}
return buildLocationSuggestions(suggestions.data);
},
);
}
@override
Widget buildSuggestions(BuildContext context) {
return buildResults(context);
}
Widget buildLocationSuggestions(List<Suggestion> suggestions) {
return ListView.builder(
itemBuilder: (context, index) => ListTile(
leading: Icon(Icons.location_on),
title: Text(suggestions[index].text),
onTap: () {
showResults(context);
},
),
itemCount: suggestions.length,
);
}
}
类_LocationSearchDelegate扩展了SearchDelegate{
@凌驾
列出buildActions(BuildContext上下文){
返回[
图标按钮(
工具提示:“清除”,
图标:常量图标(图标清除),
已按下:(){
查询=“”;
展示建议(背景);
},
)
];
}
@凌驾
小部件buildLeading(BuildContext上下文)=>IconButton(
工具提示:“返回”,
图标:动画指令(
图标:animateDictions.menu\u箭头,
进展:过渡化,
),
已按下:(){
关闭(上下文,空);
},
);
@凌驾
小部件构建结果(构建上下文){
回归未来建设者(
未来:GooglePlaces.getInstance().getAutocompleteSuggestions(查询),
生成器:(BuildContext上下文,异步快照建议){
如果(!suggestions.hasData){
返回文本(“无结果”);
}
返回buildLocationSuggestions(suggestions.data);
},
);
}
@凌驾
小部件构建建议(构建上下文){
返回构建结果(上下文);
}
Widget buildLocationSuggestions(列表建议){
返回ListView.builder(
itemBuilder:(上下文,索引)=>ListTile(
前导:图标(图标位置打开),
标题:文本(建议[索引]),
onTap:(){
展示结果(背景);
},
),
itemCount:suggestions.length,
);
}
}
3-我需要节流/去盎司搜索,直到xxx毫秒过去
我有一个想法,是否有一个简单的方法将FutureBuilder转换为Stream并使用Stream builder(我在一些文章中读到它支持去Bouncing)
**我知道有一些第三方自动完成小部件,如
TypeAhead
,正在这样做(从头开始),但我现在不想使用它。更新:我为此制作了一个包,可用于回调、期货和/或流。使用它将简化下面描述的两种方法,尤其是基于流的方法,因为不需要引入新类。下面是一个dartpad示例
至少有两种方法可以做到这一点,一种是基于未来
的方法,另一种是基于流
的方法。由于内置了去抖动功能,类似的问题也被指向使用流,但让我们看看这两种方法
基于未来的方法
Future
s本身是不可取消的,但它们使用的底层Timer
s是可取消的。下面是一个简单的类,它实现了基本的去盎司功能,使用回调而不是未来
class Debouncer<T> {
Debouncer(this.duration, this.onValue);
final Duration duration;
void Function(T value) onValue;
T _value;
Timer _timer;
T get value => _value;
set value(T val) {
_value = val;
_timer?.cancel();
_timer = Timer(duration, () => onValue(_value));
}
}
基于流的方法
由于所讨论的数据来自未来而不是流,因此我们必须设置一个类来处理查询输入和建议输出。幸运的是,它可以自然地处理输入流的去抖动
class SuggestionsController {
SuggestionsController(this.duration) {
_queryController.stream
.transform(DebounceStreamTransformer(duration))
.listen((query) async {
_suggestions.add(
await GooglePlaces.getInstance().getAutocompleteSuggestions(query));
});
}
final Duration duration;
final _queryController = StreamController<String>();
final _suggestions = BehaviorSubject<List<Suggestion>>();
Sink<String> get query => _queryController.sink;
Stream<List<Suggestion>> get suggestions => _suggestions.stream;
void dispose() {
_queryController.close();
_suggestions.close();
}
}
从您的示例中不清楚query
字符串来自何处,但要完成连接,您需要调用controller.query.add(newQuery)
,然后StreamBuilder处理其余部分
结论
由于您使用的API会产生期货收益,因此使用这种方法似乎更简单一些。缺点是Debouncer类的开销,并添加了一个补足符将其绑定到FutureBuilder
流方法很流行,但也包括相当数量的开销。如果您不熟悉,正确创建和处理流可能会很棘手 这里有一个简单的替代答案
import 'package:debounce_throttle/debounce_throttle.dart';
final debouncer = Debouncer<String>(Duration(milliseconds: 250));
Future<List<Suggestion>> queryChanged(String query) async {
debouncer.value = query;
return GooglePlaces.getInstance().getAutocompleteSuggestions(await debouncer.nextValue)
}
@override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<Suggestion>>(
future: queryChanged(query),
builder: (BuildContext context, AsyncSnapshot<List<Suggestion>> suggestions) {
if (!suggestions.hasData) {
return Text('No results');
}
return buildLocationSuggestions(suggestions.data);
},
);
}
import'包:去盎司节流/去盎司节流.dart';
最终去抖动器=去抖动器(持续时间(毫秒:250));
未来查询更改(字符串查询)异步{
debouncer.value=查询;
返回GooglePlaces.getInstance().getAutocompleteSuggestions(等待debouncer.nextValue)
}
@凌驾
小部件构建结果(构建上下文){
回归未来建设者(
未来:查询更改(查询),
生成器:(BuildContext上下文,异步快照建议){
如果(!suggestions.hasData){
返回文本(“无结果”);
}
返回buildLocationSuggestions(suggestions.data);
},
);
}
我想这大概就是你应该做的
这里有几个使用流的想法,使用去Bouncer
void queryChanged(query) => debouncer.value = query;
Stream<List<Suggestion>> get suggestions async* {
while (true)
yield GooglePlaces.getInstance().getAutocompleteSuggestions(await debouncer.nexValue);
}
@override
Widget buildResults(BuildContext context) {
return StreamBuilder<List<Suggestion>>(
stream: suggestions,
builder: (BuildContext context, AsyncSnapshot<List<Suggestion>> suggestions) {
if (!suggestions.hasData) {
return Text('No results');
}
return buildLocationSuggestions(suggestions.data);
},
);
}
void queryChanged(query)=>debouncer.value=query;
流获取异步建议*{
while(true)
生成GooglePlaces.getInstance().getAutocompleteSuggestions(wait debouncer.nexValue);
}
@凌驾
小部件构建结果(构建上下文){
返回流生成器(
流:建议,
生成器:(BuildContext上下文,异步快照建议){
如果(!suggestions.hasData){
返回文本(“无结果”);
}
返回buildLocationSuggestions(suggestions.data);
},
);
}
或者使用流变压器
Stream<List<Suggestion>> get suggestions =>
debouncer.values.transform(StreamTransformer.fromHandlers(
handleData: (value, sink) => sink.add(GooglePlaces.getInstance()
.getAutocompleteSuggestions(value))));
Stream-get-suggestions=>
debouncer.values.transform(StreamTransformer.fromHandlers(
handleData:(value,sink)=>sink.add(GooglePlaces.getInstance()
.getAutocompleteSuggestions(值));
我就是这样做的,不需要库:
void searchWithThrottle(String keyword, {int throttleTime}) {
_timer?.cancel();
if (keyword != previousKeyword && keyword.isNotEmpty) {
previousKeyword = keyword;
_timer = Timer.periodic(Duration(milliseconds: throttleTime ?? 350), (timer) {
print("Going to search with keyword : $keyword");
search(keyword);
_timer.cancel();
});
}
}
定时器可用于消除搜索输入的抖动
Timer debounce;
void handleSearch(String value) {
if (debounce != null) debounce.cancel();
setState(() {
debounce = Timer(Duration(seconds: 2), () {
searchItems(value);
//call api or other search functions here
});
});
}
每当向文本b添加新输入时
Stream<List<Suggestion>> get suggestions =>
debouncer.values.transform(StreamTransformer.fromHandlers(
handleData: (value, sink) => sink.add(GooglePlaces.getInstance()
.getAutocompleteSuggestions(value))));
void searchWithThrottle(String keyword, {int throttleTime}) {
_timer?.cancel();
if (keyword != previousKeyword && keyword.isNotEmpty) {
previousKeyword = keyword;
_timer = Timer.periodic(Duration(milliseconds: throttleTime ?? 350), (timer) {
print("Going to search with keyword : $keyword");
search(keyword);
_timer.cancel();
});
}
}
Timer debounce;
void handleSearch(String value) {
if (debounce != null) debounce.cancel();
setState(() {
debounce = Timer(Duration(seconds: 2), () {
searchItems(value);
//call api or other search functions here
});
});
}