猫哥课堂 ducafecat.com
附件下载

Flutter AutoComplete Debounce 防抖

@programunity Photo shared by programunity on February 06, 2023 taggin

效果

image-20230206121620330

前言

防抖是指在短时间内无意义的多次操作,然后我们规定单位时间内只处理一次。

比如搜索框正在输入,就不应该频繁的去查询处理,控制在500毫秒响应一次查询操作。

本文将会说明 AutoComplete 组件的使用以及加入防抖的控制,提示用户交互体验。

代码

完整代码详见 github .

https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_autocomplete

正文

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

编写 Autocomplete 组件

返回可选列表数据值

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