반응형
defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.test.abcd"
    minSdkVersion flutter.minSdkVersion
    targetSdkVersion flutter.targetSdkVersion
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

 

위 코드에서 보다시피 각 속성들이 참조로만 지정되어 있다.

Flutter 2.8 전에는 직접 입력되어져 있었으나 2.8 이후부터는 위처럼 참조 형태로 바뀌게 되었다.

 

기본 값은 무엇으로 되어있는지 확인하기 위해 검색을 해보았다.

위와 같이 검색해보았으나, 참조되어있는 부분 빼고는 원 출처가 어디인지 나오지 않았다.

그 이유는 프로젝트 내에 해당 정보가 있지 않고 플러터 SDK 경로안의 gradle 파일을 참조하기 때문이다.

 

해당 파일의 위치는 아래와 같다.

플러터 경로가 C:\flutter라고 가정할 경우 C:\flutter\packages\flutter_tools\gradle\flutter.gradle 파일안에 해당 정보가 있다.

 

파일을 열어보면 위와 같이 선언되어 있는 것을 확인할 수 있다.

굳이 왜 저렇게 했을까 싶다. 플러터 버전을 업그레이드하면서 자기 마음대로 해당 Sdk버전을 변경해버릴 수 있기 때문에, 나의 경우는 위 선언 내용을 그대로 사용하지 않는다.

 

applicationId "com.test.abcd"
minSdkVersion 16
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

위와 같이 숫자를 직접 대입해서 쓰는 것도 간편하고 나쁘지 않은 방법이라고 본다. (기존의 방법)

하지만 나는 현재 아래처럼 사용하고 있다.

def flutterCompileSdkVersion = localProperties.getProperty('flutter.flutterCompileSdkVersion')
if (flutterCompileSdkVersion == null) {
    flutterCompileSdkVersion = '31'
}

def flutterMinSdkVersion = localProperties.getProperty('flutter.flutterMinSdkVersion')
if (flutterMinSdkVersion == null) {
    flutterMinSdkVersion = '16'
}

