Flutter; GestureDetector의 onTapUp 오류(onTapDown 유지되는 오류) 해결방법

     

    GestureDetector를 사용하던 중 onTapUp과 onTapDown만 사용하던 중 불편한 점을 발견. 불편한 점이 무엇이냐면 보통의 상황에서는 눌렀다 떼면 원하는 기능이 잘 작동한다. 그러나 TapDown상태에서 손가락을 끌어서 나중에 놓는 경우 onTapUp이 작동하지 않음.

     

    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    bool isClickedBtn1 = false;
    
    class _MyAppState extends State<MyApp> {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Test',
          home: Scaffold(
            body: Center(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  GestureDetector(
                    onTapUp: (_) => setState(() {
                      isClickedBtn1 = false;
                    }),
                    onTapDown: (_) => setState(() {
                      isClickedBtn1 = true;
                    }),
                    child: Container(
                      width: 100,
                      height: 100,
                      color: Colors.amberAccent,
                      child: Center(
                        child: isClickedBtn1
                            ? const Text('Clicked')
                            : const Text('button1'),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }

     

    위 코드는 onTapUp과 onTapDown만 사용한 코드.

     

    이를 해결하기 위해서 onTapCancel을 사용.

    onTapCancel: () => setState(() {
    	isClickedBtn1 = false;
    }),

     

     

    그러나, onTapCancel을 사용했는데 또 불편한 점이 발생.

    버튼을 누른 상태(TapDown)에서 손가락을 누른 상태로 끌어서 움직이면 onTapUp 대신 onTapCancel이 호출되는데, 이때 약간만 움직여도 onTapCancel이 호출 되어 맘에 들지 않음.

     

    그래서 Listener를 사용하는 방법 채택.

    Listener(
    	onPointerDown: (details) {
    		setState(() {
    			isClickedBtn2 = true;
    		});
    	},
    	onPointerUp: (details) {
    		setState(() {
    			isClickedBtn2 = false;
    		});
    	},
    	child: Container(
    		width: 100,
    		height: 100,
    		color: Colors.amberAccent,
    		child: Center(
    			child: isClickedBtn2 ? const Text('Clicked') : const Text('button2'),
    		),
    	),
    ),

     

    내가 딱 원하던 동작을 수행함. TapDown 상태에서 손가락을 끌어서 TapUp을 하면 onTapUp이 호출되는 동작을 생각한다면 Listener를 사용하는 게 나을 거 같다.

     

     

     

     

    전체 코드

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    bool isClickedBtn1 = false;
    bool isClickedBtn2 = false;
    
    class _MyAppState extends State<MyApp> {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          home: Scaffold(
            body: Center(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  GestureDetector(
                    onTapUp: (_) => setState(() {
                      isClickedBtn1 = false;
                    }),
                    onTapDown: (_) => setState(() {
                      isClickedBtn1 = true;
                    }),
                    onTapCancel: () => setState(() {
                      isClickedBtn1 = false;
                    }),
                    child: Container(
                      width: 100,
                      height: 100,
                      color: Colors.amberAccent,
                      child: Center(
                        child: isClickedBtn1
                            ? const Text('Clicked')
                            : const Text('button1'),
                      ),
                    ),
                  ),
                    Listener(
                      onPointerDown: (details) {
                        setState(() {
                          isClickedBtn2 = true;
                        });
                      },
                      onPointerUp: (details) {
                        setState(() {
                          isClickedBtn2 = false;
                        });
                      },
                      child: Container(
                        width: 100,
                        height: 100,
                        color: Colors.amberAccent,
                        child: Center(
                          child: isClickedBtn2
                              ? const Text('Clicked')
                              : const Text('button2'),
                        ),
                      ),
                    ),
                ],
              ),
            ),
          ),
        );
      }
    }

     

    댓글