본문 바로가기
프로그래밍/Flutter

[Flutter] Android 어플 Flutter로 실행시키기

by YuminK 2022. 4. 14.

최근에 회사에서 작업한 내용이 Android로 개발한 어플을 Flutter 환경에서 실행시키는 것을 해봤습니다. 

 

플랫폼 별 코드 작성

앱에서 커스텀하게 플랫폼 별 코드를 작성하는 방법을 배워보세요.

flutter-ko.dev

 

MethodChannel이라는 개념을 이용하여 Flutter로 실행시킨 어플에서 바로 Android쪽 소스의 Activity를 호출하는 형태로 작업을 했습니다. 

 

Dart의 코드로 실행시켜서 버튼을 누르면(Native Call) 프로젝트의 FlutterActivity가 call을 받아서 startActivity를 통해 실질적으로 사용할 Activity를 호출합니다.

 

이런 식으로 처리하면, Flutter를 기반으로 실행을 시켜서 Android Native코드를 실행시킬 수 있습니다. 

 

main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(WillPopScope(
      onWillPop: () async => false,
      child: const MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String CHANNEL = "package.name/channel";
  static const platform = MethodChannel(CHANNEL);

  Future<void> _startActivity() async {
    try{
      await platform.invokeMethod("startActivity");
    }
    on PlatformException catch(e) {
      print(e.message);
    }
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    _startActivity();
    return const MaterialApp();
  }
}

 

FlutterStartActivity.kt

import android.content.Intent
import android.os.Bundle
import android.util.Log
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant

class FlutterStartActivity: FlutterActivity() {
    private val CHANNEL = "package.name/channel"
    val TAG = javaClass.simpleName

    override fun onBackPressed() {}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "Flutter activity Created")
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        GeneratedPluginRegistrant.registerWith(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                if(call.method == "startActivity") {
                    val intent = Intent(this, StartActivity::class.java)
                    startActivity(intent)
                }
            }
    }
}

아래 영상을 참고하시면 좋은데, 모듈 버전이 살짝 달라서 그 부분은 참고만 해주세요.

Flutter모듈에서 기존의 안드로이드 프로젝트 옮기기.
https://youtu.be/LcM9T14UzR8

 

기존에 사용하던 프로젝트에서 설정해놓은 부분과 dart 초기 생성 파일이 맞물려 돌아가도록 하는 작업이 까다로웠다고 생각합니다. 

 

1. Flutter 프로젝트를 생성하고 Android 프로젝트를 기준으로 프로젝트 열기

(Flutter 프로젝트를 기준으로 하면 인식이 제대로 되지 않습니다.)

 

2. Android 환경에서 프로젝트 설정(Flutter에서 생성된 파일 + 기존에 있던 모듈 내용 합치는 작업)

Flutter 프로젝트를 실행한 이후에 생기는 기본 설정 파일들은 대부분 그대로 두면서 build.gradle, project의 build.gradle,

properties 속성들을 수정했습니다. 기존에 사용하던 proguard 같은 것들 그대로 옮겼고요.

 

Flutter에서는 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 식으로 되어 있는데 Android쪽 내용으로 적었습니다.

 

app의 매니페스트 쪽도 비슷하게 옮겨주시고 FlutterStartActivity에 대한 내용은 그대로 남겨주셔야 합니다.

 

빌드 이슈

1. Flutter 프로젝트에서 컴파일 버전 31 관련해서 오류 있어서 compileVersion 수정

2. Project의 gradle파일에서 쓰던 버전은 4.2.2인데 처리하다가 문제 생겨서 버전을 낮춤
classpath 'com.android.tools.build:gradle:4.0.0' 

https://stackoverflow.com/questions/37086806/android-studio-failed-to-apply-plugin-id-com-android-application

스택오버플로우에서 코틀린 버전 낮추라는 말도 많은데 이건 상관없는 것 같습니다.


3. BindingAdapter ArrayAdapter<String> 관련 오류

If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches. Open File

 

XML클래스에서 DataBinding을 다음과 같이 사용하고 있었는데 ArrayAdapter처리만 안 되서 binding연결해서 사용하지 않는 형태로 수정했습니다. (activity 내에서 셋팅)

    <data class="com.example.exampleBinding">

        <import type="android.widget.ArrayAdapter" />

        <variable
            name="activity"
            type="com.example.activity" />

        <variable
            name="adapter"
            type="ArrayAdapter" />

    </data>

4. R 경로가 제대로 import 되어 있지 않는 경우

위의 Binding오류 때문에 나오지 않은 것으로 보인다.

 

5. 모듈 이름 관련 이슈 
중간에 모듈 이름을 따로 바꿨다가 빌드에서 경로를 잘못 잡는 이슈가 있어가지고 프로젝트 다시파서 app 이름 그대로 사용했습니다.