def flutterTargetSdkVersion = localProperties.getProperty('flutter.flutterTargetSdkVersion')
if (flutterTargetSdkVersion == null) {
    flutterTargetSdkVersion = '31'
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion flutterCompileSdkVersion.toInteger()

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.test.abcd"
        minSdkVersion flutterMinSdkVersion.toInteger()
        targetSdkVersion flutterTargetSdkVersion.toInteger()
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutterVersionCode, flutterVersionName은 local.properties를 참조하는 형태로 되어있었는데,

compileSdkVersion, minSdkVersion, targetSdkVersion 또한 그렇게 참조하는 형태로 바꿔버리는 것이다.

sdk.dir=C:\\Users\\Master\\AppData\\Local\\Android\\sdk
flutter.sdk=C:\\flutter
flutter.buildMode=debug
flutter.versionName=1.0.0
flutter.versionCode=1
flutter.flutterCompileSdkVersion=31
flutter.flutterMinSdkVersion=16
flutter.flutterTargetSdkVersion=31

그리고 local.properties 파일을 위와 같이 수정해주면 좀 더 깔끔한 형태로 sdk 버전을 관리할 수 있게 된다.

 

반응형
반응형

 

플러터로 앱 개발하던 도중 잘만 실행 되다가 어느 순간 갑자기 아래와 같은 오류를 발견하였다.

 

위와 같이 devtools-2.9.2\build 폴더를 찾을 수 없다는 오류였으며,

저렇게 오류가 발생하여도 실행하는데는 문제가 없었지만 무언가 찝찝했다.

 

실제로 저 경로에 들어가보아도 devtools-2.9.2 폴더 안에 build 폴더는 존재하지 않았다.

무언가의 영향을 받아서 build를 생성하지 못한 것 같다.

 

먼저 위 경로 중 pub.dartlang.org 폴더까지 들어가보면 아래와 같이 많은 버전의 devtools가 깔려있다.

(devtools-2.9.2 폴더는 어짜피 안되니까 삭제해버렸다.)

 

해결방법은 단순하게 사용할 버전을 바꾸어주면 된다.

 

dart pub global activate devtools -v 2.8.0

 

위 명령어를 실행하게 되면 기존 활성화되어 있던 devtools-2.9.2 버전에서 devtools-2.8.0 버전으로 활성화 버전이 변경된다.

2.8.0 버전 안에는 정상적으로 build폴더도 존재하고 문제가 없으므로 위의 문제는 간단히 해결된다.

이렇게 하지 않고 devtools-2.9.2 버전을 재설치하는 방법도 있겠으나,

일단 급하게 해결이 우선이었고 버전을 바꾼다하여도 문제될 것은 없었기 때문에 위와 같은 방법으로 해결하였다.

반응형
반응형

 

플러터가 2.0이 발표되면서 프로젝트 관리에 이슈가 생겼다.

안정적인 프로젝트 운영을 위해 2.0으로 업그레이드하는 것이 마땅하나,

이미 1.0으로 개발을 완료한 프로젝트를 2.0으로 업그레이드하기란 그렇게 간단하지 않기 때문이다.

게다가 플러터 1.0으로 개발된 프로젝트가 많은 상황에 플러터 버전을 2.0으로 업그레이드하는 순간

모든 프로젝트들을 플러터 2.0에 맞게 마이그레이션해주어야 하는데 보통 일이 아니다.

 

나의 경우도 계속 2.0으로 업그레이드하는 것을 미루다가 플러터의 특정한 패키지 버그 해결을 위해

플러터 2.0 업그레이드가 불가피해져서 업그레이드하기로 결정했는데 다 바꾸기엔 무리가 있으니,

해당 프로젝트 하나만 플러터 2.0으로 마이그레이션 하기로 결정하였다.

이 때 사용하는 것이 FVM(Flutter Version Management)이라는 것이다.

이 FVM을 사용하게 되면 프로젝트별로 원하는 버전을 쉽게 변경해서 사용할 수 있다.

A라는 프로젝트는 플러터 1.0, B라는 프로젝트는 플러터 2.0 이런 식으로 선택적으로 사용할 수 있게 된다.

또 B라는 프로젝트가 플러터 2.0으로 사용하고 있더라도 fvm으로 1.0으로 변경해서 사용할 수도 있다.

 

이제 FVM 사용하는 방법을 설명할 것이다.

 

1. 먼저 FVM을 활성화한다.

터미널(혹은 명령프롬프트)을 열고 아래 명령을 실행한다.

dart pub global activate fvm

(위 명령이 되지 않는다면 pub global activate fcm 이 명령으로 실행해보도록 한다.)

※ 혹시나 무언가 잘못되어서 되돌려야 하는 경우라면 pub global deactivate fvm 명령을 사용하면 된다.

 

2. 환경변수 path를 실행해준다.

윈도우10 기준으로 보통 'C:\Users\사용자이름\AppData\Local\Pub\Cache\bin' 이 경로를 설정해주면 될 것이다.

MAC일 경우는 export PATH="$PATH":"/Users/사용자이름/.pub-cache/bin" 과 같은 형태로 지정해주면 된다.

(사용자이름은 본인의 PC에 설정된 사용자이름으로 치환해서 사용한다.)

그리고 MAC에서는 터미널 실행시마다 적용될 수 있게 ~/.bashprofile 파일 내에도 위 패스에 대한 것을 적용해주도록 한다.

(zsh을 사용한다면 ~/.zshrc를 수정해야 하며 없으면 생성하여 내용을 추가하면 된다.)

 

※ 혹시나 Dart 경로(나의 경우는 C:\flutter\bin\cache\dart-sdk\bin)도 환경변수에 지정되어 있지 않다면 지정해준다.

   Dart 경로를 추가하는 이유는 fvm 활성화 후 실행할 fvm 명령이 내부적으로 dart의 pub 명령어를 실행하기 때문이다.

 

경로 지정을 해주지 않을 경우 아래와 같은 오류가 발생할 수 있으므로 참고하도록 한다.

-------------------------------------------------------------------
Installed executable fvm.
Warning: Pub installs executables into C:\Users\Master\AppData\Local\Pub\Cache\bin, which is not on your path.
You can fix that by adding that directory to your system's "Path" environment variable.
A web search for "configure windows path" will show you how.
Activated fvm 1.3.7.
-------------------------------------------------------------------

 

3. fvm help 명령을 실행해서 설치가 정상적으로 되었는지 확인한다.

이런 형태로 명령어 목록이 뜬다면 정상적으로 설치된 것이다.

 

4. 사용할 플러터 버전을 설치한다.

fvm을 사용할 경우 fvm 내부에서 플러터 버전을 별도로 관리하므로,

기존에 쓰던 플러터 버전은 아무 의미가 없다.

나의 경우 기존 플러터 버전을 1.22.5를 사용했었고, 이 버전과 최신버전 두가지 버전으로 관리할 것이다.

아래 두 명령을 실행하여 플러터 두가지 버전을 모두 설치해준다.

fvm install 1.22.5

fvm install stable (이 명령은 최신의 안정적인 플러터 버전을 설치해준다.)

 

5. 설치된 버전을 확인해본다.

fvm list 명령을 사용하여 아래와 같이 설치된 버전 목록을 확인할 수 있으며,

해당 플러터 버전들이 어느 경로에서 관리되고 있는지도 확인할 수 있다.

 

6. 내가 원하는 프로젝트에 원하는 플러터 버전을 적용한다.

터미널에서 플러터 버전을 적용할 프로젝트 경로로 이동한다.

그리고 fvm use라는 명령어를 사용하여 원하는 버전으로 변경할 수 있다.

최신버전의 플러터 버전을 사용하기 위해 아래 명령을 실행한다.

fvm use stable (다른 버전을 사용하려면 fvm use 다른버전을 입력하면 된다.)

위와 같이 표시된다면 정상적으로 버전이 변경된 것이다.

 

※ 오류 대응 방법

(1) Could not install stable 에러 발생

만약 위와 같은 에러가 뜬다면, 명령프롬프트에 관리자 권한이 필요한 것일 수 있다.

윈도우10이라면 PowerShell을 관리자 권한으로 실행해서 사용하면 정상적으로 동작할 것이다.

 

(2) FVM 업그레이드를 요하는 문구

___________________________________________________
FVM Update Available 1.3.7 → 2.2.2
Changelog: https://pub.dev/packages/fvm/changelog
Run pub global activate fvm to update
___________________________________________________

 

위와 같이 뜨는 경우는 flutter upgrade를 통해 기존에 사용하던 플러터를 최신버전으로 업그레이드하면 된다.

그리고 다시 fvm을 활성화하는 명령을 실행해주면 정상적으로 동작하게 된다.

 

7. 해당 프로젝트에 원하는 버전이 정상적으로 적용되었는지 확인한다.

fvm flutter --version 명령을 실행한다.

위와 같이 정상적으로 stable 버전이 선택된 것을 확인할 수 있다.

flutter --version 명령을 실행할 경우 fvm 외부의 플러터 버전이 보여지므로 착오하지 않도록 한다.

 

8. 안드로이드 스튜디오에서 fvm에서 설정한 버전을 사용하도록 변경한다.

아무리 fvm use로 버전 변경을 하더라도 해당 프로젝트에서 fvm 플러터 버전을 참조하도록 경로 수정을 하지 않는다면 의미가 없다.

안드로이드 스튜디오를 실행하고 File -> Setting -> Languages & Frameworks -> Flutter -> Flutter SDK Path를 프로젝트 루트에 생성된 .fvm/flutter_sdk 의 경로를 지정해주면 된다.

위와 같이 적용하고 OK 버튼을 눌러주면 모든 설정이 끝난다.

이제 해당 프로젝트를 빌드하거나 디버깅을 하게 되면 적용된 플러터 버전으로 실행할 수 있을 것이다.

다만, 적용한 플러터 버전에 맞게 프로젝트 내의 소스 코드나 패키지도 마이그레이션해야 정상적으로 실행될 것이다.

그리고 터미널에서 빌드시에 flutter build apk, flutter build ios 등의 명령들을 사용하게 되는데, 

이 때에도 fvm flutter build apk, fvm flutter build ios와 같이 적용 중인 플러터 버전으로 빌드할 수 있는 명령어로 사용해야한다.

반응형
반응형

 

보통 페이지 이동시 Navigator의 pushNamed 혹은 push를 이용한다. 

반대로 페이지를 종료할 경우 pop 함수를 이용한다.

그런데 이러한 기능을 사용하다가 처음 페이지 혹은 앞의 특정 페이지까지 바로 이동해야 할 경우가 발생할 수 있다.

물론 pop() 함수를 여러번 사용하여 해당 문제를 해결할 수도 있지만,

push된 페이지 수가 많을 경우 그만큼 pop을 호출해야 하므로 매우 비효율적이고,

특정 액션에 따라 페이지를 진입하여 경우에 따라 pop()해야 되는 개수가 달라지는 경우는 pop()함수로 해결이 불가능하다.

이 때, popUntil() 함수를 사용하게 되면 특정 페이지까지 pop()을 호출한 것 같은 효과를 낼 수 있다.

 

Navigator.popUntil(context, ModalRoute.withName("/"));

 

위 코드는 첫 홈화면까지 pop()을 진행하는 코드이다.

다만 주의해야 할 것이 있다.

popUntil의 두번째 인수로 사용되는 것이 어떤 것을 의미하는지 알아야 한다.

위 코드에서는 "/"으로 쓰였는데 루트로 이동하는 코드이지만 이것 또한 주의할 것이 있다.

 

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Demo',
      debugShowCheckedModeBanner: false,
      onGenerateRoute: _routes(),
      home: MainScreen(),
    );
  }
}

 

