Search code examples
androidbuttonmaterial-designfluttergesturedetector

Flutter Execute Method so long the button pressed


I want to execute a method while a user is pressing down on a button. In pseudocode:

while (button.isPressed) {
  executeCallback();
}

In other words, the executeCallback method should fire repeatedly as long as the user is pressing down on the button, and stop firing when the button is released. How can I achieve this in Flutter?


Solution

  • Use a Listener and a stateful widget. I also introduced a slight delay after every loop:

    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'),
              ),
            ),
          ),
        );
      }
    }