Flutter에서 작업을 하다보면 Native를 사용해야하는 경우가 발생할 수 있습니다.
저의 경우, Native로 안드로이드 사용자의 메세지를 읽어와야하는 상황을 마주하게 되었습니다.
물론, 여러 패키지들이 있는 pub.dev 사이트에도 사용자의 메세지를 가져오는 패키지가 존재합니다. 하지만, [Web 발신]
문자는 가져오지 못하는 것을 확인하여 Native를 사용하게 되었습니다...
본론으로 Flutter에서 Native를 사용하는 방법을 알아보도록 합시다.
메세지는 아래와 같이 플랫폼 채널을 사용하여 클라이언트(UI)와 호스트(플랫폼) 간에 전달됩니다. 메세지와 응답은 사용자 인터페이스가 응답 상태를 유지하도록 비동기식으로 전달됩니다.
클라이언트 측에서는 MethodChannel이 method call에 해당하는 메세지를 보낼 수 있습니다. 반면, 플랫폼 측에서는 MethodChannel (Android), FlutterMethodChannel (iOS)을 통해 method call을 수신하고 결과를 전송할 수 있게됩니다.
https://docs.flutter.dev/assets/images/docs/PlatformChannels.png
실습을 위해 flutter project를 새로 만들어줍니다.
Channel을 통해 결과 값이 잘 받아지는지 확인을 위해 AppBar에 제목을 바꿔는 실습을 하였습니다.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key) ;
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter - Android Channel Connection',
home: HomePage()
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Java 에서 구현한 함수를 실행할 때 실행할 함수의 이름을 지정
static const platform = MethodChannel('samples.flutter.dev/channel') ;
String _state = 'flutter' ; // Original Title of AppBar
// initState(): 페이지가 호출되면 가장 먼저 호출되는 메소드
void initState() {
super.initState();
// async: 비동기 방식
Future.microtask(() async {
await _get_string_from_channel() ;
print(_state) ;
});
}
Future<void> _get_string_from_channel() async {
String string_from_channel;
try {
// MethodChannel을 통해 Android host로 부터 받은 결과
var res = await platform.invokeMethod('getResult');
string_from_channel = "Result: $res";
} on PlatformException catch (e) {
string_from_channel = "${e.message}";
}
// 결과가 도착하였을 때, AppBar Title을 변경
setState(() {
_state = string_from_channel;
});
}
Widget build(BuildContext context) {
return Scaffold(
// Android host로 부터 받은 결과 값을 AppBar Title로 지정
appBar: AppBar(title: Text(_state)),
body: Container(),
);
}
}
package com.example.flutter_native_channel;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
// Java API 에서는 CHANNEL 이름을 통해 실행
// CHANNEL 변수의 값과 Flutter의 Channel 값이 동일해야합니다.
private static final String CHANNEL = "samples.flutter.dev/channel" ;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
result.success("Native Java Code") ; // 결과 반환
}
);
}
}