skip to Main Content

I have this UI: enter image description here
When user taps on right side we go to the next story, left we go to previous and holding down anywhere should stop the automatic loading on top. I am currently using GestureDetectors and using the three callbacks

  • onTap To go to next or previous story
  • onTapDown To pause the loader
  • onTapUp To resume the loader

The problem is onTap gets called with onTapUp so when I try to pause for example by holding down on the right side, it would immediately go to the next story after I lift my finger.

4

Answers


  1. Try to use a combination of onHorizontalDragStart, onHorizontalDragEnd, and onHorizontalDragUpdate instead of onTapDown and onTapUp. This will allow you to differentiate between taps and drags, providing more control over the pause and resume actions.

    GestureDetector(onHorizontalDragStart: (details) {
        setState(() {
          _isLoading = true;
        });
      },
      onHorizontalDragEnd: (details) {
        setState(() {
          _isLoading = false;
        });
      },
      onHorizontalDragUpdate: (details) {
        // You can handle swipe gestures here if needed
      },
    
    Login or Signup to reply.
  2. Let’s try the below solution, I tested it and it works quite well in your case.

    Below is a custom GestureDetector widget that allows us to set the duration for onLongPressStart and onLongPressEnd

    import 'package:flutter/material.dart';
    import 'package:flutter/gestures.dart';
    
    class CustomGestureLongPress extends StatelessWidget {
      final Widget? child;
      final Duration duration;
      final GestureLongPressStartCallback onLongPressStart;
      final GestureLongPressEndCallback onLongPressEnd;
      final VoidCallback? onLongPress;
    
      const CustomGestureLongPress({
        super.key,
        this.child,
        required this.duration,
        required this.onLongPressStart,
        required this.onLongPressEnd,
        this.onLongPress,
      });
    
      @override
      Widget build(BuildContext context) {
        return RawGestureDetector(
          behavior: HitTestBehavior.translucent,
          gestures: <Type, GestureRecognizerFactory>{
            LongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
                  () => LongPressGestureRecognizer(duration: duration), (instance) {
                    instance.onLongPress = () {
                      print("Parent onLongPress");
                      onLongPress?.call();
                    };
                    instance.onLongPressStart = (details) {
                      print("Parent onLongPressStart");
                      onLongPressStart.call(details);
                    };
                    instance.onLongPressEnd = (details) {
                      print("Parent onLongPressEnd");
                      onLongPressEnd.call(details);
                    };
                  },
            ),
          },
          child: child,
        );
      }
    }`
    

    And the usage of the custom widget

    CustomGestureLongPress(
                duration: const Duration(milliseconds: 200),
                onLongPressStart: (_) {
                  //TODO Pause the loader
                },
                onLongPressEnd: (_) {
                  //TODO Resume the loader
                },
                child: Stack(
                    children: [
                      GestureDetector(
                        onTap: () {
                          //TODO Go to previous story
                        },
                        child: Align(
                          alignment: Alignment.centerLeft,
                          child: Container(
                            width: 50.w,
                            color: Colors.transparent,
                          ),
                        )
                      ),
                      GestureDetector(
                        onTap: () {
                          //TODO Go to next story
                        },
                        child: Align(
                          alignment: Alignment.centerRight,
                          child: Container(
                            width: 50.w,
                            color: Colors.transparent,
                          ),
                        )
                      )
                    ]
                ),
              )
    
    Login or Signup to reply.
  3. A tap gesture consists of a tap-down followed "immediately" by a tap-up gesture. Consequently, as you indicate, if you combine those callbacks they will overlap each other.

    What you are looking:

    • For pausing the loader: onLongPressStart (or onLongPressDown)
    • For resuming the loader: onLongPressEnd (or onLongPressUp)

    This will allow differentiating between long-presses and taps.

    Login or Signup to reply.
  4. The more simple and customizable approach is using Timer for delaying tap interaction.

    This is the simple workaround to demonstrate:

    import 'package:flutter/material.dart';
    import 'dart:async';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      MyApp({super.key});
      
      final isPaused = ValueNotifier(false);
      Timer? timer;
      
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: ValueListenableBuilder(
              valueListenable: isPaused,
              builder: (_, paused, child) {
                return ColoredBox(
                  color: paused ? Colors.red : Colors.yellow,
                  child: child,
                );
              },
              child: GestureDetector(
                onTapDown: (_) {
                  print('> onTapDown');
                  timer = Timer(const Duration(milliseconds: 300), () {
                    isPaused.value = true;
                    print('Story paused');
                  });
                },
                onTapUp: (_) {
                  print('> onTapUp');
                  if (isPaused.value) {
                    print('Resume current story');
                  } else {
                    print('Go to the next story');
                  }
                  timer?.cancel();
                  isPaused.value = false;
                },
              ),
            ),
          ),
        );
      }
    }
    

    And this is the result:

    demo

    You can reusing this widget to achieve the story next-prev navigation by layouting this widget into a row to capture left and right area for story navigation.

    Maybe it could be an alternative solution for your problem, Thanks 😉

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search