import 'dart:math'; import 'package:flutter/material.dart'; class RepeatOnHoldButton extends StatefulWidget { /// Function to be called on tap and on long press. /// Return [false] to signify that the loop should be broken after execution. final bool? Function() onTap; /// Specifies whether repetition speeds up when the user keeps holding the button. final bool increaseSpeed; /// Specifies how many ms should pass before action is repeated. final int initialRepetitionIntervalMs; /// Specifies by how much the interval between actions should be divided after [speedUpAfterTimes] times. final int speedUpFactor; /// Specifies how many times [onTap] will be called before increasing the speed. final int speedUpAfterTimes; final Widget child; const RepeatOnHoldButton({ Key? key, required this.onTap, this.increaseSpeed = true, this.initialRepetitionIntervalMs = 250, this.speedUpFactor = 2, this.speedUpAfterTimes = 5, required this.child, }) : super(key: key); @override _RepeatOnHoldButtonState createState() => _RepeatOnHoldButtonState(); } class _RepeatOnHoldButtonState extends State { bool _isHeld = false; void onLongPress() async { setState(() { _isHeld = true; }); int holdCycle = 0; int speed = widget.initialRepetitionIntervalMs; while (true) { final result = widget.onTap() ?? true; if (!_isHeld || !result) { break; } holdCycle++; if (speed > 1 && holdCycle % widget.speedUpAfterTimes == 0) { speed = max(1, (speed ~/ widget.speedUpFactor)); } await Future.delayed( Duration( milliseconds: speed, ), ); } } @override Widget build(BuildContext context) { return GestureDetector( onLongPress: onLongPress, onLongPressEnd: (_) => _isHeld = false, child: widget.child, ); } }