π€ Android μ€μ
μ΄ λ¬Έμλ 'Test Solution' Flutter SDKλ₯Ό λ€μ΄ν°λΈ Android μ±μ ν΅ν©νκΈ° μν μ€μ λ°©λ²μ μλ΄ν©λλ€.
μμνκΈ° > μ€μΉ κ°μ΄λ λ¬Έμμ λ°λΌ νλ‘μ νΈμ Flutter Moduleμ΄ μΆκ°λμλ€κ³ κ°μ ν©λλ€.
1. AndroidManifest.xml μ€μ β
- SDKλ API μλ²μ ν΅μ νκΈ° μν΄ μΈν°λ· κΆνμ΄ νμν©λλ€.
app/src/main/AndroidManifest.xmlνμΌμ μλ κΆνμ μΆκ°νλμ§ νμΈνμΈμ. - μΆκ°λ‘, Flutterλ μλλ‘μ΄λ μ± λ΄μμ νλ¬ν° νλ©΄μ νμνκΈ° μν΄
FlutterActivityλ₯Ό μ 곡ν©λλ€. λ€λ₯Έ λͺ¨λ Activityμ λ§μ°¬κ°μ§λ‘ FlutterActivity λAndroidManifest.xmlμ λ°λμ λ±λ‘ν΄μΌ ν©λλ€.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
...>
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
</application>
</manifest>
2. SDK μ€ν λ° ν΅μ μ€μ β
Host μ±κ³Ό SDK(Flutter)λ **MethodChannel**μ ν΅ν΄ ν΅μ ν©λλ€. ν
μ€νΈλ₯Ό μμνκ³ , κ²°κ³Όλ₯Ό μ½λ°±μΌλ‘ λ°κΈ° μν μ€μ μ΄ νμν©λλ€.
MethodChannel μ€μ β
MainActivity.kt λλ SDKλ₯Ό νΈμΆν Activityμμ MethodChannelμ μ€μ ν©λλ€. μ±λ₯μ μν΄ FlutterEngineμ μΊμ±νμ¬ μ¬μ©νλ κ²μ κΆμ₯ν©λλ€.
λ€μμ Application.kt μ μΊμ± μμ μ½λ μ
λλ€.
class MyApplication : Application() {
companion object {
const val FLUTTER_ENGINE_ID = "test_solution_engine"
const val CHANNEL_NAME = "com.mscbrain.sdk.test_solution_sdk/channel"
}
lateinit var flutterEngine: FlutterEngine
lateinit var methodChannel: MethodChannel
override fun onCreate() {
super.onCreate()
flutterEngine = FlutterEngine(this)
flutterEngine
.dartExecutor
.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
.getInstance()
.put(FLUTTER_ENGINE_ID, flutterEngine)
methodChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL_NAME
)
methodChannel.setMethodCallHandler { call, result ->
when (call.method) {
"onResult" -> {
// ν
μ€νΈ μλ£ λλ κ²°κ³Ό μμ
println("Flutter sent a result: ${call.arguments}")
result.success(null) // μλ΅ μ μ‘
}
"onInfo" -> {
// SDK version μμ (getSDKVersion μμ² μ)
println("Flutter sent a version: ${call.arguments}")
result.success(null)
}
"onLog" -> {
// SDK λ‘κ·Έ μμ (enableHostLogging = true μΌ λ)
println("Flutter sent a log: ${call.arguments}")
result.success(null)
}
else -> {
result.notImplemented()
}
}
}
}
}
μλλ MainActivity.ktμ μ 체 μμ μ½λμ
λλ€.
- Views (XML)
- Jetpack Compose
package com.example.yourapp
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.example.myandroidapp.MyApplication.Companion.FLUTTER_ENGINE_ID
import com.example.myandroidapp.databinding.ActivityMainBinding
import io.flutter.embedding.android.FlutterActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
findViewById<Button>(R.id.start_test_button).setOnClickListener {
startTestSolution()
}
}
private fun startTestSolution() {
startActivity(
FlutterActivity
.withCachedEngine(FLUTTER_ENGINE_ID)
.build(this)
)
// ν
μ€νΈ μμ νλΌλ―Έν° ꡬμ±
val arguments = mapOf(
// νμ κ°
"goodsId" to "[GOODS_ID]",
"suid" to "[SUID]",
"language" to "[LANGUAGE]",
// μ ν κ°
"isDevelopment" to true,
"enableHostLogging" to true,
"themeColor" to 0xFF7E57C2, // μμ: 보λΌμ
"themeMode" to "light",
"titleText" to "λμ μ±κ²© μ ν μ°ΎκΈ°",
"customData" to mapOf("userId" to "12345", "source" to "app_event")
)
// `startTest` λ©μλ νΈμΆ
(application as MyApplication).methodChannel.invokeMethod("startTest", arguments)
}
}
package com.example.yourapp
import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.example.mycomposeapp.ui.theme.MyComposeAppTheme
import io.flutter.embedding.android.FlutterActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyComposeAppTheme {
MainScreen()
}
}
}
}
@Composable
fun MainScreen() {
val context = LocalContext.current
val application = context.applicationContext as MyApplication
Box(Modifier.fillMaxSize(), Alignment.Center) {
Button({
startTestSolution(context, application)
}) {
Text("ν
μ€νΈ μμ")
}
}
}
private fun startTestSolution(context: Context, application: MyApplication) {
context.startActivity(
FlutterActivity
.withCachedEngine(MyApplication.FLUTTER_ENGINE_ID)
.build(context)
)
// ν
μ€νΈ μμ νλΌλ―Έν° ꡬμ±
val arguments = mapOf(
// νμ κ°
"goodsId" to "[GOODS_ID]",
"suid" to "[SUID]",
"language" to "[LANGUAGE]",
// μ ν κ°
"isDevelopment" to true,
"enableHostLogging" to true,
"themeColor" to 0xFF7E57C2, // μμ: 보λΌμ
"themeMode" to "light",
"titleText" to "λμ μ±κ²© μ ν μ°ΎκΈ°",
"customData" to mapOf("userId" to "12345", "source" to "app_event")
)
// `startTest` λ©μλ νΈμΆ
application.methodChannel.invokeMethod("startTest", arguments)
}
μ½λ μ€λͺ β
FlutterEngineμΊμ±: μ± μ€ν μ 미리FlutterEngineμ μμ±νμ¬ μΊμμ μ μ₯ν©λλ€. μ΄λ κ² νλ©΄ SDK νλ©΄(FlutterActivity)μ λμΈ λ λ‘λ© μλκ° λ§€μ° λΉ¨λΌμ§λλ€. μ΄ μ½λλApplicationν΄λμ€μonCreate()μμ μ€ννλ κ²μ΄ κ°μ₯ μ΄μμ μ λλ€.MethodChannelμ΄κΈ°ν: Hostμ SDKκ° ν΅μ ν μ±λμ μμ±ν©λλ€. μ±λ μ΄λ¦(CHANNEL_NAME)μ SDK μΈ‘μ μ μλ μ΄λ¦κ³Ό λ°λμ λμΌν΄μΌ ν©λλ€.setMethodCallHandler: SDKκ° Host μͺ½μΌλ‘ λ©μλλ₯Ό νΈμΆν λ(onResult,onLog,onInfo) μ΄λ₯Ό μμ νλ 리μ€λμ λλ€.whenꡬ문μ μ¬μ©νμ¬ κ° λ©μλ μ΄λ¦μ λ°λΌ λΆκΈ° μ²λ¦¬λ₯Ό ν©λλ€.- ν
μ€νΈ μμ: λ²νΌ ν΄λ¦ λ± νΉμ μ΄λ²€νΈκ° λ°μνμ λ μΊμλ
FlutterEngineμ μ¬μ©νμ¬FlutterActivityλ₯Ό μ€νν©λλ€. invokeMethod("startTest", ...):FlutterActivityκ° μ€νλ ν,startTestλ©μλλ₯Ό νΈμΆνμ¬ νμ λ° μ ν νλΌλ―Έν°λ₯Ό Map ννλ‘ SDKμ μ λ¬ν©λλ€.
Activity λΏλ§ μλλΌ Fragment λ μ§μν©λλ€. FlutterFragment(Flutter Docs)λ₯Ό μ°Έκ³ ν΄μ£ΌμΈμ.