[Flutter] Expandable bottom sheet dialog 를 직접 만들어보자

2023. 6. 1. 23:33IT/Flutter

SMALL

오늘 만들어 볼 ui 는 Expandable bottom sheet dialog 이다.

 

위 그림처럼 드래그를 했을때 드래그 한 만큼 길이가 늘어나거나 줄어드는 인터렉션을 가진 ui 이다.

 

 

해당 ui를 만들기 위해서 필요한 것은 다음과 같다.

 

  1. 드래그 이벤트가 발생할때마다 dy 차이(delta dy) 를 BottomSheetContainer 의 height 에 더해준다

  2. 사이즈를 최소 미만 또는 최대 초과 할 수 없도록 한다.

  3. 드래그 이벤트가 끝났을 때, 설정된 기준점에 도달했다면 사이즈를 최대로 늘리고 그렇지 않다면 최소로 줄인다.

 

 

첫번째로, 

delta dy 를 BottomSheetContainer 의 기존 height 값에 더해주면, 드래그 하는만큼 컨테이너의 height 도 늘어남으로 마치 컨테이너의 크기가 마우스를 따라 늘었다 줄었다 하는것 같은 인터렉션을 만들 수 있다.

 

그럼, delta dy 는 어떻게 구할 수 있을까 ?

 

flutter 에서는 제스쳐를 감지할 수 있게 도와주는 widget 이 있는데 GestureDetector 위젯이 바로 그것이다.

GestureDetector 위젯의 onVerticalDragUpdate 콜백에서 해당 이벤트를 받을 수 있다.

 

/// A pointer that is in contact with the screen with a primary button and
/// moving vertically has moved in the vertical direction.
///
/// See also:
///
///  * [kPrimaryButton], the button this callback responds to.
final GestureDragUpdateCallback? onVerticalDragUpdate;

 

구현을 해보면...

  Offset _updateOffset = Offset.zero; // update 시 delta offset

  double _bottomContainerHeight = 300; // 최소 컨테이너 높이
  double get bottomContainerHeight => _bottomContainerHeight;
  set bottomContainerHeight (double value) {
    _bottomContainerHeight = value;
  }


// GestureDetector( .....
/child : Container ...
onVerticalDragUpdate: (value){
 _updateOffset = value.delta; // delta offset

 final result = bottomContainerHeight - _updateOffset.dy; // 컨테이너 높이 최종 결과값

 setState(() {
  bottomContainerHeight = result; // 결과를 높이에 반영하기
 });
},

//.....)

 

위 아래로 드래그를 할 수 있게 되었다!

 

 

두번째로, 높이의 최소, 최대값을 설정할 차례이다.

여러 방법이 있겠지만 나는 간단히 컨테이너 높이의 최종 결과값이 최소값미만 또는 최대값 초과일 경우에 대해서는 높이를 반영하지 않는 방법을 선택했다.

 

final result = bottomContainerHeight - _updateOffset.dy;

                if(result < 300){
                  return;
                }

                if(result > MediaQuery.of(context).size.height - 100){
                  return;
                }

                setState(() {
                  bottomContainerHeight = result;
                });

 

세번째로, 기준점에 따라 컨테이너의 크기를 조절해줄 단계이다.

GestureDetector가 제공해주는 콜백함수중에 OnVerticalDragEnd 가 있는데 이것을 활용하면 가능하다.

해당 콜백 안에서 드래그가 끝난 시점에 컨테이너의 크기를 기준점과 비교하여 그에따라 최소 또는 최대크기로 변경해주면 된다.

 

나는 최소는 300, 최대는 size.height - 100 으로 잡았고 기준점은 해당 값들의 중간지점으로 benchmark를 설정했다.

 

onVerticalDragEnd: (value){
  final benchMark = (((MediaQuery.of(context).size.height - 100 ) - 300 )/ 2) + 300;

  print(benchMark);

  if(bottomContainerHeight < benchMark){
    setState(() {
      bottomContainerHeight = 300;
    });
    return;
  }else{
    setState(() {
      bottomContainerHeight = MediaQuery.of(context).size.height - 100;
    });
    return;
  }
},

 

생각보다 어렵지않게 인터렉션을 만들어냈다!

 

일반적으로 드래그없이 크기를 조절하는것은 AnimatedContainer 같은 것을 활용하면 쉽게 만들 수 있지만 드래그를 사용하는것이 조금더 사용자 친화적으로 보인다. 또한 중간중간에 또다른 인터렉션을 만들수도 있으니 잘만 활용하면 참 좋을 것 같다.

소스코드 : https://github.com/sejun2/draggable_bottom_sheet/

LIST