위 코드는 가장 메인에 해당하는 코드인데, MaterialApp의 home 속성이 MainScreen()로 지정되어있다.

즉, 이 상황에서 여러 push가 이루어지고, Navigator.popUntil(context, ModalRoute.withName("/")); 코드가 실행되면, MainScreen() 화면까지 pop()이 이루어지게 된다.

 

그 다음 또 간과할 수 있는 문제가 있다.

홈화면이 스플래시 화면이거나 로그인 화면 등의 경우 해당 화면 액션 처리 후 pushReplacement() 를 통해 Back으로 돌아와도 해당 화면으로 돌아가지 않게 조치하는 경우가 있다.

 

Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => SecondScreen());

위의 코드를 사용하여 첫 홈화면 MainScreen()에서 SecondScreen()로 바뀌었다고 가정하자.

이 때 여러 push로 다른 화면들을 띄워놓고 Navigator.popUntil(context, ModalRoute.withName("/")); 코드를 실행하면 어떻게 될까?

첫 페이지는 SecondScreen()로 바뀌었으니 해당 화면까지 pop()이 되어야 할 것 같지만 SecondScreen()에 대한 라우팅 정보가 없으므로 제대로 홈까지 찾아가지 못하고 모두 pop() 처리를 해버릴 것이다.

 

Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => SecondScreen(), settings: RouteSettings(name: "/")));

 