The detected reason was:
NO ~app\src\main\AndroidManifest.xm

 

6. Flavor in Flutter project
flutter모듈에서 안드로이드 flavor를 지정하려면 EditConfiguration에서 flavor처리를 해야 한다.
https://stackoverflow.com/a/62808639/17141266

혹은 dart에서 기본적인 처리를 해주고 Android 모듈 자체에서 Build Variants 값을 조절할 수도 있다.

 

7. ndk 관련 오류

Flutter initialization failed.
java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dlopen failed: library "libflutter.so" not found
at java.util.concurrent.FutureTask.report(FutureTask.java:123)
at java.util.concurrent.FutureTask.get(FutureTask.java:193)
at io.flutter.embedding.engine.loader.FlutterLoader.ensureInitializationComplete(FlutterLoader.java:223)
at io.flutter.embedding.engine.FlutterEngine.<init>(FlutterEngine.java:327)
at io.flutter.embedding.engine.FlutterEngine.<init>(FlutterEngine.java:207)

ndk 관련하여 해당 옵션으로 실행시 시뮬레이터에서 죽음(x86 시뮬레이터)
실기기에서는 정상 동작. (디버그)
ndk {
      abiFilters "arm64-v8a", "armeabi-v7a"
  }
  
다양한 ndk를 사용하는 방식으로 시뮬레이터를 쓰면 UI코드는 동작하고 코어 처리시 죽어서 진행 불가 (VP8, failed to load libraries)  (디버그)
ndk { abiFilters "armeabi", "x86", "armeabi-v7a","x86_64", "mips",
      "mips64", "arm64-v8a" } 
  
=> 결론적으로는 기존에 사용하던 옵션에서 시뮬레이터가 정상적으로 작동해야 한다.
이전에 시뮬레이터로 해당 부분이 정상적으로 동작했기 때문에 방법이 있을 것(Android project)
ndk {
      abiFilters "arm64-v8a", "armeabi-v7a"
  }

유사한 내용인데 찾기 어렵다. 
https://github.com/flutter/flutter/issues/37396


해결한 내용은 아닌데 회사 어플 내용에서 정리해보면 ==============================
실기기(Flutter 모듈) 
Release 모드 처리가 되지 않고 Debug만 동작했다.

실기기(Android 모듈)
정상 동작

시뮬레이터(Flutter 모듈)
Debug에서 동작(ndk 전체 오픈시)만 동작, 릴리즈 빌드 오류

시뮬레이터(Android 모듈)
Debug에서 동작, 릴리즈에서 빌드오류

StackOverFlow찾아봐도 뭔가 상황이 조금 달라서 그런지 매칭 되는 것이 나오지 않는다. 
Execution failed for task ':app:lintVitalCsapRelease'.
> Could not resolve all artifacts for configuration ':app:keadeduReleaseRuntimeClasspath'.
   > Failed to transform libs.jar to match attributes {artifactType=processed-jar, org.gradle.libraryelements=jar, org.gradle.usage=java-ru
      > Execution failed for JetifyTransform: F:\CXL_V3\build\app\intermediates\flutter\keadeduRelease\libs.jar.
         > Failed to transform 'F:\CXL_V3\build\app\intermediates\flutter\keadeduRelease\libs.jar' using Jetifier. Reason: FileNotFou
           Please file a bug at http://issuetracker.google.com/issues/new?component=460323.
   

안드로이드 모듈로 처리하면 실기기에서는 크게 문제가 보이지 않아서 일단 이걸로 쓰게 될 것 같다. ㅠㅠ...

 

M1으로 빌드를 하니까 Flutter프로젝트에 대한 Android 디버깅 지원, IntelliJ 지원한다. (코드 변경 Hot Reload는 지원 X)

릴리즈 안 돌아가는 이슈는 console 명령어를 치니까 알 수 있었는데, M1에서 돌아가는 것을 확인

 

내 개발PC에서는 릴리즈 모드를 지원하지 않았다;; (x86)

실기기가 아닌 경우에도 M1에서 정상적으로 동작했다. (잘 돌아가면 같이 애플 주식 사자고 했는데 진짜로 되네)

 

안드로이드에서 이미지 소스의 이름과 flutter의 리소스 이름이 같은 경우에 문제가 생기지 않는지 테스트

=> 정상

 

'프로그래밍 > Flutter' 카테고리의 다른 글

[Flutter] BackPress to exit  (0) 2022.04.16
[Flutter] WebView on Web  (0) 2022.04.16
[Flutter] ScrollView 처리  (0) 2022.04.12
[Flutter] Dialog insetPadding  (0) 2022.04.10
[Flutter] 카드에 반투명한 이미지 만들기  (0) 2022.04.09

댓글