Flutter手势防抖插件gesture_debouncer的使用

Flutter手势防抖插件gesture_debouncer的使用

插件介绍

gesture_debouncer 是一个简单的 Flutter 小部件,可以作为防抖器来吸收某些手势,根据冷却时间或未来的状态。

这个小部件在触发与 UI 交互的后端 API 时非常有用。它确保如果用户不小心多次点击,则不会为每次点击都调用后端 API。

通常,在 WebSockets/REST API 使用成功回调的情况下,使用 Completer 将其转换为 Future,并在该小部件中使用。

Completer<List?> completer = Completer<List?>();
Socketio!.emitWithAck("your backend socketio event", params, ack: (data) {
  if (data) // 如果数据符合预期
  {
    if (!completer.isCompleted) {
      completer.complete(data);
    }
  } else {
    if (!completer.isCompleted) {
      completer.complete(null);
    }
  }
});
return completer.future;

喜欢我的工作吗?支持我

示例

完整的示例请查看 example/main.dart

Widget _getIconWidget(onPressed) {
  return IconButton(
    icon: Icon(
      liked ? FontAwesome.heart : FontAwesome.heart_empty,
    ),
    iconSize: iconSize,
    color: liked ? Colors.red : Colors.grey,
    onPressed: onPressed,
  );
}

// 检测其他手势
Widget _getDoubleClickWidget(onPressed) {
  return GestureDetector(
    onDoubleTap: onPressed,
    child: Icon(
      liked2 ? FontAwesome.heart : FontAwesome.heart_empty,
      size: iconSize,
      color: liked2 ? Colors.green : Colors.grey,
    ),
  );
}
GestureDebouncer(
  cooldownAfterExecution: const Duration(seconds: 5),
  cooldownBuilder: (BuildContext ctx, _) {
    return Stack(
      alignment: AlignmentDirectional.center,
      children: [
        _getIconWidget(() {
          if (liked) {
            toast("你最近已点赞此内容");
          } else {
            toast("你最近已取消点赞此内容");
          }
        }),
        IgnorePointer(
          // 用于使主图标获得点击事件
          child: Icon(
            Icons.lock_outline,
            color: liked ? Colors.white : Colors.grey,
            size: iconSize / 3,
          ),
        ),
      ],
    );
  },
  onError: (e) {
    log(e.toString());
  },
  builder: (BuildContext ctx, Future<void> Function()? onGesture) {
    return _getIconWidget(onGesture);
  },
  onGesture: () async {
    setState(() {
      liked = !liked;
    });
    await Future.delayed(const Duration(seconds: 5), () {});
  },
)

截图

完整示例代码

import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:fluttericon/font_awesome_icons.dart';
import 'package:gesture_debouncer/gesture_debouncer.dart';
import 'package:overlay_support/overlay_support.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return OverlaySupport.global(
      toastTheme: ToastThemeData(
        background: Colors.indigo,
        textColor: Colors.white,
      ),
      child: MaterialApp(
        title: '手势防抖器',
        theme: ThemeData(
          primarySwatch: Colors.indigo,
          primaryColor: Colors.indigo,
        ),
        home: const HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  [@override](/user/override)
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool liked = false;
  bool liked2 = false;
  double iconSize = 45;

  Widget _getIconWidget(onPressed) {
    return IconButton(
      icon: Icon(liked ? FontAwesome.heart : FontAwesome.heart_empty),
      iconSize: iconSize,
      color: liked ? Colors.red : Colors.grey,
      onPressed: onPressed,
    );
  }

  Widget _getDoubleClickWidget(onPressed) {
    return GestureDetector(
      onDoubleTap: onPressed,
      child: Icon(
        liked2 ? FontAwesome.heart : FontAwesome.heart_empty,
        size: iconSize,
        color: liked2 ? Colors.green : Colors.grey,
      ),
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text(
          "手势防抖器",
          style: TextStyle(fontSize: 15),
        ),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text(
                "单击检测器",
                style: TextStyle(color: Colors.red),
              ),
              GestureDebouncer(
                cooldownAfterExecution: const Duration(seconds: 2),
                cooldownBuilder: (BuildContext ctx, _) {
                  return Stack(
                    alignment: AlignmentDirectional.center,
                    children: [
                      _getIconWidget(() {
                        if (liked) {
                          toast("你最近已点赞此内容");
                        } else {
                          toast("你最近已取消点赞此内容");
                        }
                      }),
                      IgnorePointer(
                        // 用于使主图标获得点击事件
                        child: Icon(
                          Icons.lock_outline,
                          color: liked ? Colors.white : Colors.grey,
                          size: iconSize / 3,
                        ),
                      ),
                    ],
                  );
                },
                onError: (e) {
                  log(e.toString());
                },
                builder: (BuildContext ctx, Future<void> Function()? onGesture) {
                  return _getIconWidget(onGesture);
                },
                onGesture: () async {
                  setState(() {
                    liked = !liked;
                  });
                  await Future.delayed(const Duration(seconds: 2), () {});
                },
              ),
            ],
          ),
          const SizedBox(height: 50),
          Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text(
                "双击检测器",
                style: TextStyle(color: Colors.green),
              ),
              GestureDebouncer(
                cooldownAfterExecution: const Duration(seconds: 2),
                cooldownBuilder: (BuildContext ctx, _) {
                  return Stack(
                    alignment: AlignmentDirectional.center,
                    children: [
                      _getDoubleClickWidget(() {
                        if (liked2) {
                          toast("你最近已点赞此内容(双击)");
                        } else {
                          toast("你最近已取消点赞此内容(双击)");
                        }
                      }),
                      IgnorePointer(
                        // 用于使主图标获得点击事件
                        child: Icon(
                          Icons.lock_outline,
                          color: liked2 ? Colors.white : Colors.grey,
                          size: iconSize / 3,
                        ),
                      ),
                    ],
                  );
                },
                onError: (e) {
                  log(e.toString());
                },
                builder: (BuildContext ctx, Future<void> Function()? onGesture) {
                  return _getDoubleClickWidget(onGesture);
                },
                onGesture: () async {
                  setState(() {
                    liked2 = !liked2;
                  });
                  await Future.delayed(const Duration(seconds: 2), () {});
                },
              ),
            ],
          ),
        ],
      ),
    );
  }
}