위와 같이 settings 속성으로 SecondScreen() 화면이 "/" 라우팅을 가진 스크린이라는 것을 알려준다.

그렇다면 이 때부터는 popUntil로 "/"를 지정하더라도 SecondScreen()까지만 pop()을 정상적으로 처리할 것이다.

 

정리하면, popUntil을 정상적으로 사용하기 위해 어떤 페이지든 이동할 때 Routesettings 속성을 반드시 지정하도록 하자.

반응형
반응형

매우 자주 사용하는 위젯 중 하나인데, 사용하면서 한가지 문제를 발견했다.

ListView에 담긴 내용에는 텍스트, 여러 사진들, 구글 지도 등이 포함되어 있었다.

그런데 문제는 예를 들어, 사진이 보이지 않는 곳까지 스크롤을 했다가 다시 돌아오면 사진이 다시 로딩되는 경우가 발생한다.

지도의 경우도 지도가 안보이는 영역까지 스크롤했다가 빠르게 돌아오면 지도가 다시 로딩되는 경우가 발생한다.

일단 이것의 원인은 리스트뷰 위젯이 메모리 관리를 위해 보이는 부분과 일부 주변 영역에 대해서만 불러오고 표시하기 때문이다.(캐싱을 하지 않는 것이다.)

그래서 느리게 천천히 스크롤하면 해당 문제가 없는 것처럼 보인다.

이 문제를 해결하기 위해 스크롤 하여 안보이는 곳으로 이동한다해도 그 공간의 내용들을 캐싱해놓는 것이다.

다행히도 ListView에 cacheExtent라는 속성이 존재한다. 이것은 보이는 영역으로부터 얼마만큼의 주변 영역까지 캐싱해놓을지 지정하는 속성이다.

 


픽셀 값이므로 자신이 필요한 만큼 지정해서 사용하면 된다. 무조건 크게 해놓으면 해당 문제는 발생하지 않겠지만 메모리 관련하여 문제가 발생할 수 있으므로 상황에 맞게 적용하는 것이 좋을 것 같다.

 

반응형
반응형

 

