File - New - New Flutter Project - Package

 

pubspec.yaml 설정

name: [라이브러리 이름]
description: [설명]
version: 1.0.1 //버전
homepage: [홈페이지 주소]
repository: [코드 저장소 주소]

 

 

example 폴더 생성

- 생성시 pub.dev에서 example란에 표시됨

flutter create example

 

- android, ios만 생성하고 싶은 경우

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

 

 

example yaml에서 본인 라이브러리를 사용하는 방법

path: ../ 추가

dev_dependencies:
  flutter_test:
    sdk: flutter

  [라이브러리 이름]:
    path: ../

 

 

코드 정렬

dart format .

 

점검, 확인

flutter pub publish --dry-run

 

올리기

flutter pub publish

 

 


Pub Cache경로 설정

https://dart.dev/tools/pub/cmd/pub-cache

User에 대한 사용자 변수이름 : PUB_CACHE경로 : D:/src/Pub/Cache- 컴퓨터 다시 시작을 해야 잘 적용된다

 

 

특정 OS만 가능하도록 설정

: 아래 사항을 표시하지 않으면 모든 OS에서 사용이 가능함을 뜻함

flutter:
  plugin:
    platforms:
      android: ^2.3.0
      ios: ^3.0.0

 

 

플러그인 

전체 추가 명령

- 아무 플랫폼도 없거나 해당 플랫폼 빌드가 안되면 플러터에서 알려줄 것이다.

flutter create .

 

개별 추가 명령

- android, ios, web, macos, window

flutter create --platforms -android .

 

flutter create --org com.example --template=plugin --platforms=android,ios,linux,macos,windows -a kotlin hello

 

다음과 같이 실행해주어야 '플러그인'이 생성된다.

$ flutter create --org com.donguran --template=plugin --platforms=android,ios -a kotlin -i swift torch

 

 

그리고 gitignore에 다음을 꼭 명시해준다.

* 8 checked-in files are ignored by a `.gitignore`.
  Previous versions of Pub would include those in the published package.

  Consider adjusting your `.gitignore` files to not ignore those files, and if you do not wish to
  publish these files use `.pubignore`. See also dart.dev/go/pubignore

  Files that are checked in while gitignored:

  .idea/.gitignore
  .idea/inspectionProfiles/Project_Default.xml
  .idea/libraries/Dart_SDK.xml
  .idea/libraries/Flutter_Plugins.xml
  .idea/misc.xml
  .idea/modules.xml
  .idea/torch.iml
  .idea/vcs.xml


* D:\donguran\dev\flutter\app\torch\CHANGELOG.md doesn't mention current version (0.1.1).
  Consider updating it with notes on this version prior to publication.
Package validation found the following hint:
* You're about to publish a package that opts into null safety.
  The previous version (0.1.0) isn't opted in.
  See https://dart.dev/null-safety/migration-guide for best practices.

아래는 이미 버전이 있다는 말..

 

패키지 우측 이미지 넣기

ref : bloc패키지 참조

screenshots:
  - description: torchx logo.
    path: screenshots/logo.png

 

 

해당 패키지 admin에서 publisher를 수정할 수 있다.

 

2024-04-23-화 : 패키지 경로를 설정할 때

export: "pacakge:..." 로 시작하지 않고,

내부 경로로 설정해주어도 괜찮다.

 

그리고 'show'키워드를 통해서 사용할 기능만 등록할 수 있다.

 

 

패키지 우측 이미지 넣기

ref : bloc패키지 참조

screenshots:
  - description: torchx logo.
    path: screenshots/logo.png

 

 

해당 패키지 admin에서 publisher를 수정할 수 있다.

 

2024-04-23-화 : 패키지 경로를 설정할 때

export: "pacakge:..." 로 시작하지 않고,

내부 경로로 설정해주어도 괜찮다.

 

그리고 'show'키워드를 통해서 사용할 기능만 등록할 수 있다.

 

 

플러터 폴더명 관례

'src'라는 이름으로 폴더를 만들면 외부에서 접근할 수 없게 된다.

'assets'라는 이름으로 폴더를 만들어야 앱에 번들로 포함되어 빌드된다.

