Flutter开发笔记 —— 防抖和节流的应用实战

前言

我们在开发客户端或者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看看效果,功能概念也比较简单。

结束语

感谢你的观看!

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