IOS 앱 배포를 위해 XCODE를 통해 업로드 하던 중 다음과 같은 오류가 발생했다.

 

Error ITMS-90717: "Invalid App Store Icon. The App Store Icon in the asset catalog in 'YourApp.app' can't be transparent nor contain an alpha channel."

 

앱 아이콘에 알파 채널이 존재하니 알파 채널을 없애라는 의미이다.

 

먼저 XCode의 프로젝트 폴더에서 Runner를 선택한다.

그럼 변경된 화면에서, TARGETS -> Runner -> General 탭 -> App Icons Source 우측의 화살표 모양을 클릭한다.

 

그럼 앱에 사용되는 아이콘 목록이 뜨는데 그 아이콘 목록 중에 1024x1024에 해당하는 이미지 파일을 파인더로 연다.

그 후 내보내기를 알파를 제거하고 진행할 수 있다.

 

 

위와 같이 알파 해제 후 저장을 하여 저장된 파일을 기존 1024x1024 파일에 덮어씌우고,

다시 앱 배포를 진행하면 정상적으로 업로드가 된다.

반응형
반응형

 

플러터로 개발하면서 안드로이드쪽은 그리 문제된 적이 없는데 IOS쪽은 종종 문제가 생기곤 했다.

IOS 빌드 에러가 한두가지는 아니지만 그 중 속 썩였던 에러가 있었다.

 

하나는

Xcode's output:

ld: framework not found Flutter clang: error: linker command failed with exit code 1 (use -v to see invocation) note: Using new build systemnote: Planning buildnote: Constructing build description

 

하나는

Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_FlutterMethodChannel", referenced from: objc-class-ref in FLTPackageInfoPlugin.o "_FlutterMethodNotImplemented", referenced from: -[FLTPackageInfoPlugin handleMethodCall:result:] in FLTPackageInfoPlugin.o

 

물론 위 에러가 해결 방법은 상황에 따라 다를 수 있다.

대부분 자료를 찾아보면 아래처럼 클린 후 빌드하는 것으로 해결을 한다.

flutter clean

flutter run

그런데 나는 이것으로 해결되지 않았고

 

flutter pub cache repair라던지,

Runner.xcworkspace를 실행하여 XCode에서 빌드하는 방법 등 거의 3일간을 삽질한 것 같다.

물론 위의 방법으로 해결되는 것이 가장 좋다.

 

최후의 방법은 플러터 프로젝트에 있는 Build 폴더를 삭제하고, ios 폴더 자체도 삭제를 한다.

(단, ios폴더에 미리 세팅해놓은 코드나 Info.plist, PodFile 파일 등은 백업해놓도록 한다.)

 

모두 삭제를 했다면 아래 명령을 실행한다.

flutter create . 

이는 현재 상태에서 누락되어있는 파일들을 새로 만들어준다. 즉, ios폴더가 새로 만들어진다.

그럼 이전에 세팅했던 Info.plist , PodFile등을 상황에 맞게 세팅한 후 다시 빌드를 해주면 된다.

생각보다 매우 간단하지만, ios 빌드에서 애를 먹고 있다면 굳이 다른 것을 찾을 필요없이

깔끔하게 다시 시작하는게 최고인 것 같다.

 

무언가 빌드하면서 꼬이기 시작하면서 알 수 없는 에러들을 뱉어내는데 이 때는 flutter clean만으로도 

완전히 클린이 되지 않으므로 위와 같은 방법을 써야만 할 것이다.

 

반응형
반응형

 

플러터에서는 PODO(Plain Old Dart Object)라고 부르는 다트 객체를 모델을 관리하기 위해 사용한다.

흔히 자바에서 DTO라고 부르는 객체와 동일한 역할을 하는 녀석이라고 보면 된다.

 

어쨌든 백엔드 서버와의 통신을 이러한 객체 단위로 주로 하게 될테고, 이 객체의 내용은 보통 DB 스키마에 맞추어 사용하게 된다. 그런데 몽고DB의 경우 _id라는 기본적인 필드가 존재한다. (물론 안쓸 수도 있지만 RDB의 Primary Key를 가진 필드와 비슷한 역할을 하는 중요한 필드이므로 안쓰는 경우는 드물다.)

 

여기서 문제는 하필 필드 이름이 _id 라는 것이다. id 라면 좋았을 것을...

