이미지를 입힌 버튼이 필요했다.

대표적인 구글, 네이버, 카카오 간편 로그인이다.

 

하지만, 플러터에서는 버튼에 이미지를 입히는 것을 제공하지 않고 있다. (그만한 이유가 있었을까?)

꼭 간편로그인 뿐만 아니라 투명 배경의 이미지를 버튼식으로 만들어서 클릭해야 하는 일이 있다.

 

물론 GestrueDetector, InkWell를 입혀서 표현해주어도 되지만,

Image위젯을 감싼다하더라고 이미지를 클릭한 모션을 제공해주지 않는다.

 

라이브러리 프로젝트 생성

 

무엇을 만들 것인지 정한다 (Project type:)

 

이렇게 만들면 '끝'이지만,

내가 만들 라이브러리(패키지)를 테스트 해보면서 만들어야 완성도가 높아질 것이다.

 

그런 점을 위해서 플러터에서 'example'영역을 제공해준다.

명령어에 다음과 같이 입력한다.

flutter create example .

 

위의 명령어는 모든 플랫폼에 적용가능한 example을 생성해준다.

만약에 특정 OS만 테스트가 가능한 example을 만들고 싶은 경우 다음과 같이 명령한다.

(ex. 안드로이드, iOS, 웹)

flutter create --platforms android,ios,web -a kotlin -i swift example .

 

+ 개별로 만들고 싶은 경우

flutter create --platforms ios .

 

 

라이브러리 example영역 참조 방법

현재 내가 만든 라이브러리를 example영역에서 참조하고 있지 않기 때문에,

참조를 해주어 테스트를 해볼 수 있다.

 

현재 제작하는 라이브러리 이름은 'picture_button'이다.

다음과 같이 작성한다.

dependencies:
  flutter:
    sdk: flutter
  
  [library_name]:
    path: ../

 

 

Pub.dev사이트 업로드

업로드 하기전 검증 명령어

flutter pub publish --dry-run

 

업로드 명령어

flutter pub publish

 

 

이후 'y'를 입력하여 승인 한다.

 

 

+ 추가로 패키지를 올리기전 코드들을 자동으로 정리해주는 명령어를 사용하면 좋다.

dart format ./

 

PageView를 사용하거나 DefaultTabController를 사용하여 화면을 표시해주는 경우에,

위젯 자체가 다른 화면으로 바뀌는 순간에 'dispose'가 되어 사라지게 된다.

 

총 A, B, C화면이 있을 때 A→B →C로 넘어갔다가 다시 A←B ←C로 페이지를 스크롤 했을때,

리스트들이 유지되었으면 좋겠을 때, 다음과 같이 설정해준다.

 

State<T>를 상속한 클래스에 AutomaticKeepAliveClientMixin을 추가한다.

해당 mixin은 StatefulWidget에서 사용이 가능하다.

