본문 바로가기
Programming/APP

Flutter MethodChannel 연동 (1) - Android

by 해도 Haedo 2022. 8. 22.

안녕하세요. 뉴핀입니다.

오늘은 Flutter에서 MethodChannel을 이용하여 Android Native 호출을 하는 방법에 대해 알려드리고자 합니다.

플러터는 크로스 플랫폼 프레임워크AndroidiOS가 단일 코드로 개발이 가능하다는 장점을 가지고 있습니다.

하지만 플러터를 사용하다 보면 Android와 iOS에서 네이티브 호출을 필요로 하는 경우가 종종 발생합니다.
저는 블루투스 통신과 관련된 프로젝트를 개발하며 Android 12 이상에서 BLUETOOTH_SCAN에 대한 퍼미션 문제로 MethodChannel을 사용하였습니다.

이러한 네이티브 호출을 사용하기 위해서는 Android의 경우 Java 또는 Kotlin을, iOS는Objective-C 또는 Swift를 숙지하고 있어야 합니다.

이번 시간에는 Andrid에서 Kotlin을 이용하여 Flutter에서 MethodChannel 호출하는 방법을 알려 드리겠습니다.

출처: 플러터 플랫폼 별 코드 작성

구조는 이렇습니다. 클라이언트(플러터) 단에서는 MathodChannel이 메시지를 그에 상응하는 메소드로 보낼 수 있도록 해줍니다. 호스트(네이티브) 단에서는 Android의 경우 MethodChannel을, iOS의 경우 FlutterMethodChannel들이 메시지를 받는 것과 응답을 가능하게 합니다.

쉽게 말해서 메소드 채널을 이용하여 플러터와 네이티브 통신을 하는 것을 비동기적으로 처리를 합니다.

준비물은 안드로이드 스튜디오에서 플러터를 구동할 수 있도록 세팅만 해 두시면 됩니다.
플러터 세팅에 관한 내용은 다음에 다루도록 하겠습니다.

1. MethodChannel 생성

우선 메소드 채널을 적용하기 위한 플러터 빈 프로젝트를 만듭니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(
      ),
    );
  }
}

프로젝트를 만들면 테스트 코드가 생성되는데, 위젯을 포함하여 테스트 코드를 모두 지우시고 작업하시면 메소드 채널에 대한 테스트를 하기가 더욱 편합니다.

이 상태에서 메소드 채널을 생성하여 비동기 함수로 구현을 합니다.
main.dart 하단에 있는 state 부분에서 구현

static const platform = const MethodChannel('com.example/value');

  String _value = 'null';

  Future<void> _getNativeValue() async {
    String value;

    try{
      value = await platform.invokeMethod('getValue');
    } on PlatformException catch (e) {
      value = 'error message : ${e.message}';
    }

    setState((){ _value = value; });
  }

이렇게 platform 선언을 함으로써 'com.example/value'라는 채널로 통신이 가능하게 되었습니다.

2. MethodChannel 호출

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          Text('$_value'),
          RaisedButton(
            child: Text('native value'),
            onPressed: _getNativeValue,
          ),
        ],
      ),
    );
  }

위젯 부분은 이렇게 구현하였습니다. 버튼을 클릭함으로써 Text 단에 메소드 채널로 불러온 value 값이 들어갈 예정입니다.

3. Android 세팅

프로젝트 폴더 안에 android > app > src > main > kotlin > MainActivity로 들어갑니다.

상단에 알림이 뜨는데, 안드로이드 코딩을 편리하게 사용하기 위해 Open을 눌러줍니다.

저는 안드로이드 SDK가 설정되어 있기 때문에 알림이 뜨지 않지만, 안드로이드 SDK가 설정이 되어 있지 않다면 상단에 'Project SDK is not defined'가 뜹니다. 안내에 따라 SDK 경로를 선택하여 마무리합니다.

class MainActivity: FlutterActivity() {
    companion object {
        const val CHANNEL = 'example.com/value'
    }

    override fun onCreate(savedInstanceState: Bundle?){
        super.onCreate(savedInstanceState)
        GeneratedPluginRegistrant.registerWith...

        MethodChannel(flutterView, CHANNEL)
        .setMethodVallHandler {
			methodCall, result => if(methodCall.method == 'getValue') {
				result.success("success")
                }
                else {
                    result.notImplemented()
                }
        }
    }
}

Android 부분에서는 MainActivity에 해당 코드를 입력 후 저장합니다.
메소드 채널을 'example.com/value'로 선언하고, 'getValue'를 통해 methodcall을 해줌으로써 입력된 값을 result로 플러터에게 보냅니다.

모든 작업이 끝났습니다. 이제 에뮬레이터 혹은 테스트 기기를 통해 실행을 하게 되면 정상적으로 네이티브 호출이 되는 것을 볼 수 있습니다.

이렇게 간단하게 Android 네이티브 호출을 하는 것에 대한 예제를 풀어 보았습니다. 다음 시간에는 이 메소드 채널을 응용하여 Android 12 이상의 기기에서 블루투스 통신을 하기 위한 퍼미션을 받고, 테스트까지 완료하게 되는 것을 해보도록 하겠습니다.

감사합니다.

댓글