前言
我们在开发客户端或者Web端中,总是会遇到用户多次点击登录,而造成多次的网络请求,如果不做限制,不光用户体验差,同时可能也会给服务器带来一定量的负担,而如何避免遇到这种问题呢?
本文就引出了“防抖”、“节流”两个概念
接下来简单讲下这两个东西
防抖节流
基础概念
节流(throttle)与 防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。
区别
防抖(debounce):在一定时间内,任务频繁触发的情况下,只会保证函数动作只执行一次
节流(throttle):指定范围时间内只会执行一次,会有类似“CD冷却”概念
实战应用
接下来以一个简单的登录系统为例
定义基础视图
import 'dart:async';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
class Example08 extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return Example08State();
}
}
class Example08State extends State<Example08>{
TextEditingController userNameController = TextEditingController(text: "");
TextEditingController passWordController = TextEditingController(text: "");
@override
void initState()
super.initState();
}
@override
void dispose() {
super.dispose();
}
login(){
String uName = userNameController.text;
String uPwd = passWordController.text;
if(uName == "" || uPwd == ""){
BotToast.showText(text: "账号密码不能为空!");
return;
}
if(uName == "admin" && uPwd == "root"){
BotToast.showText(text: "登录成功");
return;
}
BotToast.showText(text: "登录失败,请确认账号密码是否正确后重试!");
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("防抖和节流案例demo",style: TextStyle(
color: Colors.white
),),
centerTitle: false,
backgroundColor: Colors.blue.shade300,
),
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 30),
Container(
child: Center(
child: Text(
"登录系统",
style: TextStyle(
color: Colors.blue,
fontSize: 26
),
),
),
),
SizedBox(height: 30),
//账号
Container(
child: FractionallySizedBox(
widthFactor: .6,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: Text(
"账号:",
style: TextStyle(
color: Colors.black,
fontSize: 16,
),
),
),
Expanded(
child: Container(
child: SizedBox(
height: 30,
child: TextField(
controller: userNameController,
decoration: InputDecoration(
hintText: "请输入账号",
hintStyle: TextStyle(
color: Colors.grey.shade300,
fontSize: 14
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300,width: 1),
borderRadius: BorderRadius.circular(5)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300,width: 1),
borderRadius: BorderRadius.circular(5)
),
contentPadding: EdgeInsets.only(left: 10)
),
style: TextStyle(
fontSize: 14,
color: Colors.black
),
),
),
),
)
],
),
)
),
SizedBox(height: 30),
//密码
Container(
child: FractionallySizedBox(
widthFactor: .6,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: Text(
"密码:",
style: TextStyle(
color: Colors.black,
fontSize: 16,
),
),
),
Expanded(
child: SizedBox(
height: 30,
child: TextField(
controller: passWordController,
decoration: InputDecoration(
hintText: "请输入密码",
hintStyle: TextStyle(
color: Colors.grey.shade300,
fontSize: 14
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300,width: 1),
borderRadius: BorderRadius.circular(5)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade300,width: 1),
borderRadius: BorderRadius.circular(5)
),
contentPadding: EdgeInsets.only(left: 10)
),
style: TextStyle(
fontSize: 14,
color: Colors.black
),
),
),
)
],
),
)
),
SizedBox(height: 30),
Container(
width: MediaQuery.of(context).size.width,
height: 40,
child: FractionallySizedBox(
widthFactor: .3,
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue.shade300),
shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)))
),
onPressed: (){
login();
},
child: Center(
child: Text(
"登录",
style: TextStyle(
color: Colors.white,
letterSpacing: 10,
fontSize: 16
),
)
),
),
),
)
],
),
),
);
}
}
我们已经把视图准备完毕。
目前我们的登录按钮这里是直接执行login方法,也就是每点击一次就会调用一次,接下来我们引入相关节流与防抖拓展代码
/*
* @author Marinda
* @date 2024/8/6 15:36
* @description 方法增强
*/
extension FunctionExtension on Function{
static Map<Function,Timer?> debounceMap = {};
static Map<Function,bool> throttleMap = {};
static Map<Function,Timer?> throttleTimerMap = {};
void Function() debounce([int milliseconds = 500]){
return(){
if(debounceMap[this]?.isActive ?? false){
debounceMap[this]?.cancel();
BotToast.showText(text: "已提交,请不要过快点击!");
}
debounceMap[this] = Timer(Duration(milliseconds: milliseconds),(){
this();
debounceMap[this] = null;
});
};
}
/*
* @author Marinda
* @date 2024/8/6 16:09
* @description 节流
*/
void Function() throttle([int milliseconds = 500]){
throttleMap[this] = true;
return(){
if(!throttleMap[this]!) return;
throttleMap[this] = false;
this();
throttleTimerMap[this]?.cancel();
throttleTimerMap[this] = Timer(Duration(milliseconds: milliseconds),(){
throttleTimerMap[this] = null;
throttleMap[this] = true;
});
};
}
}
我们函数增强拓展代码完毕,这里是我个人案例,防抖和节流实现方法有很多种,仅供参考。
接下来在应用中实例
节流写法
//新增节流方法变量 late Function onThrottleFunction; ... initState(){ onThrottleFunction = (){ login(); }.throttle(1000); } ... onPressed: (){ onThrottleFunction(); },
这样就完成了节流,点击后,会在1000毫秒内只会执行一次
防抖写法
...
//直接onPressed
onPressed: (){
var cb = login.debounce(1000);
cb();
},
...
到此功能完毕,大家可以自己跑跑Example看看效果,功能概念也比较简单。
结束语
感谢你的观看!