class _ExamplePageListState extends State<ExamplePageList> with AutomaticKeepAliveClientMixin {

 

그럼, _ExamplePageListState에 빨간줄이 그어질 것이다.

반드시 재정의가 필요한 함수가 있기 때문이다.

 

wantKeepAlive라는 함수이며, 'true'로 설정해주어야 위젯이 재선언이 되지 않는다.

(반대로, 'false'로 선언하면 이전과 같이 위젯이 reload된다.)

@override
bool get wantKeepAlive => true;

 

 

해당 위젯을 선언한 부모 위젯이 dispose되지 않는 이상 계속해서 유지된다.

 

 

감사합니다🥫

플러터에서는 아주 간편하게 'Navigator'인스턴스를 사용하여 스크린, 페이지를 자유롭게 이동할 수 있다.

심지어 return값까지 받을 수 있다.

 

하지만 페이지를 이동할 때 단순히 보여줄 때도 있지만,

자연스럽게 애니메이션 효과를 넣어서 이동한다면 사용자 입장에서 더욱 부드러운 경험을 갖게 된다.

 

지금부터 작성하는 것은 아마 앞으로 나도 자주 현재 페이지를 방문할 것 같다.

 

애니메이션을 표현하는 대표적인 단어

- Fade : 흐리게(opacity)

- Scale : 크기

- Translate : x, y축으로 이동

 

- Tween : 직선

- Spring : 완곡하게

 

위의 정도가 있다고 알고 사용하면 된다.

애니메이션 효과를 주는 것은 Flutter팀에서 제공하는 라이브러리 또는 PageRouteBuilder위젯을 사용하여 표현할 수 있다.

 

1. 플러터 팀에서 제공하는 페이지 애니메이션 라이브러리

이름 : animations

링크 : https://pub.dev/packages/flutter_staggered_animations

 

add

flutter pub add flutter_staggered_animations

 

import

import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';

 

 

위 라이브러리는 정말 친절하게도 본인이 만든 리스트나 페이지에 애니메이션을 주고 싶은 경우

해당 위젯을 감싸주기만 하면된다.

 

내가 ListView.builder를 사용해서 리스트를 만들고, 내부에 아이템을 추가한 경우이다

@override
Widget build(BuildContext context) {
  return SafeArea(
    child: ListView.builder(
      itemCount: datas.length,
      itemBuilder: (context, index) {
        return Text("item[$index]");
      }
    )
  );
}

 

위의 위젯에 라이브러리를 사용하고 싶은 경우 다음과 같이

AnimationLimiter, AnimationConfiguration.staggeredList, SlideAnimation(+FadeInAnimation)위젯으로 감싸주면 된다.

 

@override
Widget build(BuildContext context) {
  return SafeArea(
    child: AnimationLimiter(
      child: LiseView.builder(
        itemCount: datas.length,
        itemBuilder: (context, index) {
          return AnimationConfiguration.staggeredList(
            position: index,
            child: SlideAnimation(
              verticalOffset: 50.0,
              child: FadeInAnimation(
                child: Text("item[index]")
              )
            )
          );
        }
      )
    )
  );
}

 

 

 

2. PageRouteBuilder를 사용한 애니메이션

PageRouteBuilder는 Navigator함수 내부에서 사용한다.

아래의 코드는 다른 스크린 위젯을 띄어주는데

우측에서 좌측으로 이동되면서 점점 FadeIn흐릿하면서 선명해지는 동작이다.

 

