Flutter 로 공통 화면이나 기능을 구현후 각각의 Native(Android, iOS )에서 동일하게 사용이 가능해요.
기존 네이티브 앱이 개발된 상태에서 Flutter로 만든 기능을 추가하고 싶다면, 한번 따라해 보세요 !
Flutter로 모듈을 생성 후 native(Android, iOS)에서 플러터 모듈을 사용 하는 방법에 대해 알아볼게요.
1. 먼저 Flutter module을 생성 합니다.
Flutter module은 두가지 방법으로 생성이 가능 합니다.
- Command 생성 방법
flutter create -t module --org com.example flutter_ble_module
- Android Studio -> new -> flutter project 생성시 Project type 을 module로 생성함
모듈로 생성을 할경우 Flutter clean을 하면 Flutter module 프로젝트의 android, iOS 폴더가 삭제 되기 때문에 중요한 수정 항목이 있다면 백업을 해야 합니다.
2. 프로젝트의 디렉토리 구조 또한 중요합니다.
대부분의 문서에 나온 설정 코드들은 모듈과 프로젝트가 같은 폴더 내에 있다고 가정하고,
작성되어 있기 때문에 모듈과 프로젝트가 서로 다른 디렉토리 구조상에 존재하면 path를 다시 잡아줘야 합니다.
따라서 프로젝트 생성시 아래 구조처럼 생성을 하면 편하게 설정이 가능합니다.
모듈 - Android - iOS 프로젝트는 모두 동일한 폴더 내에 위치 하는게 좋습니다.
3. Flutter module import in Andoird Native
Flutter 모듈에서 aar(Android Archive) 를 만들어 보겠습니다.
여기서 생성된 aar 은 Android native 프로젝트에서 사용됩니다.
아래 명령어를 통해 aar 을 생성 합니다.
Flutter build aar
정상적으로 생성이 되면 아래처럼 콘솔에 사용 방법이 출력 됩니다.
Android native 프로젝트를 생성했다고 가정하고, 위의 Flutter module 에서 생성된 aar 파일을 프로젝트에 추가해 보겠습니다.
Android native 프로젝트를 열고,
settings.gradle 파일을 열어주고 아래와 같이 추가해줍니다.
import org.gradle.api.initialization.resolve.RepositoriesMode
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
repositories {
google()
mavenCentral()
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
maven {
url '/Users/gangjeongu/Documents/test/flutter_ble_module/build/host/outputs/repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
}
rootProject.name = "flutter_ble_module" //자신이 생성한 flutter module 이름을 넣어주세요.
include ':app'
setBinding(new Binding([gradle:this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_ble_module/.android/include_flutter.groovy'
//자신이 생성한 flutter module 경로를 넣어주세요.
))
include ':flutter_ble_module'
project(':flutter_ble_module').projectDir = new File('../flutter_ble_module')
//자신이 생성한 flutter module 경로 및 이름을 넣어주세요.
build.gradle(:app) 파일을 열어서 아래와 같이 추가해주세요.
dependencies {
implementation project(':flutter')
implementation fileTree(dir: 'libs', include: ['*.jar'])
.
.
.
.
}
위의 과정이 완료가 되었다면,
sync Project with Gradle Files 통해 빌드를 해줍니다.
이제 플러터 화면을 띄워 보기 위해서 Android native 프로젝트에 샘플 코드를 만들어 볼게요.
MainActivity.kt
package com.strongbulb.myapplication
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.strongbulb.myapplication.databinding.ActivityMainBinding
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugin.common.MethodChannel
private const val FLUTTER_ENGINE_NAME1 = "custom1"
class MainActivity : AppCompatActivity() {
private val channelName = "com.example.flutter_ble_module/test" // 메소드 채널 이름(모듈과 같아야 함)
lateinit var channel1 : MethodChannel
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
warmupFlutterEngine()
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.fab.setOnClickListener { view ->
channel1.invokeMethod("message", "test")
startActivity(
FlutterActivity
.withCachedEngine(FLUTTER_ENGINE_NAME1)
.build(this)
)
overridePendingTransition(0, 0)
}
}
private fun warmupFlutterEngine() {
val flutterEngine1 = FlutterEngine(this)
// FlutterEngine 변수들 초기화
// flutterEngine1.navigationChannel.setInitialRoute("/custom1")
flutterEngine1.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// FlutterEngine을 등록하기 위해 Dart 코드 실행
channel1 = MethodChannel(flutterEngine1.dartExecutor.binaryMessenger, channelName)
// 메소드채널 위치 설정
FlutterEngineCache
.getInstance()
.put(FLUTTER_ENGINE_NAME1, flutterEngine1)
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.constraintlayout.widget.ConstraintLayout>
이제 안드로이드 실행후 보라색 fab button 을 눌러보면 Flutter 가 실행 됩니다.
4. Flutter module import in iOS Native
이제 대망의 iOS 앱에 setting 방법 입니다. 가장 까탈 스러우니 잘 따라오시길 바랍니다.
iOS는 Android 와 달리 aar을 만들지 않아도 됩니다. 다만 Podfile 과 싸울뿐...
1. 먼저 깔끔하게 Flutter module 프로젝트에서 터미널을 열고 flutter clean을 입력해 줍니다.
2. Flutter module 폴더에서 flutter pub get 명령어를 입력해줍니다.
3. Flutter module -> .ios 폴더에 들어가서 pod install 명령어를 입력 합니다.
4. iOS Native 프로젝트 생성후 podfile을 열어서 아래와 같이 작성해 줍니다.
# Uncomment the next line to define a global platform for your project
platform :ios, '13.0'
flutter_application_path ='../flutter_ble_module' //flutter 모듈 경로를 입력합니다.
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'moduletest' do
# Pods for Project
# Flutter
use_modular_headers! //flutter 라이브러리에서 header관련 오류가 나는것을 방지합니다.
use_frameworks! //flutter 라이브러리 plugin 오류가 나는것을 방지합니다.
install_all_flutter_pods(flutter_application_path)
end
post_install do |installer|
flutter_post_install(installer) if defined?(flutter_post_install)
end
5. 작성이 완료 되었다면,
iOS native 폴더에서 터미널을 열고, 아래 명령어를 순서대로 입력해 줍니다.
pod install
pod update
6. 이제 실행을 위해 swift 코드로 화면을 만들어줍니다.
moduletestApp.swift
//
// moduletestApp.swift
// moduletest
//
// Created by JeonguKang on 2023/08/22.
//
import SwiftUI
import Flutter
// The following library connects plugins with iOS platform code to this app.
import FlutterPluginRegistrant
class FlutterDependencies: ObservableObject {
let flutterEngine = FlutterEngine(name: "my flutter engine")
init(){
// Runs the default Dart entrypoint with a default Flutter route.
flutterEngine.run()
// Connects plugins with iOS platform code to this app.
GeneratedPluginRegistrant.register(with: self.flutterEngine);
}
}
@main
struct moduletestApp: App {
@StateObject var flutterDependencies = FlutterDependencies()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(flutterDependencies)
}
}
}
ContentView.swift
import SwiftUI
import Flutter
struct ContentView: View {
@EnvironmentObject private var flutterDependencies: FlutterDependencies
var body: some View {
Button("Show Flutter!") {
openFlutterApp()
}
}
func openFlutterApp() {
// Get the RootViewController.
guard
let windowScene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
let window = windowScene.windows.first(where: \.isKeyWindow),
let rootViewController = window.rootViewController
else { return }
// Create the FlutterViewController without an existing FlutterEngine.
let flutterViewController = FlutterViewController(
engine: flutterDependencies.flutterEngine, nibName:nil, bundle: nil)
flutterViewController.modalPresentationStyle = .overCurrentContext
flutterViewController.isViewOpaque = false
rootViewController.present(flutterViewController, animated: true)
}
}
위 코드에서 가장 중요한 부분은
FlutterViewController(
engine: flutterDependencies.flutterEngine, nibName:nil, bundle: nil)
부분입니다 . flutter engine 매개 변수를 넣어주어야 모든 프로젝트가 정상 동작 가능합니다.
'공부방 > Flutter' 카테고리의 다른 글
Flutter module MissingPlugin 주의사항 (0) | 2023.08.30 |
---|---|
Flutter module local notification foreground push in ios not working (0) | 2023.08.29 |
Execution failed for task ':app:mapDebugSourceSetPaths' (0) | 2023.08.14 |
Flutter shorebird code push for android (0) | 2023.08.10 |
Flutter TextFormField 폰 번호 양식 자동 완성 코드 (0) | 2023.07.04 |
행복한 코딩을 위하여!
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!