防抖是指在短时间内无意义的多次操作,然后我们规定单位时间内只处理一次。
比如搜索框正在输入,就不应该频繁的去查询处理,控制在500毫秒响应一次查询操作。
本文将会说明 AutoComplete 组件的使用以及加入防抖的控制,提示用户交互体验。
完整代码详见 github .
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_autocomplete
参数 | 类型 | 说明 |
---|---|---|
optionsBuilder | 函数 | 返回可选列表数据值 |
displayStringForOption | 函数 | 格式化显示字段 |
fieldViewBuilder | 函数 | 输入字段组件 |
onSelected | 函数 | 选中项时 |
optionsViewBuilder | 函数 | 构建下拉列表项 |
参考
https://api.flutter.dev/flutter/material/Autocomplete-class.html
lib/model.dart
/// 国家数据模型 class Country { const Country({ required this.name, required this.size, }); final String name; final int size; String toString() { return '$name ($size)'; } }
lib/debounce.dart
class DebouncePage extends StatefulWidget { const DebouncePage({super.key}); State<DebouncePage> createState() => _DebouncePageState(); }
class _DebouncePageState extends State<DebouncePage> { List<Country> countryOptions = <Country>[ const Country(name: 'Africa', size: 30370000), const Country(name: 'Asia', size: 44579000), const Country(name: 'Australia', size: 8600000), const Country(name: 'Bulgaria', size: 110879), const Country(name: 'Canada', size: 9984670), const Country(name: 'Denmark', size: 42916), const Country(name: 'Europe', size: 10180000), const Country(name: 'India', size: 3287263), const Country(name: 'North America', size: 24709000), const Country(name: 'South America', size: 17840000), ];
返回可选列表数据值
Widget _mainView() { return Autocomplete<Country>( // 返回可选列表数据值 optionsBuilder: (TextEditingValue textEditingValue) async { debounceValue = textEditingValue.text; return countryOptions .where((Country county) => county.name .toLowerCase() .startsWith(debounceValue.toLowerCase())) .toList(); }, ...
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Autocomplete Getx 防抖")), body: Padding(padding: const EdgeInsets.all(10), child: _mainView()), ); }
格式化显示字段
displayStringForOption: (Country option) => option.name,
输入字段组件
fieldViewBuilder: ( BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted, ) { return TextField( controller: fieldTextEditingController, focusNode: fieldFocusNode, style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.amber, fontSize: 24, ), ); },
选中项时
onSelected: (Country selection) { if (kDebugMode) { print('Selected: ${selection.name}'); } },
构建下拉列表项
optionsViewBuilder: ( BuildContext context, AutocompleteOnSelected<Country> onSelected, Iterable<Country> options, ) { return Align( alignment: Alignment.topLeft, child: Material( child: Container( width: 300, color: Colors.cyan, child: ListView.builder( padding: const EdgeInsets.all(10.0), itemCount: options.length, itemBuilder: (BuildContext context, int index) { final Country option = options.elementAt(index); return GestureDetector( onTap: () { onSelected(option); }, child: ListTile( title: Text(option.name, style: const TextStyle(color: Colors.white)), ), ); }, ), ), ), ); },
// 标记是否正在防抖 bool isDebounce = false; // 防抖查询值 String debounceValue = "";
Widget _mainView() { return Autocomplete<Country>( // 返回可选列表数据值 optionsBuilder: (TextEditingValue textEditingValue) async { debounceValue = textEditingValue.text; // 防抖处理 // 防抖状态下直接返回空 List [] if (isDebounce) { return Future.value([]); } else { isDebounce = true; // 设置防抖标志 // 1秒防抖直接返回 FutureOr return Future.delayed(const Duration(seconds: 1), () async { List<Country> rows = []; // http 拉取数据 try { rows = await Future.delayed( const Duration(milliseconds: 500), // 延迟 500 毫秒,模拟 http 请求 () => countryOptions .where((Country county) => county.name .toLowerCase() .startsWith(debounceValue.toLowerCase())) .toList()); } finally { isDebounce = false; // 关闭防抖标志 } return rows; }); } },
lib/debounce.dart
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'model.dart'; class DebouncePage extends StatefulWidget { const DebouncePage({super.key}); State<DebouncePage> createState() => _DebouncePageState(); } class _DebouncePageState extends State<DebouncePage> { List<Country> countryOptions = <Country>[ const Country(name: 'Africa', size: 30370000), const Country(name: 'Asia', size: 44579000), const Country(name: 'Australia', size: 8600000), const Country(name: 'Bulgaria', size: 110879), const Country(name: 'Canada', size: 9984670), const Country(name: 'Denmark', size: 42916), const Country(name: 'Europe', size: 10180000), const Country(name: 'India', size: 3287263), const Country(name: 'North America', size: 24709000), const Country(name: 'South America', size: 17840000), ]; // 标记是否正在防抖 bool isDebounce = false; // 防抖查询值 String debounceValue = ""; Widget _mainView() { return Autocomplete<Country>( // 返回可选列表数据值 optionsBuilder: (TextEditingValue textEditingValue) async { debounceValue = textEditingValue.text; // 防抖处理 // 防抖状态下直接返回空 List [] if (isDebounce) { return Future.value([]); } else { isDebounce = true; // 设置防抖标志 // 1秒防抖直接返回 FutureOr return Future.delayed(const Duration(seconds: 1), () async { List<Country> rows = []; // http 拉取数据 try { rows = await Future.delayed( const Duration(milliseconds: 500), // 延迟 500 毫秒,模拟 http 请求 () => countryOptions .where((Country county) => county.name .toLowerCase() .startsWith(debounceValue.toLowerCase())) .toList()); } finally { isDebounce = false; // 关闭防抖标志 } return rows; }); } }, // 格式化显示字段 displayStringForOption: (Country option) => option.name, // 输入字段组件 fieldViewBuilder: ( BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted, ) { return TextField( controller: fieldTextEditingController, focusNode: fieldFocusNode, style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.amber, fontSize: 24, ), ); }, // 选中项时 onSelected: (Country selection) { if (kDebugMode) { print('Selected: ${selection.name}'); } }, // 构建下拉列表项 optionsViewBuilder: ( BuildContext context, AutocompleteOnSelected<Country> onSelected, Iterable<Country> options, ) { return Align( alignment: Alignment.topLeft, child: Material( child: Container( width: 300, color: Colors.cyan, child: ListView.builder( padding: const EdgeInsets.all(10.0), itemCount: options.length, itemBuilder: (BuildContext context, int index) { final Country option = options.elementAt(index); return GestureDetector( onTap: () { onSelected(option); }, child: ListTile( title: Text(option.name, style: const TextStyle(color: Colors.white)), ), ); }, ), ), ), ); }, ); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Autocomplete Getx 防抖")), body: Padding(padding: const EdgeInsets.all(10), child: _mainView()), ); } }
end
Copyright 2023 ducafecat. All rights reserved.
微信: ducafecat, line: ducafecat,京ICP备2021009050号-3