  await Navigator.push(context,
    PageRouteBuilder(
     pageBuilder: (context, animation, secondaryAnimation) {
        return SecondaryScreen(data: data);
      },
     transitionsBuilder: (context, animation, secondaryAnimation, child) {
       const begin = Offset(1.0, 0.0);
       const end = Offset.zero;
       const curve = Curves.easeInOut;

       var slideTween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
       var fadeTween = Tween<double>(begin: 0.0, end: 1.0).chain(CurveTween(curve: curve));

       var slideAnimation = animation.drive(slideTween);
       var fadeAnimation = animation.drive(fadeTween);

       return SlideTransition(
         position: slideAnimation,
         child: FadeTransition(
           opacity: fadeAnimation,
           child: child,
         ),
       );
     },
      transitionDuration: const Duration(milliseconds: 450),
  ));

 

pageBuilder함수 파라메터에는 다음 보여줄 위젯을 설정해준다.

애니메이션을 넣어줄 곳 transitionBuilder함수파라메터에서 설정해준다.

 

(예시 이미지는 추후 업로드 하도록 하겠습니다.)

 

감사합니다🍖

앱을 만들게되면 '이미지'를 다루는 일은 기본입니다.

플러터에서는 제가 알기로 Image, ImageProvider 이렇게 두 가지 방식으로 이미지를 표현할 수 있는데요.

 

'Image'위젯을 사용해서 표현하는 방법을 설명하고자 합니다.

네 가지 방식을 제공합니다.

- Image.network : 인터넷을 통하여 이미지 로드

- Image.file : 파일을 통하여 이미지 로드

- Image.memory : bytes값을 통해 이미지 로드

- Image.asset : 로컬 Asset폴더를 통해 이미지 로드

 

 

화질이 높은 이미지일 수록 그 이미지를 표현하기 위해서 컴퓨터는 계산을 하게 됩니다.

계산하는 동안 화면에 아무런 표시도 되어있지 않아도 상관은 없지만,

사용자의 입장에서 고려해보면 무언가 이미지가 나올것인가? 라는 기대감과

기다릴 수 있게하는 인내심을 주지 않을까요?

 

그래서 저는 이미지가 로드되는 동안 LoadingProgressbar를 표현하려고 합니다.

 

FutureBuilder를 통해서 받은 이미지 데이터를 'Image.memory'함수에 추가하였습니다.

그리고 frameBuildererrorBuilder를 설정해주었습니다.

 

 

이미지가 로드되는 동안 프로그레스바 표시하기

위의 언급되었던 frameBuilder를 통해서 표현할 수 있습니다.

child이미지 위젯을 뜻 합니다.

 

wasSynchronouslyLoaded는 동기화가 되었는지 확인해주는 파라메터입니다.

동기화가 되었다면 바로 child(이미지 위젯)을 보여주어도 되겠지만,

이미지를 로드한다는 것은 비동기 작업이기 때문에 아마 true일 일은 많이 없을겁니다.

frame은 현재 이미지가 로드가 완료되어 객체가 되었는지를 알려주는 것 입니다.

framenull이 아니라는건 이미지가 정상적으로 로드된 것을 뜻하기 때문에 child를 return해줍니다.

 

그 이외의 상황이라면 LoadingProgressbar를 보여주면 되겠습니다.

frame == null
frame != null

 

그러면 위와 같이 frame이 null이 아닌 동안 else하위의 LoadingProgressbar가 표현되게 됩니다.

 

+ 추가로 errorBuilder는 이미지가 정상적으로 로드되지 못한 경우 실행됩니다.

그러므로 이미지가 로드되지 못한 경우 보여줄 이미지 위젯을 선언해주면 되겠습니다.

 

 

감사합니다🍊

플러터를 배우게되면 StatelessWidget, StatefulWidget 이 두 형태로 위젯을 만드는 것을 이해하게 됩니다.

저도 물론 마찬가지로 이 두 가지를 다음과 같은 상황에 따라 사용하게 됩니다.

 

StatelessWidget : 내부 로직에서 상태변화가 일어나지 않는 경우 사용

StatefulWidget : 내부 로직에서 상태변화가 일어나는 경우 사용

 

상태변화가 일어난다는 것은 'int count'라는 프로퍼티가 있고,

Text위젯을 사용해서 현재 'count'가 가지고 있는 값을 표출할 때,

'count'값이 늘어나거나 감소하는 경우 상태변화가 일어난다고 합니다.

 

이렇게 변화된 값을 표출하기 위해서는 StatefulWidget에서 제공하는 

setState 함수를 사용하여 위젯을 재빌드하고 변경된 값을 보여주게 됩니다.

 

StatelessWidget에서는 애초에 값 변화가 없는 경우 선택을 하지만,

때로는 그렇지 않은 경우도 있습니다.

 

- 앱을 보수하거나 기능을 추가가 필요한 경우

- 부모 클래스가 StatelessWidget으로 선언된 경우

- 다이얼로그에서 사용이 필요한 경우

등.. 값을 갱신해주는 것이 필요할 때가 있습니다.

 

 

좀 더 플러터에 대해서 다양한 기능을 사용해보았다면,

HeritedWidget, Selector, Consumer위젯 등을 사용해서 표현할 수 있지만,

더욱 간단한 방법을 플러터 팀에서 제공해주고 있습니다.

 

그 위젯의 이름은 바로 'StatefulBuilder' 위젯입니다.

저같은 경우 다이얼로그에서 필요하여 사용하였었습니다.

 

코드를 살펴보면 StatefulBuilder위젯의 하위에 값의 변화가 필요한 위젯을 선언해줍니다.

그리고 setState함수를 커스텀 위젯으로 전달하여 사용합니다.

 

위의 코드에서는 EditingTextController에서 이벤트가 발생할 때마다 UI가 재빌드되도록 해주었네요.

StatelessWidget에서 곧 바로 StatefulWidget으로 바꾸어도 좋지만,

더 큰 보수가 들어가거나 어쩔 수 없는 경우 StatefulBuilder를 사용하여 UI를 재빌드해주도록 합니다 !

 

감사합니다🥦

 

 

링크 : https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html

Flutter로 모바일 개발을 하게되면 Scaffold라는 위젯으로 UI구조의 시작을 알리게 됩니다.

네이티브로 UI를 구성하게 되면 일일히 구분해주었던 영역을 Scaffold위젯하나로 정리를 하게되니,

저로써는 너무나 편리하게 느껴지는데요. 여러분도 그러실까요?

 

하지만 Scaffold로 위젯의 구조를 정해주게될 때, appBar파라메터를 설정해주지 않게 되면,

다음과 같이 상단에 시계색상과 네트워크 상태 색상이 보이지 않게 됩니다.

 

상단을 보시면 플러터 로고는 보이지만 그외 색상은 현재 보이지 않습니다.

(상단바의 이름은 상단 네비게이션바, status바 등 이름으로 불립니다.

플러터에서는 statusBar라고 불리지만, 이해하기 쉽도록 다양한 이름을 같이 부르도록 할게요)

위 화면의 코드는 다음과 같이 이루어져 있습니다.

 

쭉 ~ child를 따라가다보면 Scaffold가 보이실 텐데요.

그 하단에 SafeArea가 있고 이후 임의로 정의된 위젯들이 선언되어 있습니다.

 

그럼, 상단의 상태값들이 보이게 수정해보도록 하겠습니다.

 

상단 상태(statusBar, top navigation bar)보이도록 수정

appBar파라메터를 정의해주지 않고 body파라메터에만 위젯을 설정해주는 경우만 해당됩니다.

우선 AnnotatedRegion이라는 위젯으로 body의 위젯들을 감싸줍니다.

 

그리고 상단의 상태바의 색상을 지정해주는데요.

가장, 중요한 왜? 왜? 상태바의 색상이 안보이느냐? 의 대한 대답은

색상이 현재 System Brightness자체의 색상으로 보여지고 있기 때문입니다.

'다크 모드'라고 들어보셨을까요?

스마트폰에 기본 모드는 밝지만, 다크모드는 어두운 분위기를 띄게 되는데요.

 

바로, 네비게이션 상단 바의 색상은 기본 모드이기 때문에 흰 색 글씨로 보이게 되는 것 입니다.

자세히 보시면 흰 색으로 얼핏 보이는 것을 알 수 있습니다.

 

 

이런 현상을 해결하기 위해서는 상단바의 색상을 다크모드로 변경해주어야 합니다.

(따로 임의의 색상을 설정하는 기능이 없기 떄문입니다)

 

먼저, 현재 다크모드인지 확인해주는 bool프로퍼티를 생성합니다.

(build안에서 또는 initState하는 시점에 생성해주세요^^)

@override 
Widget build(BuildContext context) {
  bool isDarkMode = MediaQuery.of(context).platformBrightness == Brightness.dark;
  
  return Scaffold(
    ...
  )
}

 

AnnotatedRegion위젯으로 body하위 위젯을 감싸줍니다.

return Scaffold(
  body: AnnotatedRegion<SystemUiOverlayStyle>(
    value: SystemUiOverlayStyle(
      ...
    ),
    child: SafeArea(
      ...
    )
  )
)

 

SystemUiOverlayStyle 파라메터를 살펴보겠습니다.

- statusBarColor : 상단 네비게이션바의 배경색상

- statusBarIconBrightness : 시간, 와이파이, 배터리 상태를 Icon으로 표현합니다. 

 

statusBarColor는 기본 primary색상이 적용되어 지정해주지 않아도 되지만,

보통 Scaffold의 배경색상을 White로 설정하면 이질감이 있기 떄문에 투명하게 지정해주는 것을 추천합니다.

 

statusBarIconBrightness현재 '기본모드' 이기 때문에 '다크모드'로 변형해주어야 글자가 보인다는 것

앞의 내용을 이해했다면 생각할 수 있습니다.

 

아래와 같이 설정해주도록 하겠습니다.

AnnotatedRegion<SystemUiOverlayStyle>(
  value: SystemUiOverlayStyle(
    statusBarColor: Colors.transparent,
    statusBarIconBrightness: isDarkMode ? Brightness.light : Brightness.dark
  )
)

 

결과는?

 

위 상단의 글자가 이제 식별이 되는 것을 볼 수 있습니다.

 

상단 네비게이션의 글자가 보이지 않는다고해서 당황하지마시고,

차근차근 위의 설명을 토대로 만들어가시면 되겠습니다.

 

감사합니다🍸

+ Recent posts