更多关于Flutter手势防抖插件gesture_debouncer的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter手势防抖插件gesture_debouncer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


gesture_debouncer 是 Flutter 中用于防抖(debounce)手势操作的一个插件。防抖是一种常见的技术,用于防止在短时间内多次触发同一个事件,特别是在处理用户输入、按钮点击或手势操作时非常有用。

1. 安装插件

首先,你需要在 pubspec.yaml 文件中添加 gesture_debouncer 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  gesture_debouncer: ^1.0.0  # 请根据最新版本号进行替换

然后,运行 flutter pub get 来安装插件。

2. 使用 GestureDebouncer

GestureDebouncer 是一个 Widget,它可以包裹其他需要防抖操作的 Widget,比如 GestureDetectorInkWell 等。

以下是一个简单的示例,展示如何使用 GestureDebouncer 来防止按钮在短时间内多次点击:

import 'package:flutter/material.dart';
import 'package:gesture_debouncer/gesture_debouncer.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Gesture Debouncer Example'),
        ),
        body: Center(
          child: GestureDebouncer(
            child: ElevatedButton(
              onPressed: () {
                print('Button clicked!');
              },
              child: Text('Click Me'),
            ),
            debounceDuration: Duration(milliseconds: 500), // 设置防抖时间
          ),
        ),
      ),
    );
  }
}

3. 参数说明

  • debounceDuration: 设置防抖的时间间隔。在这个时间间隔内,如果用户多次触发手势操作,只会执行一次回调。默认值是 Duration(milliseconds: 300)

  • child: 需要防抖操作的子 Widget,比如 ElevatedButtonGestureDetector 等。

4. 解释

在上面的例子中,GestureDebouncer 包裹了 ElevatedButton,并设置了 debounceDuration 为 500 毫秒。这意味着,如果用户在 500 毫秒内多次点击按钮,只会触发一次 onPressed 回调。

5. 使用场景

GestureDebouncer 适用于以下场景:

  • 按钮防抖: 防止用户快速连续点击按钮,导致多次触发事件。
  • 手势防抖: 防止用户快速连续进行手势操作,比如双击、长按等。
  • 输入防抖: 在处理用户输入时,防止频繁触发回调,比如搜索框的动态搜索。

6. 注意事项

“gesture_debouncer” 插件的版本可能随着时间更新,使用前请确保查看最新的文档和版本信息。

7. 其他实现方式

如果你不想使用第三方插件,也可以自己实现一个简单的防抖逻辑。以下是一个自定义防抖的示例:

import 'package:flutter/material.dart';

class DebouncedButton extends StatefulWidget {
  final VoidCallback onPressed;
  final Widget child;
  final Duration debounceDuration;

  DebouncedButton({
    required this.onPressed,
    required this.child,
    this.debounceDuration = const Duration(milliseconds: 500),
  });

  [@override](/user/override)
  _DebouncedButtonState createState() => _DebouncedButtonState();
}

class _DebouncedButtonState extends State<DebouncedButton> {
  DateTime? _lastPressed;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        final now = DateTime.now();
        if (_lastPressed == null ||
            now.difference(_lastPressed!) >= widget.debounceDuration) {
          _lastPressed = now;
          widget.onPressed();
        }
      },
      child: widget.child,
    );
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Custom Debounced Button'),
        ),
        body: Center(
          child: DebouncedButton(
            onPressed: () {
              print('Button clicked!');
            },
            child: Text('Click Me'),
          ),
        ),
      ),
    );
  }
}
回到顶部