assets하위의 폴더이름은 자유롭게 해도 상관없지만, 

  • json → data
  • 이미지 → images
  • 폰트 → fonts

인터넷 연결이 되어있는지 확인하기위해서 다음 패키지를 사용한다.

 

Add

flutter pub add internet_connection_checker

 

Usage

import 'package:internet_connection_checker/internet_connection_checker.dart';

bool isInternetConnected = await InternetConnectionChecker().hasConnection;

 

 

 

여러번 확인을 하기 위해 'StreamBuilder'를 사용한다.

그리고 10초마다 확인하기위한 Steam함수를 작성한다.

Stream<bool> checkInternetConnectedPeriodically() async* {
  bool isConnect = await checkInternetConnect();
  
  while (!isConnect) {
    yield await checkInternetConnect();
    
    isConenct = await checkInternetConnect();
    await Future.delayed(const Duration(seconds: 10));
  }
  
  yield isConnect;
}
Future<bool> checkInternetConnected() async {
  return await InternetConnectionChecker().hasConnection;
}

 

Use StreamBuilder

@override
Widget build(BuildContext context) {
  stream: checkInternetConnectedPeriodically(),
  builder: (context, AsyncSnapshot<bool> snapshot) {
    if (snapshot.data == true) {
      return Home();
    } else {
      return LoadingProgressbar();
    }
  }
}

Flutter에서 Android의 Fragment처럼 동작되는 위젯을 구현해보았다.

만들 예제는 하나의 기본 화면이 Container안에 있고 추가적으로 다른 위젯을 대체시키고, 뒤로가기가 가능하여 앞의 위젯이 다시 표출되도록 만들것

 

우선, Fragment처럼 사용할 위젯들을 담아내는 Container를 하나 만든다.

List<Widget> container = [];

 

(StatefulWidget으로 setState함수로 UI를 갱신시킬 것이다)

 

 

Android에서 Fragment를 보여주려면 FrameLayout라는 레이아웃안에 넣어 보여준다.

FrameLayout을 '액자'라고 생각하고 Fragment'사진'이라고 생각하면 쉽다.

 

'액자'역할을 해줄 위젯을 만든다. 뒤로가기시 Fragment화면의 동작의 유연함도 고려해야하기 때문에,

'PopScope'위젯으로 전체를 감싸준다.

그리고 FragmentIndexedStack위젯안에 위젯들을 쌓는 형식으로 표현한다.

@override
void initState() {
  super.initState();
  
  container.add(
    SomethingOneWidget()
  );
}

 

 

container리스트 안에 1개이하로 있을때는 PopScope가 정상적으로 동작하고,

2개 이상이 있다는건 Fragment처럼 사용된 위젯들이 IndexedStack안에 쌓여있기 떄문에, 직접 뒤로가기 설정을 해준다.

@override
Widget build(BuildContext context) {
  return PopScope(
    canPop: container.length <= 1,
    onPopInvokedWithResult: (didPop, result) {
      if (!didPop) {
        onBackPressed();
      }
    }
    
    child: ...
  );
}

void onBackPressed() {
  if (container.length > 1) {
    setState(() {
      container.removeLast();
    });
  }
}

 

 

PopScope의 child파라메터 설정

  PopScope(
    ...
    
    child: IndexedStack(
      index: container.length - 1,
      children: container.map((page) {
        return Navigator(
          onGenerateRoute: (settings) {
            return MaterialPageRoute(builder: (context) => page);
          }
        );
      }).toList(),
    )
  );

 

 

 

FirebaseCore패키지를 추가해준다.

flutter pub add firebase_core

 

'IPHONEOS_DEPLOYMENT_TARGET' is set to 11.0, but the range of supported deployment target versions is 12.0 to 17.5.99. 

iOS앱을 빌드하는데 계속해서 발생하였다.

일단 XCode에서 뭔가 target을 맞춰주어야 할 것 같다.

 

현재 iOS 12.0에 맞춰져 있다.

로그에서는 현재 11.0으로 맞춰져있다고 하는데 무엇이 문제일까.

 

XCode좌측창 네비게이션 맨우측 메뉴를 클릭하면 자세한 내용을 볼 수 있다.

 

