⌨️flutter

Roman Ismagilov 공부회. flutter tap effect native인척하기. 자연스러운 버튼!

덩크냥 2025. 3. 10. 21:32

https://medium.com/@pomis172/making-flutter-apps-look-more-native-part-1-tap-effects-48a59cb158bf

 

Making Flutter apps look more native. Part 1: tap effects

By default, if you are using InkWells to handle tap events in your UI, a ripple effect will be applied. The issue here is that while the…

medium.com

InkWells쓰면 리플효과가 있다. 리플 효과는 안드로이드 디자인에서는 필수적인 요소이지만, iOS에서는 다소 어색하게 보일 수 있다는 점이다.

대신 ios는 버튼을 탭할 때 리플 효과 대신 콘텐츠가 부드럽게 페이드 아웃(fade out)되는 효과가 더 일반적이다. 리플효과는 물결처럼 퍼지는것~

 

자 그럼 플러터에서 inkwell로 안드로이드 버튼효과를 줄수 있다.

IOS는? 다르게 적용되어야겠다.

함께 보자,

 

class TapArea extends StatefulWidget {
  final Widget child;
  final GestureTapCallback? onTap;
  final GestureTapCallback? onLongTap;
  final double borderRadius;
  final EdgeInsets? padding;

  const TapArea({
    super.key,
    required this.child,
    required this.onTap,
    this.onLongTap,
    this.padding,
    this.borderRadius = 0,
  });

  @override
  State<TapArea> createState() => defaultTargetPlatform == TargetPlatform.iOS
      ? _TapAreaIosState()
      : _TapAreaAndroidState();
}

이걸 보면 Inkwell대신 쓰일 TapArea라는위젯이 있다.

 

defaultTargetPlatform을 확인하여 플랫폼별로 다른 적용을 할 수 있다.

 

안드로이드 타겟 위젯

class _TapAreaAndroidState extends State<TapArea> {
  @override
  Widget build(BuildContext context) {
    final content = Padding(
      padding: widget.padding ?? EdgeInsets.zero,
      child: widget.child,
    );

    if (widget.onTap == null && widget.onLongTap == null) return content;

    return Material(
      color: Colors.transparent,
      clipBehavior: Clip.none,
      borderRadius: BorderRadius.circular(widget.borderRadius),
      child: InkWell(
        borderRadius: BorderRadius.circular(widget.borderRadius),
        onTap: widget.onTap,
        onLongPress: widget.onLongTap,
        child: content,
      ),
    );
  }
}

 

 

IOS타겟위젯은 Inkwell 대신 GestureDetector을 사용한다.

class _TapAreaIosState extends State<TapArea> {
  bool _isDown = false;

  @override
  Widget build(BuildContext context) {
    final content = Padding(
      padding: widget.padding ?? EdgeInsets.zero,
      child: widget.child,
    );

    if (widget.onTap == null && widget.onLongTap == null) return content;

    return GestureDetector(
      onTapDown: (_) {
        setState(() {
          _isDown = true;
        });
      },
      onTapCancel: () {
        setState(() {
          _isDown = false;
        });
      },
      onTap: () {
        setState(() {
          _isDown = false;
        });
        widget.onTap!();
      },
      onLongPress: widget.onLongTap,
      child: Focus(
        child: Opacity(
          opacity: _isDown ? 0.7 : 1.0,
          child: content,
        ),
      ),
    );
  }
}

둘다 터치시 이벤트로 다른 처리를 하는걸 볼 수 있다.

 

결론: 플러터는 크로스플랫폼이어서 범용적 코드작성이 필요한데 그런점에서 native UI에대한 이해를 가지고 커스텀 위젯을 만드는게 좋은것같다.

 

 

=> 내부적으로 플랫폼 타고 들어가서 안드로이드는 InkWell, IOS는 GestureDetector을 사용하도록 연결해주면 범용적인 커스텀 위젯이 된다.



활용예제 : 

TapArea(

  onTap: () {

    print("버튼 클릭됨!");

  },

  borderRadius: BorderRadius.only( // 🎯 각 모서리 다르게 설정 가능!

    topLeft: Radius.circular(20),

    bottomRight: Radius.circular(10),

  ),

  child: Container(

    padding: EdgeInsets.all(16),

    decoration: BoxDecoration(

      color: Colors.blue,

      borderRadius: BorderRadius.only( // 🎯 컨테이너도 동일한 radius 적용!

        topLeft: Radius.circular(20),

        bottomRight: Radius.circular(10),

      ),

    ),

    child: Text(

      "클릭하세요!",

      style: TextStyle(color: Colors.white),

    ),

  ),

)