다음과 같이 몽고에서 임시로 컬렉션을 만들고 도큐먼트를 생성해보았다.

 

 

조회해보면 _id 필드가 자동으로 생성된다. 이 필드는 인덱스가 기본적으로 붙어있어서 검색시에 빠른 성능을 제공한다.

이제 플러터에서 이에 대한 다트 객체를 만들어보겠다.

 

일반적으로 만드는 방식대로 다트 객체를 만들어보았다.

빨간줄이 보인다. 매우 거슬린다.

 

선택적 매개변수의 첫 글자에 _(언더바)가 들어갈 수 없다는 내용이다.

다트에서 지원하지 않으니 몽고DB의 모든 _id를 사용하지 않고 다른 것으로 바꾸고 인덱스를 씌워야 하나?

다행히 해결 방법이 존재한다.

JsonKey라는 어노테이션(Annotation)을 쓰면 된다.

(어노테이션이라는 것은 @기호를 앞에 사용하는 메타데이터라고 보면 된다.)

 

바로 적용해보도록 하겠다.

두 줄만 추가하면 된다.

json_annotation 를 import 시켜주고 JsonKey annotation을 적용해주기만 하면 된다.

 

@JsonKey(name: '_id')

이 코드가 핵심인데 실제 키 이름은 _id를 사용한다는 의미이다.

그리고나서 밑에서는 String id 로 선언하여 _id를 사용하지 않고도 _id를 사용하는 것처럼 쓸 수 있다.

 

보통 서버와 클라이언트의 주고 받는 필드는 일치시켜주는 것이 혼동을 줄이는 일인데,

특히 몽고DB의 경우는 거의 필수적으로 사용될 내용인 것 같다.

_id만 해당되는 것이 아니라 lowerCamelCase를 써야 하는 경우면 다 해당된다고 보면 된다.

 

