ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter + Spring Boot 가족, 모임 서비스 구현하기 3.2 (Feat. UI 구현)
    Flutter 2023. 10. 26. 12:04
    728x90
    반응형
    SMALL

    이전 글에서 이어집니다!...

    이전 글 보러가기

     

    Flutter + Spring Boot 가족, 모임 서비스 구현하기 3 (Feat. UI 구현)

    가족, 모임 서비스 구현 중 Flutter로 UI 구성 시 있었던 시행착오들을 글로 작성해보려고 합니다. 구현 중 알게 된 점들이나, 트러블슈팅 과정들이 섞여 있어 글이 매끄럽지 않을 수 있다는 점 양

    beomseok37.tistory.com


     

     

    1. ChangeNotifierProvider list

    ChangeNotifier로 정의한 전역 상태값을 사용할 경우 ChangeNotifierProvider로 상태값을 사용할 수 있도록 주입해주어야 합니다.

     

    하나의 상태값을 넣어줄 경우 다음과 같이 진행하면 됩니다.

    runApp(
      ChangeNotifierProvider(
        create: (context) => LoginModel(KakaoLogin()),
        child: const JibbapApp(),
      ),
    );

     

    두 개 이상의 상태값을 넣어줄 경우에는 MultiProvider를 통해 상태값을 넣어줄 수 있습니다.

    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (context) => LoginModel(KakaoLogin()),
          ),
          ChangeNotifierProvider(
            create: (context) => CurrentGroupInfoModel(),
          ),
        ],
        child: const JibbapApp(),
      ),
    );

     

    위의 상태들은 모두 전역 상태로 runApp시 상태값을 넣어줌으로써 모든 위젯에서 사용이 가능하도록 해주었지만, 각 위젯에서도 하위 위젯들에게 상태값을 주입할 경우 Provider로 하위 위젯을 감싸준다면 하위 위젯에서 주입된 상태값을 사용할 수 있습니다.

     

     

    Flutter의 상태관리 방법에 대해 조사해본 결과 Provider를 통해 상태를 주입하는 Provider Pattern UI와 business logic을 분리해주는 BLoC 패턴이 있습니다.

     

    BLoC 패턴을 간단히 소개하자면,

    UI 객체들이 BLoC 객체를 구독하고 있고, BLoC 객체의 상태가 변경된다면 상태를 구독 중인 UI 객체들은 해당 상태를 통해 UI 화면을 재구성합니다.

    BLoC 객체는 필요한 데이터를 다른 레이어로부터 전달 받아 로직을 처리해주기 때문에 UI는 화면 구성 코드에, BLoC은 logic 처리 코드에 집중할 수 있습니다.

    BLoC 패턴은 구글 개발자에 의해 Flutter를 위해 설계되었지만, 하나의 디자인 패턴이므로 다른 프레임워크에서도 사용이 가능할 것 같습니다. (자바스크립트에서도 Observer Pattern을 구현할 경우 동일하게 사용할 수 있을 것 같습니다.)

     

    Flutter의 상태관리 도구 종류에 관련된 글들이 많으니 찾아보는 것도 좋은 경험이 될 것 같습니다.

     

     

     

    2. AlertDialog size

    Flutter Dialog의 height나 width를 지정할 수 있는 방법에 대해 알아보도록 하겠습니다.

     

    Dialog의 크기 조정은 내부 위젯의 크기를 조정하면 자동으로 지정이 됩니다.

    만약, 특정 값으로 height를 조절하고 싶다면 내부 Container 위젯이나 SizedBox로 크기를 지정해주면 됩니다.

    AlertDialog(
        content: Container(
            width: width,
            height: height,
            child: ...,
        ),
    )

     

    만약 내부 content의 size에 맞게 자동으로 조절되도록 하기 위해서는 다음과 같이 할 수 있습니다.

    AlertDialog(
        content: Container(
            child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                    ...
                ],
            ),
        ),
    )

    주축의 크기를 최소치로 지정해준다면 특정 height로 지정하지 않아도 내부 content의 크기에 맞게 자동으로 조절이 됩니다.

     

     

    3. PageView

    단계별로 어떤 행위를 해야할 경우 페이지가 왼쪽, 오른쪽으로 슬라이드되면서 다음 단계로 혹은 이전 단계로 움직일 수 있도록 하는 UI 구성이 필요했습니다. 하나 하나 구현을 해서 만들 수도 있지만 아무래도 Flutter가 이미 구현된 위젯들이 많기 때문에 제공되는 위젯을 사용해보기 위해 찾아본 결과 PageView 위젯을 사용할 경우 그대로 구현이 가능해 보였습니다.

     

    final PageController controller = PageController();
    
    PageView(
      /// [PageView.scrollDirection] defaults to [Axis.horizontal].
      /// Use [Axis.vertical] to scroll vertically.
      controller: controller,
      children: const <Widget>[
        Center(
          child: Text('First Page'),
        ),
        Center(
          child: Text('Second Page'),
        ),
        Center(
          child: Text('Third Page'),
        ),
      ],
    );

    다음과 같이 지정해주었을 경우 좌우로 슬라이드 시 페이지를 이동할 수 있게 됩니다.

     

    onPagedChanged 속성을 통해 페이지가 변경되었을 경우 실행할 함수를 지정할 수 있습니다.

    PageView(
        controller: pageController,
        physics: const NeverScrollableScrollPhysics(),
        onPageChanged: onPageChanged,
        children: [
            ...
        ]
    )

     

    저와 같은 경우 어떤 조건을 만족했을 경우에만 다음 페이지로 이동할 수 있도록 해주어야 하기 때문에 슬라이드를 통해 다음 페이지로 이동하면 안됐습니다. 그러므로 다음 속성을 추가해 이를 막아주었습니다.

     

    스크롤 불가 속성

    PageView(
        controller: pageController,
        physics: const NeverScrollableScrollPhysics(),
        ...
    )

    PageView의 physics에 NeverScrollableScrollPhysics을 지정해주면 스크롤을 통해 다음페이지 이동이 불가하게 됩니다.

    페이지 이동을 하기 위해서는 controller를 사용하면 됩니다.

     

    controller를 통한 페이지 이동

    pageController.nextPage(
      duration: const Duration(milliseconds: 200),
      curve: Curves.linear,
    );
    
    pageController.previousPage(
      duration: const Duration(milliseconds: 200),
      curve: Curves.linear,
    );
    
    pageController.animateToPage(page, duration: duration, curve: curve);

    버튼을 클릭했을 경우 위의 로직을 넣어주면 페이지 이동이 가능합니다.

    controller의 nextPagepreviousPage를 통해 직전, 직후 페이지로 이동할 수 있습니다.

    animateToPage를 통해 특정 페이지로도 이동이 가능합니다.

     

    duration을 지정해 slide animation의 지속 시간을 설정해줄 수 있고 curve를 통해 animation의 시간에 따른 변화를 조절해 줄 수 있습니다.

     

    curve 종류

    ///  * [Curves.fastLinearToSlowEaseIn]
    ///  * [Curves.ease]
    ///  * [Curves.easeIn]
    ///  * [Curves.easeInToLinear]
    ///  * [Curves.easeInSine]
    ///  * [Curves.easeInQuad]
    ///  * [Curves.easeInCubic]
    ///  * [Curves.easeInQuart]
    ///  * [Curves.easeInQuint]
    ///  * [Curves.easeInExpo]
    ///  * [Curves.easeInCirc]
    ///  * [Curves.easeInBack]
    ///  * [Curves.easeOut]
    ///  * [Curves.linearToEaseOut]
    ///  * [Curves.easeOutSine]
    ///  * [Curves.easeOutQuad]
    ///  * [Curves.easeOutCubic]
    ///  * [Curves.easeOutQuart]
    ///  * [Curves.easeOutQuint]
    ///  * [Curves.easeOutExpo]
    ///  * [Curves.easeOutCirc]
    ///  * [Curves.easeOutBack]
    ///  * [Curves.easeInOut]
    ///  * [Curves.easeInOutSine]
    ///  * [Curves.easeInOutQuad]
    ///  * [Curves.easeInOutCubic]
    ///  * [Curves.easeInOutQuart]
    ///  * [Curves.easeInOutQuint]
    ///  * [Curves.easeInOutExpo]
    ///  * [Curves.easeInOutCirc]
    ///  * [Curves.easeInOutBack]
    ///  * [Curves.fastOutSlowIn]
    ///  * [Curves.slowMiddle]

     

     

    4. Flutter animation

    Flutter는 animation 또한 많은 위젯들을 제공해주고 있습니다.

    AnimatedContainer, AnimatedOpacity, AnimatedIcon 등 많은 애니메이션 관련 위젯들이 제공되고 있습니다.

     

    Flutter Animation Widgets

     

    Animation and motion widgets

    A catalog of Flutter's animation widgets.

    docs.flutter.dev

     

    AnimatedContainer의 경우 위젯의 크기, 색상, 테두리 등에 대한 변화가 일어날 경우 자동으로 애니메이션을 적용해주는 위젯입니다.

    물론, 해당 크기, 색상, 테두리들은 stateful widget 내부 상태값으로 지정해주고 변경해주어야 합니다.

     

    AnimtaedOpacity는 내부 위젯의 투명도 변화에 애니메이션을 적용해줍니다.

     

    AnimatedIcon은 애니메이션을 적용할 수 있는 아이콘들을 사용할 수 있습니다. 아이콘의 개수가 적어 사용 범위가 그렇게 넓니는 않을 것 같습니다.

     

     

     

    5. 둥근 이미지

    유저 프로필의 경우 둥근 이미지로 제작하고 싶어 모든 이미지를 동일한 사이즈에 테두리를 동그랗게 만들어주고 싶었습니다.

    처음 시도했던 이미지가 다행이도 가로 세로 길이 차이가 많이 나는 직사각형의 이미지였기 때문에 이 오류를 찾을 수 있었습니다.

     

    처음에는 테두리를 동그랗게 해준 컨테이너 위젯을 감싼 형태로 했더니 이미지의 사이즈마다 다른 출력 형태를 보여주게 되었습니다.

    이미지가 클 경우 해당 컨테이너 내부에 잘 이미지가 조정이 되었지만 크기가 작을 경우 둥근 직경에 이미지가 들어가지 않아 테두리가 직선인 부분도 생기게 되었습니다.

    이를 해결하기 위해 크기에 상관없이 동그란 이미지를 잘 구현해주는 위젯을 찾아보게 되었습니다.

     

    CircleAvatar

    CircleAvatar(
      radius: 48, // Image radius
      backgroundImage: NetworkImage('imageUrl'),
    )

    CircleAvatar 위젯을 사용하면 정해준 직경대로 모든 이미지들이 사이즈에 맞게 동그란 형태로 지정될 수 있습니다.

    반응형
    LIST

    댓글

Designed by Tistory.