Flutter Execute Method 长按按钮

Posted

技术标签:

【中文标题】Flutter Execute Method 长按按钮【英文标题】:Flutter Execute Method so long the button pressed 【发布时间】:2019-02-07 06:15:25 【问题描述】:

我想在用户按下按钮时执行一个方法。在伪代码中:

while (button.isPressed) 
  executeCallback();

换句话说,只要用户按下按钮,executeCallback 方法就会重复触发,并在按钮释放时停止触发。如何在 Flutter 中实现这一点?

【问题讨论】:

那么GestureLongPressCallback onLongPress 属性有什么问题? 压机操作后onLongPress被触发但压机操作过程中需要的问题 这没什么意义:如果您只是进行正常的快速按下(意味着您在短时间内松开手指),那么您如何获得长按? 正是我需要的,我需要的方法将被执行很长时间,以至于手指放在按钮上 这个想法是关于音乐播放器中的前言按钮,当用户点击按钮时我需要寻找歌曲前言 【参考方案1】:

使用Listener 和有状态小部件。我还在每个循环之后引入了一点延迟:

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(brightness: Brightness.dark),
      home: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  int _counter = 0;

  bool _buttonPressed = false;
  bool _loopActive = false;

  void _increaseCounterWhilePressed() async 
    // make sure that only one loop is active
    if (_loopActive) return;

    _loopActive = true;

    while (_buttonPressed) 
      // do your thing
      setState(() 
        _counter++;
      );

      // wait a bit
      await Future.delayed(Duration(milliseconds: 200));
    

    _loopActive = false;
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Listener(
          onPointerDown: (details) 
            _buttonPressed = true;
            _increaseCounterWhilePressed();
          ,
          onPointerUp: (details) 
            _buttonPressed = false;
          ,
          child: Container(
            decoration: BoxDecoration(color: Colors.orange, border: Border.all()),
            padding: EdgeInsets.all(16.0),
            child: Text('Value: $_counter'),
          ),
        ),
      ),
    );
  

【讨论】:

这正是我所需要的 请同时处理onPointerCancel ;)【参考方案2】:

没有监听器的更简单的方法如下:

  GestureDetector(
      child: InkWell(
        child: Icon(Icons.skip_previous_rounded),
        onTap: widget.onPrevious,
      ),
      onLongPressStart: (_) async 
        isPressed = true;
        do 
          print('long pressing'); // for testing
          await Future.delayed(Duration(seconds: 1));
         while (isPressed);
      ,
      onLongPressEnd: (_) => setState(() => isPressed = false),
    );
  

【讨论】:

【参考方案3】:

基于ThinkDigital 的解决方案,我的观察是InkWell 包含执行此操作所需的所有事件,而无需额外的GestureDetector(我发现GestureDetector 会干扰长按时的墨水动画) .这是我为一个宠物项目实现的控件,该控件在按住时触发其事件并减少延迟(这是一个带有图标的圆形按钮,但任何使用 InkWell 的东西都可以):

/// A round button with an icon that can be tapped or held
/// Tapping the button once simply calls [onUpdate], holding
/// the button will repeatedly call [onUpdate] with a
/// decreasing time interval.
class TapOrHoldButton extends StatefulWidget 
  /// Update callback
  final VoidCallback onUpdate;

  /// Minimum delay between update events when holding the button
  final int minDelay;

  /// Initial delay between change events when holding the button
  final int initialDelay;

  /// Number of steps to go from [initialDelay] to [minDelay]
  final int delaySteps;

  /// Icon on the button
  final IconData icon;

  const TapOrHoldButton(
      Key? key,
      required this.onUpdate,
      this.minDelay = 80,
      this.initialDelay = 300,
      this.delaySteps = 5,
      required this.icon)
      : assert(minDelay <= initialDelay,
            "The minimum delay cannot be larger than the initial delay"),
        super(key: key);

  @override
  _TapOrHoldButtonState createState() => _TapOrHoldButtonState();


class _TapOrHoldButtonState extends State<TapOrHoldButton> 
  /// True if the button is currently being held
  bool _holding = false;

  @override
  Widget build(BuildContext context) 
    var shape = CircleBorder();
    return Material(
      color: Theme.of(context).dividerColor,
      shape: shape,
      child: InkWell(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Icon(
            widget.icon,
            color:
                Theme.of(context).textTheme.headline1?.color ?? Colors.white70,
            size: 36,
          ),
        ),
        onTap: () => _stopHolding(),
        onTapDown: (_) => _startHolding(),
        onTapCancel: () => _stopHolding(),
        customBorder: shape,
      ),
    );
  

  void _startHolding() async 
    // Make sure this isn't called more than once for
    // whatever reason.
    if (_holding) return;
    _holding = true;

    // Calculate the delay decrease per step
    final step =
        (widget.initialDelay - widget.minDelay).toDouble() / widget.delaySteps;
    var delay = widget.initialDelay.toDouble();

    while (_holding) 
      widget.onUpdate();
      await Future.delayed(Duration(milliseconds: delay.round()));
      if (delay > widget.minDelay) delay -= step;
    
  

  void _stopHolding() 
    _holding = false;
  


它在行动:

【讨论】:

很好的回答,非常感谢!

以上是关于Flutter Execute Method 长按按钮的主要内容,如果未能解决你的问题,请参考以下文章

flutter textfield 长按输入框出现剪切/复制/粘贴的菜单如何设置中文?

Flutter WebView安卓端输入框不能长按粘贴

页面报错误:HTTP Status 500 - Method "execute" failed for object com.oa.action.loginAction@3c346

行中图像的动态列表,带有换行符(之间没有空格),允许长按图像打开编辑选项 - Flutter

Flutter开发iOS上TextField长按或反复点击报错No CupertinoLocalizations found.

android的报错提示:java.lang.IllegalStateException: Could not execute method for android:onClick