Flutter와 Native 코드를 연동하는 방법은 Flutter 앱의 기능을 확장하거나, Flutter에서 제공하지 않는 기능을 사용할 수 있게 해줍니다. Flutter는 Dart 언어를 사용하여 개발되지만, iOS와 Android의 네이티브 기능을 호출하거나, 네이티브 코드와 상호작용할 수 있는 여러 방법을 제공합니다. 아래에서는 Flutter와 네이티브 코드(Android와 iOS) 간의 연동 방법에 대해 설명하겠습니다.
1. Flutter와 네이티브 코드 연동 개요
Flutter에서 네이티브 기능을 호출하거나 네이티브 코드와 상호작용하려면 두 가지 주요 메커니즘을 사용합니다:
- Platform Channels: Flutter와 네이티브 플랫폼(Android, iOS) 간의 통신을 가능하게 하는 메커니즘입니다. 이 메커니즘을 사용하여 네이티브 기능을 호출하거나 데이터를 주고받을 수 있습니다.
- Flutter Plugins: 플러그인을 통해 네이티브 기능을 캡슐화하고 Flutter 앱에서 사용할 수 있도록 합니다. 플러그인은 Dart와 네이티브 코드 사이의 상호작용을 관리합니다.
2. Platform Channels
Platform Channels는 Flutter와 네이티브 플랫폼(Android, iOS) 간의 메시징을 가능하게 합니다. Flutter와 네이티브 간의 데이터 전송을 위한 메커니즘을 제공합니다.
2.1. 기본 개념
- Method Channel: 메서드 호출을 통해 데이터를 주고받습니다. Flutter에서 네이티브 메서드를 호출하거나 네이티브 코드에서 Flutter로 데이터를 보낼 때 사용합니다.
- Event Channel: 이벤트를 스트리밍하여 데이터를 전송합니다. 네이티브 코드에서 Flutter로 데이터 스트림을 보낼 때 사용합니다.
- Basic Message Channel: 일반적인 메시지를 주고받을 때 사용합니다. 이 채널은 텍스트 메시지와 바이너리 메시지를 처리할 수 있습니다.
2.2. Flutter에서 네이티브 메서드 호출
2.2.1. Android
1. Dart 코드 (Flutter)
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('com.example/native');
String _result = 'Result will be displayed here';
Future<void> _getNativeData() async {
try {
final String result = await platform.invokeMethod('getNativeData');
setState(() {
_result = result;
});
} on PlatformException catch (e) {
setState(() {
_result = "Failed to get data: '${e.message}'.";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Platform Channels Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_result),
ElevatedButton(
onPressed: _getNativeData,
child: Text('Get Data from Native'),
),
],
),
),
);
}
}
2. Native 코드 (Android)
MainActivity.java
또는 MainActivity.kt
에서 다음과 같이 구현합니다.
import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example/native";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getNativeData")) {
String nativeData = "Data from Native";
result.success(nativeData);
} else {
result.notImplemented();
}
}
);
}
}
2.2.2. iOS
1. Dart 코드 (Flutter)
Dart 코드 부분은 Android와 동일하게 사용합니다.
2. Native 코드 (iOS)
AppDelegate.swift
에서 다음과 같이 구현합니다.
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private let channelName = "com.example/native"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let flutterEngine = FlutterEngine(name: "my_engine")
flutterEngine.run()
GeneratedPluginRegistrant.register(with: flutterEngine)
let controller = window.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
if call.method == "getNativeData" {
let nativeData = "Data from Native"
result(nativeData)
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
3. Flutter Plugins
Flutter 플러그인은 네이티브 기능을 쉽게 사용할 수 있도록 해주는 패키지입니다. 플러그인을 사용하면, 네이티브 코드와 Flutter 코드 간의 상호작용을 캡슐화할 수 있으며, Flutter의 pub.dev
에서 다양한 플러그인을 찾을 수 있습니다.
3.1. 플러그인 생성
Flutter 플러그인을 생성하면, 네이티브 플랫폼(Android, iOS)에서 필요한 기능을 구현하고 이를 Flutter에서 사용할 수 있게 됩니다.
1. Flutter 플러그인 생성
flutter create --org com.example --template=plugin my_plugin
2. 플러그인 구성
플러그인 프로젝트에는 lib
디렉토리의 Dart 코드와 android/src/main
및 ios/Classes
디렉토리의 네이티브 코드가 포함됩니다. 플러그인의 메소드를 Dart 코드와 네이티브 코드에서 구현하고, pubspec.yaml
파일에 플러그인을 정의합니다.
3. 플러그인 사용
pubspec.yaml
파일에 플러그인을 추가하여 사용할 수 있습니다.
dependencies:
my_plugin:
path: ../my_plugin
4. 플러그인 사용 예
import 'package:flutter/material.dart';
import 'package:my_plugin/my_plugin.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _result = 'Result will be displayed here';
Future<void> _getNativeData() async {
final result = await MyPlugin.getNativeData();
setState(() {
_result = result;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Plugin Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_result),
ElevatedButton(
onPressed: _getNativeData,
child: Text('Get Data from Native'),
),
],
),
),
);
}
}
4. 결론
Flutter와 네이티브 코드를 연동하면 앱의 기능을 확장할 수 있습니다. Platform Channels를 사용하여 Flutter와 네이티브 코드 간에 데이터를 주고받거나, Flutter 플러그인을 사용하여 네이티브 기능을 Flutter에서 직접 사용할 수 있습니다. 이러한 기능들은 복잡한 애플리케이션에서 Flutter의 강력한 기능을 최대한 활용할 수 있게 도와줍니다.