본문 바로가기

개발 코딩 정보 공유/플루터 Flutter

안드로이드 네이티브앱에 flutter 소스 사용하기

native 소스에 flutter 를 적용해보자

 

 

소개

안녕하세요. 김과자 입니다.

모든 앱을 flutter 로 만들수 없습니다.

기존의 앱을 전부 뜯어 고치는것은 결코 좋은 솔루션이 아닙니다.

아래의 방법을 통해 기존의 앱에 플루터 앱을 추가해보세요.

 

시작

이 방법은 기존의 네이티브 소스에 flutter 공통화 화면이나 코드를 추가하는 방법입니다.

  • 안드로이드 네이티브소스
  • IOS 네이티브 소스

-> 플루터 화면 / 소스 추가

 

Module 작업

기존의 안드로이드 스튜디오 에서 작업하셔도 되지만 저는 cmd 로 생성해주겠습니다.

만들고자 하는 폴더로 이동후

 

> flutter create -t module --org com.example flutter_module_test

 

생성된 flutter module 을 오픈하시고 gradle 설정파일에서 자바 버전을 11 이상으로 맞추어 줍니다.

android {
  //...
  compileOptions {
    sourceCompatibility 11
    targetCompatibility 11
  }
}

그리고 lib/dart 파일을 열어줍니다. 아래의 소스를 추가합니다.

단순한 테스트를 위한 코드 입니다.

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  var _methodCallArguments = "null"; //---> 추가

  @override
  void initState() {
    super.initState();
    methodChannel.setMethodCallHandler(methodHandler); //---> 추가
  }

  Future<dynamic> methodHandler(MethodCall methodCall) async { //--> 추가

    print('methodHandler: ${methodCall.method}');
    if (methodCall.method == "aaa"){
      print('methodHandler: ${methodCall.arguments}');
      setState(() {
        _methodCallArguments = methodCall.arguments;
      });
      return "received flutter";
    }
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              _methodCallArguments,//--> 추가
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter, 
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), 
    );
  }
}

native -> flutter 상태 값을 받기 위해 변수를 생성해줍니다.

var _methodCallArguments = "null";

채널로 들어오는 값을 처리할 handler를 지정해 줍니다.

methodChannel.setMethodCallHandler(methodHandler);

저는 aaa 라는 메서드 네임으로 콜할 예정입니다.

if (methodCall.method == "aaa"){

화면에서 어떤값을 받았는지 확인합니다.

Text(
    _methodCallArguments,//--> 추가
    style: Theme.of(context).textTheme.headlineMedium,
  ),

 

테스트를 위한 flutter 셋팅은 이정도면 된것 같습니다.

안드로이드에서 사용하려면 .arr 파일로 만들어야 합니다. 

cmd 로 하셔도 좋고 android studio 로 flutter module 폴더를 open 한 후 build → aar 로 하셔도 좋습니다.

> flutter build aar

빌드 폴더를 보시면 잘 빌드가 된걸 보실수 있습니다.

 

기존의 android native 소스에서 셋팅

android studio 로 기존의 소스를 오픈합니다.

아래의 dependency 를 셋팅합니다.

android {
  // ...
}

repositories {
  maven {
    url 'some/path/my_flutter/build/host/outputs/repo'
    // This is relative to the location of the build.gradle file
    // if using a relative path.
  }
  maven {
    url '<https://storage.googleapis.com/download.flutter.io>'
  }
}

dependencies {
  // ...
  debugImplementation 'com.example.flutter_module:flutter_debug:1.0'
  profileImplementation 'com.example.flutter_module:flutter_profile:1.0'
  releaseImplementation 'com.example.flutter_module:flutter_release:1.0'
}

 

*** com.example.flutter_module 이경로는 자신의 경로로 맞춰야 합니다.

그 다음 manifest 에 activity 를 추가하겠습니다.

<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/LaunchTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />

대략 테스트할 activity 를 열고 engine, channel 명을 작성합니다. 적당한 버튼 하나 만들겠습니다.

private const val FLUTTER_ENGINE_NAME1 = "custom1"
private val channelName = "com.example.module-test/custom" //채널명 셋팅
lateinit var channel1 : MethodChannel

엔진 초기화 입력

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initFlutterEngine()
private fun initFlutterEngine() {
        val flutterEngine1 = FlutterEngine(this)

        flutterEngine1.navigationChannel.setInitialRoute("/custom1");

        flutterEngine1.dartExecutor.executeDartEntrypoint(
            DartExecutor.DartEntrypoint.createDefault()
        )

        channel1 = MethodChannel(flutterEngine1.dartExecutor.binaryMessenger, channelName)
        FlutterEngineCache
            .getInstance()
            .put(FLUTTER_ENGINE_NAME1, flutterEngine1)
     
    }

버튼 클릭시 flutter 소스로 이동

btn1.setOnClickListener {
            
            channel1.invokeMethod("aaa", "hello")

            startActivity(
                FlutterActivity
                    .withCachedEngine(FLUTTER_ENGINE_NAME1)
                    .build(this)
            )
            overridePendingTransition(0, 0)
        }

 

눈치 채셨겠지만 채널을 통해 flutter 소스와 통신합니다.

버튼으로 테스트 해보면 네이티브의 값이 flutter 화면으로 잘 전달된걸 확인할수 있습니다.

 

참고

https://docs.flutter.dev/add-to-app/android/project-setup

https://docs.flutter.dev/add-to-app/android/add-flutter-screen?tab=custom-activity-launch-kotlin-tab