그리고 오류가 알려주는 곳 위치를 들어가니 iOS Deployment Target이 iOS 11.0으로 맞춰져있다?

 

Pods의 ResourceTarget에 iOS 11.0으로 되어있다. 12.0으로 맞춰보자.

 

이제부터 무언가 빠졌는지를 제공해주는 에러로그가 표출된다.

PhaseScriptExecution FlutterFire: 라는 로그가 표출된다.

 

flutterfire: command not found: 이것은 파이어베이스 연결시 flutterfire라는 명령어를 실행할 수 없음을 나타내는 것 같다.

파이어베이스 연결부분을 다시 확인해보자

링크 : https://firebase.google.com/docs/flutter/setup?hl=ko&platform=web

 

터미널 창에 다음과 같이 명령한다.

curl -sL https://firebase.tools | bash

 

현재 내 맥에는 firebasetools가 다운로드 되어 있기 때문에 다음 명령어를 권유 받았다.

curl -sL firebase.tools | upgrade=true bash

 

 

FlutterFire설치명령어

dart pub global activate flutterfire_cli

 

FlutterFire환경변수를 설정해주어야 한다.

 

나는 현재 환경변수를 .zshenv파일로 관리하고 있다.

다음 명령을 추가하여 환경변수에 추가해준다.

sudo echo PATH=$PATH:$HOME/.pub-cache/bin > ~/.zshenv

 

그리고 다시 FlutterFire명령어를 수행한다.

 

정상적으로 잘설치되었고,

Xcode로 돌아와서 CleanBuildFolder를 한다.

 

 

다시 빌드 시도.

 

성공..!!

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

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

 

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

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

 

물론 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 .

 

 

라이브러리 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함수파라메터에서 설정해준다.

 

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

 

감사합니다🍖

안드로이드에서는 당연하게 릴리즈(배포)버전을 등록할 때 프로가드 규칙을 적용한다.

 

프로가드(proguard)란?

난독화를 시켜주는 것이다.

앱이 배포되고 나서 중요한 정보가 오가는 앱 내부 정보가 유출되면 안되기 때문이다.

 

 

유지보수중인 앱에서 계속해서 ClientError가 발생하였었다.

에러메시지에는 토큰을 찾을 수 없다 하였지만, 디버그 모드에서 확인시 토큰을 정상적으로 잘전달되었다

.

 

도대체 무엇이 문제인가 계속해서 보던 와중,

디버그 모드에서는 정상적으로, 릴리즈 모드에서는 안되는 것을 확인하였다.

처음엔 TargetSDK가 33, 34일 때의 문제인 줄 알았으나, 아니였다.

 

다시 한 번 카카오 문서를 들여다 보았고, 프로가드 규칙 예외처리가 있었다...

링크 : https://developers.kakao.com/docs/latest/ko/android/getting-started#project-pro-guard

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

# kakao
-keep class com.kakao.sdk.**.model.* { <fields>; }
-keep class * extends com.google.gson.TypeAdapter

# https://github.com/square/okhttp/pull/6792
-dontwarn org.bouncycastle.jsse.**
-dontwarn org.conscrypt.*
-dontwarn org.openjsse.**

 

 

앱이 오래되었고, 난독화가 안되어 있길래, 난독화를 적용해서 에러로그만 보이도록 예외처리 하였으나,

카카오 로그인이 암호화에 영향이 있는지는 이번 기회에 알게 되었다.

 

내가 적용하여 발생한 버그이지만..

개인적으로 억울하면서도 여러가지 감정이 느껴진다*: .。. o(≧▽≦)o .。.:*

 

 

추가로 네이버는 개발자센터에서 제공하는 것이 아닌 네이버 깃에 공유되어 있다.

링크 : https://github.com/naver/naveridlogin-sdk-android/issues/34

# naver login SDK 5.1.1
-keep public class com.nhn.android.naverlogin.** {
public protected *;
}
-keep public class com.navercorp.nid.** {
public *;
}

 

 

 

감사합니다🍺

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

플러터에서는 제가 알기로 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는 이미지가 정상적으로 로드되지 못한 경우 실행됩니다.

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

 

 

감사합니다🍊

+ Recent posts