프로그래밍/flutter

flutter에서 android native code 와 연동

인썸니아 2021. 11. 6. 01:06

플러터만으로도 대부분의 기능들을 편리하게 사용할 수 있지만, 안드로이드 시스템 API를 사용해야 한다거나, 성능상의 문제로 native 코드를 작성해야 하는 경우가 있다. native code 와 병행해서 만들어진 flutter package 들도 아주 많다. 어떻게 flutter 에서 native 로 작성된 코드를 호출할 수 있는지 정리해 보았다.

 

Android Native 와 통신

flutter 앱과 OS platform 사이의 통신 archtecture

 

위 구조에서 확인할 수 있듯이 MethodChannel을 통해 iOS 또는 Android 플랫폼과 데이터를 주고 받을 수 있으며, MethodChannel 은 비동기적으로 동작하므로 await 를 사용한다.

 

- flutter 코드 예제

class _NativeAppState extends State<NativeApp> {
  static const platform = MethodChannel('insomnia.example.native/info', 'model');
  String _deviceInfo = 'Unknown info';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Native 통신 예제'),
      ),
      body: Center(
        child: Text(
          _deviceInfo,
          style: const TextStyle(
          	fontSize: 30,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _getDeviceInfo();
        },
        child: const Icon(Icons.get_app),
      ),
    );
  }

  Future<void> _getDeviceInfo() async {
    String deviceInfo;

    try {
      final String result = await platform.invokeMethod('getDeviceInfo');
      deviceInfo = 'Device info:\n$result';
    }
    on PlatformException catch (e) {
      deviceInfo = 'Failed to get Device info : ${e.message}.';
    }
    on MissingPluginException catch (e) {
      deviceInfo = 'No Method : ${e.message}.';
    }

    setState(() {
      _deviceInfo = deviceInfo;
    });
  }
}
  • MethodChannel(key) 형태로 사용하여 채널 객체를 얻어오고, _getDeviceInfo() 함수에서 채널 객체의 invokeMethod(methodName, argument) 를 사용하여 안드로이드 native 의 API를 호출한다.
  • 여러개의 argument 를 전달하기 위해서는 argument들을 List type 으로 묶어 보내주면 된다.
    (여러개의 결과값을 받으려면 invokeMethod 대신 invokeListMethod 를 사용할 수 있다.)
  • MethodChannel 의 parameter 로 입력하는 key 값은 임의의 String 을 입력하여도 문제없이 동작한다.
  • invokeMethod 는 비동기이므로 return 값을 받으려면 await 가 필요하다. (.then을 사용하여도 될 것이다..)

 

- kotlin 코드 예제

class MainActivity: FlutterActivity() {
    private val CHANNEL = "insomnia.example.native/info"

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
                .setMethodCallHandler { call, result ->
                    if(call.method == "getDeviceInfo") {
                        val deviceInfo = getDeviceInfo(call.arguments.toString())
                        result.success(deviceInfo)
                    }
                }
    }

    private fun getDeviceInfo(): String {
        return when(arg) {
            "device" -> Build.DEVICE
            "brand" -> Build.BRAND
            "model" -> Build.MODEL
            "product" -> Build.PRODUCT
            else -> {
                val sb = StringBuffer()
                sb.append(Build.DEVICE + "\n")
                sb.append(Build.BRAND + "\n")
                sb.append(Build.MODEL + "\n")
                sb.append(Build.PRODUCT + "\n")
                sb.toString()
            }
        }
    }
}
  • flutter 에서 invokeMethod 를 호출하면 configureFlutterEngine 함수가 호출되는데 invokeMethod 사용시 입력한 parameter 가 각각 call.method, call.arguments 로 넘어오게 된다.
  • call.method 를 통해 동작을 구현한 kotlin 코드를 실행할 수 있고, call.arguments 를 통해 flutter로부터 넘겨받은 데이터를 처리할 수 있다.
  • kotiln 코드 작업시에는 flutter project 에서 작업하지 말고 flutter project 폴더 하위의 android 폴더를 android project 로 별도로 open 해야 kotlin class 들을 편집하기가 편리하다. 그냥 flutter project 에서 kotlin 파일 작업시 intellisense 가 작동하지 않는다.

 

iOS native 와 통신

아마 안드로이드 네이티브 통신과 비슷한 방식으로 구현할 수 있지 않을까 싶다. ㅋㅋㅋ

 

관련 링크 (공식 홈피)

https://flutter.dev/docs/development/platform-integration/platform-channels

 

Writing custom platform-specific code

Learn how to write custom platform-specific code in your app.

flutter.dev

 

반응형