Rune Alexander Larsen
Rune Alexander Larsen

Reputation: 41

How to prevent multiple click on a gesture?

I have a GestureDetector that need to launch a url. But if the gesture gets multiple taps, then launch is called multiple times.

In the current code im trying to use a state _isButtonTapped to control the tap. But the .whenComplete is somehow call before the launch is preformed?

   _isButtonTapped = false

   Widget _buildButton(String key, Text title, String url) {
    _onTapped() async {
      if (await canLaunch(url)) {
        launch(url).whenComplete(
          () => setState(() {
                _isButtonTapped = false;
              }),
        );
      }
    }

    return GestureDetector(
      onTap: () {
        _isButtonTapped ? null : _onTapped();
        setState(() {
          _isButtonTapped = true;
        });
      },
      child: Container(
        child: Padding(
          padding: EdgeInsets.all(6.0),
          child: Center(child: title),
        ),
      ),
    );
  }

Upvotes: 2

Views: 7126

Answers (4)

MarcoFerreira
MarcoFerreira

Reputation: 376

You have a bug in your code.

You are setting _isButtonTapped to true everytime you press it. Correct you onTap function:

  return GestureDetector(
      onTap: () {
        if (_isButtonTapped == false){
          _onTapped();
          setState(() {
            _isButtonTapped = true;
          }); 
        },
      }
      //...

Regarding why the whenComplete is not beign called when you expected, that's another problem. I never used it but tacking a quick look into the docs (https://api.flutter.dev/flutter/scheduler/TickerFuture/whenComplete.html) show us that are multiple ways of achiving this, including wraping the function in an try block and use thr finally cloused as the whenCompleted. You should take a look at he docs and tried it out. Can't help more with that detail.

Hope it helps you.

Upvotes: 0

Thavrith Tok
Thavrith Tok

Reputation: 56

the easiest way is in inkWell widget put doubleTap: () {}, it will do nothing, when user click multiple time

Upvotes: 1

loalexzzzz
loalexzzzz

Reputation: 493

Try this? It should solve your problem.

class SafeOnTap extends StatefulWidget {
  SafeOnTap({
    Key? key,
    required this.child,
    required this.onSafeTap,
    this.intervalMs = 500,
  }) : super(key: key);
  final Widget child;
  final GestureTapCallback onSafeTap;
  final int intervalMs;

  @override
  _SafeOnTapState createState() => _SafeOnTapState();
}

class _SafeOnTapState extends State<SafeOnTap> {
  int lastTimeClicked = 0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        final now = DateTime.now().millisecondsSinceEpoch;
        if (now - lastTimeClicked < widget.intervalMs) {
          return;
        }
        lastTimeClicked = now;
        widget.onSafeTap();
      },
      child: widget.child,
    );
  }
}

You can wrap any kind of widget if you want.

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            children: [
              // every click need to wait for 500ms
              SafeOnTap(
                onSafeTap: () => log('500ms'),
                child: Container(
                  width: double.infinity,
                  height: 200,
                  child: Center(child: Text('500ms click me')),
                ),
              ),
              // every click need to wait for 2000ms
              SafeOnTap(
                intervalMs: 2000,
                onSafeTap: () => log('2000ms'),
                child: Container(
                  width: double.infinity,
                  height: 200,
                  child: Center(child: Text('2000ms click me')),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Upvotes: 3

CopsOnRoad
CopsOnRoad

Reputation: 268484

Try this:

class _HomePageState extends State<HomePage> {
  bool _isButtonTapped = false;
  String _url = "https://google.ca";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          color: Colors.blue,
          width: 100,
          height: 100,
          child: GestureDetector(
            onTap: () async {
              if (!_isButtonTapped) { // only allow click if it is false
                _isButtonTapped = true; // make it true when clicked
                if (await canLaunch(_url)) {
                  await launch(_url);
                  _isButtonTapped = false; // once url is launched successfully, we again make it false, allowing tapping again
                }
              }
            },
          ),
        ),
      ),
    );
  }
}

Upvotes: 3

Related Questions