그리고 코드 상단에 json_annotation을 import하였는데, 패키지를 찾을 수 없는 경우 json_annotation 패키지를 설치하면 된다. (https://pub.dev/packages/json_annotation)

나는 별도로 설치하지는 않았는데 Dart 버전에 따라 내장되어있는 것 같기도 하다.

반응형
반응형

 

SVG 이미지는 픽셀 단위로 이루어진 비트맵 방식의 PNG와 다르게 벡터 이미지 방식을 사용하고 있다.

장점은 크기를 자유롭게 조정하여도 이미지 깨짐이 발생하지 않는다는 것이다.

 

휴대폰은 기종별로 다양한 해상도를 갖고 있으며, 개발자들은 이에 대응할 수 있어야 한다.

PNG 파일 하나를 이용해서 해상도별로 크기를 조정해가면서 사용할 경우 이미지 깨짐이 발생할 수 있고,

그렇다고 PNG 파일을 해상도별로 나누어 사용하기에는 소모 비용이 너무 크다.

 

이 때 사용할 수 있는 것이 바로 SVG 이미지이다.

플러터에서는 SVG를 쉽게 사용할 수 있도록 제공하는 플러그인이 존재한다.

 

flutter_svg 라는 플러그인인데, 아래의 순서대로 따라해보도록 하자.

 

위와 같이 pubspec.yaml 파일을 열고 flutter_svg: 0.17.4 를 추가해준다.

 

실제로 사용할 이미지를 추가해준다.

경로는 원하는 곳에 넣어도 상관없다.

 

위에서 추가한 파일을 pubspec.yaml에도 적어준다.

flutter pub get 명령으로 해당 내용을 반영해준다.

 

이제 flutter_svg 플러그인을 사용하기 위해 위와 같이 import 해준다.

 

위와 같이 SvgPicture 객체를 사용하여 SVG 이미지를 뿌려줄 수 있다.

width, height 속성을 이용하여 크기를 자유롭게 변경할 수 있으며,

SVG 벡터 이미지이므로 이미지 깨짐이 없다.

 

아주 깔끔하게 출력되었다.

 

참고로 SvgPicture.network() 함수를 통해 외부 네트워크에 있는 이미지를 표시할 수도 있다.

 

앱 내에서 아이콘이 차지하는 비중이 상당히 높기 때문에 SVG 파일을 이용하면,

이미지 용량도 절약할 수 있고 사이즈에 구애 받지 않기 때문에 더 효율적인 작업이 가능하다.

 

단, 아이콘이 아닌 복잡한 형태의 이미지는 PNG를 사용하는 것이 낫다.

단순하지 않은 이미지는 오히려 PNG 이미지보다 용량이 더 커질 수 있고, 속도에도 영향을 미칠 수 있기 때문이다.

반응형
반응형

 

firebase_admob: ^0.9.0+9

image_picker : ^0.6.2+1

 

위 두개의 패키지를 사용하고, 빌드하는데 다음과 같은 오류가 발생하였다.

 

WARNING: Conflict with dependency 'androidx.core:core' in project ':app'. Resolved versions for runtime
classpath (1.0.2) and compile classpath (1.0.0) differ. This can lead to runtime crashes. To resolve this issue follow
advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties. Alternatively, you
can try to fix the problem by adding this snippet to
C:\temp\android\app\build.gradle:
[ +4 ms] dependencies {
[ +3 ms] implementation("androidx.core:core:1.0.2")
[ +1 ms] }
[ +2 ms] WARNING: Conflict with dependency 'androidx.annotation:annotation' in project ':app'. Resolved versions for
runtime classpath (1.0.2) and compile classpath (1.0.0) differ. This can lead to runtime crashes. To resolve this issue
follow advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties.
Alternatively, you can try to fix the problem by adding this snippet to
C:\temp\android\app\build.gradle:
[ +1 ms] dependencies {
[ +2 ms] implementation("androidx.annotation:annotation:1.0.2")


[ +5 ms] *******************************************************************************************
[ +3 ms] The Gradle failure may have been because of AndroidX incompatibilities in this Flutter app.
[ +2 ms] See https://goo.gl/CP92wY for more information on the problem and how to fix it.
[ +1 ms] *******************************************************************************************
[ +5 ms] "flutter appbundle" took 40,539ms.
Gradle task bundleRelease failed with exit code 1

#0 throwToolExit (package:flutter_tools/src/base/common.dart:28:3)
#1 _buildGradleProjectV2 (package:flutter_tools/src/android/gradle.dart:751:5)
#2 _asyncThenWrapperHelper. (dart:async-patch/async_patch.dart:71:64)
#3 _rootRunUnary (dart:async/zone.dart:1132:38)
#4 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#5 _FutureListener.handleValue (dart:async/future_impl.dart:137:18)
#6 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:678:45)
#7 Future._propagateToListeners (dart:async/future_impl.dart:707:32)
#8 Future._completeWithValue (dart:async/future_impl.dart:522:5)
#9 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:30:15)
#10 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:288:13)
#11 runCommandAndStreamOutput (package:flutter_tools/src/base/process.dart)
#12 _asyncThenWrapperHelper. (dart:async-patch/async_patch.dart:71:64)
#13 _rootRunUnary (dart:async/zone.dart:1132:38)
#14 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#15 _FutureListener.handleValue (dart:async/future_impl.dart:137:18)
#16 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:678:45)
#17 Future._propagateToListeners (dart:async/future_impl.dart:707:32)
#18 Future._completeWithValue (dart:async/future_impl.dart:522:5)
#19 Future._asyncComplete. (dart:async/future_impl.dart:552:7)
#20 _rootRun (dart:async/zone.dart:1124:13)
#21 _CustomZone.run (dart:async/zone.dart:1021:19)
#22 _CustomZone.runGuarded (dart:async/zone.dart:923:7)
#23 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:963:23)
#24 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#25 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
#26 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:116:13)
#27 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:173:5)

 

위 문제를 해결하기 위해서 프로젝트 폴더의 android/build.gradle 파일을 열고 

아래 추가 부분에 해당하는 코드를 추가해주고 다시 빌드해본다.

 

buildscript {
  ext.kotlin_version = '1.3.0'
  repositories {
    google()
    jcenter()
  }

  dependencies {
    classpath 'com.android.tools.build:gradle:3.3.2'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }

  //--------------추가 부분-----------
  subprojects {
    project.configurations.all {
      resolutionStrategy.eachDependency { details ->
        if (details.requested.group == 'com.android.support'
            && !details.requested.name.contains('multidex') ) {
          details.useVersion "27.1.1"
        }

        if (details.requested.group == 'androidx.core'
            && !details.requested.name.contains('androidx') ) {
          details.useVersion "1.0.1"
        }
      }
    }
  }
  //----------------여기까지------------
}

반응형

+ Recent posts