จะเอา Flutter มาใส่ใน Android/iOS Native ต้องทำยังไงนะ?

devไปวันๆ
Krungsri Consumer-Innovation
4 min readMar 22, 2021

บล็อคนี้เกิดจากความคิดที่ว่าไม่อยากให้ Feature นั้นเป็น WebView แต่ก็ไม่อยากให้เป็น Native ก็เลยมาจบที่ Flutter มาดูกันว่ามีขั้นตอนอะไรบ้างครับ

1. วางโครงสร้างโปรเจคตามนี้ หรือตามสะดวก

เพื่อจัดกลุ่มให้ง่ายต่อการ Develop และการดูแลรักษาโค้ดของเรา

flutter-to-appex
├── android-appex
├── flutter-module
│ └── counter
└── ios-appex

android-appex คือ Android app ที่เป็น Native

flutter-module คือ Module โปรเจคของ Flutter

ios-appex คือ iOS app ที่เป็น Native

2. สร้างโปรเจค

2.1 Android (ถ้าใครเคยทำมาแล้วข้ามขั้นตอนนี้ไปได้เลย)

  • 1. เลือก Template ที่ต้องการ ในที่นี้จะเลือกเป็น Empty Activity จากนั้นกด Next ไป
  • 2. กรอกข้อมูลให้ครบ จากนั้นก็กด Finish ไป

2.2 iOS (ถ้าใครเคยทำมาแล้วข้ามขั้นตออนนี้ไปได้เลย)

  • 1. เลือก Template ที่ต้องการ ในที่นี้จะเลือกเป็น App จากนั้นกด Next ไป
  • 2. กรอกข้อมูลให้ครบ จากนั้นก็กด Finish ไป

2.3 Flutter Module

แนะนำให้ตั้งชื่อตาม feature โดยดูว่า feature นั้นทำงานเกี่ยวกับอะไร

ในตัวอย่างนี้เราจะสร้างแอพเกี่ยวกับการนับค่า เราก็จะตั้งชื่อว่า counter

  • 1. ให้เราเปิด Android Studio จากนั้นให้ลาก folder ชื่อ flutter-module มาวางที่ Android Studio ก็จะได้ประมาณนี้
  • 2. สร้าง Flutter module โดยไปที่ File -> New -> New Flutter Project… จากนั้นเลือก Flutter Module
  • 3. กรอกข้อมูลเกี่ยวกับโปรเจคให้ครบแล้วเลือก Project Location ไว้ที่ flutter-module จากนั้นก็กด Next ไป
  • 4. กรอก package name จากนั้นกด Finish ไป
  • 5. เราก็จะได้หน้าตาประมาณนี้ จากนั้นลองกดรันบนตรงรูปสามเหลี่ยมสีเขียวตามรูป
  • 6. หลังจากรันเสร็จก็จะได้ประมาณนี้

Android เรียกใช้ Flutter Module

https://flutter.dev/docs/development/add-to-app/android/project-setup

ณ ตอนนี้สามารถเรียกใช้ได้ 3 แบบ

  1. Add a single Flutter screen
  2. Add a Flutter Fragment
  3. Add a Flutter View

เพื่อเป็นตัวอย่างง่าย ๆ เราจะใช้วิธีที่ 1 ซึ่งจะมีวิธีเรียกใช้ 2 วิธี

  1. เรียกใช้งานไฟล์ .aar
  2. เรียกใช้งาน source code module ของ flutter

เราจะมาเรียกใช้ด้วยวิธีที่ 1 กัน

ก่อนอื่นเราต้องไปทำให้โปรเจค counter ของเราให้เป็นไฟล์ .aar ก่อนตามนี้

โดยเข้าไปที่โปรเจคแล้วสั่งรันตามนี้

$ cd flutter-to-appex/flutter-module/counter
$ flutter build aar

พอรันเสร็จเราก็จะได้ประมาณนี้

➜  counter git:(main) ✗ flutter build aar
Running Gradle task 'assembleAarDebug'...
Running Gradle task 'assembleAarDebug'... Done 61.4s
✓ Built build/host/outputs/repo.
Running Gradle task 'assembleAarProfile'...
Running Gradle task 'assembleAarProfile'... Done 80.7s
✓ Built build/host/outputs/repo.
Running Gradle task 'assembleAarRelease'...
Running Gradle task 'assembleAarRelease'... Done 20.8s
✓ Built build/host/outputs/repo.

Consuming the Module
1. Open <host>/app/build.gradle
2. Ensure you have the repositories configured, otherwise add them:

String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url '/Users/aycap/Development/Flutter/Workspaces/flutter-to-appex/flutter-module/counter/build/host/outputs/repo'
}
maven {
url '$storageUrl/download.flutter.io'
}
}

3. Make the host app depend on the Flutter module:

dependencies {
debugImplementation 'com.prongbang.counter:flutter_debug:1.0'
profileImplementation 'com.prongbang.counter:flutter_profile:1.0'
releaseImplementation 'com.prongbang.counter:flutter_release:1.0'
}


4. Add the `profile` build type:

android {
buildTypes {
profile {
initWith debug
}
}
}

To learn more, visit https://flutter.dev/go/build-aar

แล้วทำตามขั้นตอนที่เค้าแนะนำ หรือทำตามนี้แบบสรุปมาให้แล้ว

  1. เปิดไฟล์ <project>/app/build.gradle แล้วใส่ประมาณนี้
def storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url '/Users/aycap/Development/Flutter/Workspaces/flutter-to-appex/flutter-module/counter/build/host/outputs/repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}

dependencies {
...
implementation 'com.prongbang.counter:flutter_release:1.0'
}

2. เพิ่ม FlutterActivity ใน AndroidManifest.xml ตามนี้

<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>

3. เพิ่ม style ชื่อ LaunchTheme ในไฟล์ res/values/themes.xml

<style name="LaunchTheme" parent="@style/Theme.Androidappex">
<item name="android:windowIsTranslucent">true</item>
</style>

4. สั่งเปิด FlutterActivity เพื่อเข้าไปที่หน้า App Counter ตามนี้

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterActivityLaunchConfigs
startActivity(FlutterActivity
.withNewEngine()
.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
.build(this))

ดูข้อมูลการเรียกใช้เพิ่มเติมได้ที่ https://flutter.dev/docs/development/add-to-app/android/add-flutter-screen

5. เมื่อลองรันดูก็จะได้ประมาณนี้

iOS เรียกใช้ Flutter Module

https://flutter.dev/docs/development/add-to-app/ios/add-flutter-screen

ณ ตอนนี้สามารถเรียกใช้ได้ 1 แบบ ตามนี้

1. Add a single Flutter screen

มีอยู่ 3 วิธี

  1. Embed ด้วย CocoaPods และ Flutter SDK
  2. Embed frameworks ใน Xcode
  3. Embed application และ plugin frameworks ใน Xcode และ Flutter framework ด้วย CocoaPods

เพื่อความง่ายเราจะใช้วิธีที่ 1โดยเริ่มจาก

  1. รันคำสั่งนี้เพื่อให้ iOS Native เรียกใช้งานด้วย CocoaPods ได้
$ cd flutter-to-appex/ios-appex
$ pod init
$ pod install

3. เพิ่มคำสั่งนี้ในไฟล์ Podfile ตามนี้บนส่วนของ target

flutter_application_path = '../flutter-module/counter'load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

4. เพิ่มคำสั่งนี้ในไฟล์ Podfile ในส่วนของ target

target 'ios-appex' do
install_all_flutter_pods(flutter_application_path)
end

5. สั่งรันคำสั่งนี้อีกรอบ

$ pod install

พอรันเสร็จเราก็จะได้ประมาณนี้

➜  ios-appex git:(main) ✗ pod installAnalyzing dependenciesDownloading dependenciesInstalling Flutter 1.0.0 (was 1.22.600)Installing FlutterPluginRegistrant (0.0.1)Installing counter (0.0.1)Generating Pods projectIntegrating client projectPod installation complete! There are 3 dependencies from the Podfile and 3 total pods installed.

6. แก้ไฟล์ AppDelegate.swift ตามนี้

import UIKit
import Flutter
import FlutterPluginRegistrant

@main
class AppDelegate: FlutterAppDelegate {

lazy var flutterEngine: FlutterEngine = {
let result = FlutterEngine.init(name: "counter engine")
result.run()
return result
}()

override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self.flutterEngine)
return super.application(application, didFinishLaunchingWithOptions: launchOptions);
}
}

7. แก้ไฟล์ ViewController.swift เพื่อสั่งเปิดไปที่หน้า App Counter ตามนี้

import UIKit
import Flutter

class ViewController: UIViewController {

@objc func navigateToFlutterCounter() {
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
let flutterViewController =
FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
}

}

ดูข้อมูลการเรียกใช้เพิ่มเติมได้ที่ https://flutter.dev/docs/development/add-to-app/ios/add-flutter-screen

8. เมื่อลองรันดูก็จะได้ประมาณนี้

เพียงเท่านี้เราก็สามารถนำ Feature ที่เขียนด้วย Flutter มาใช้กับ Native ได้แล้ว ส่วนใครที่หลงมาอ่านจะใช้วิธีไหนก็เลือกตามที่ต้องการได้เลย ขอให้สนุกกับการ Coding หากผิดพลาดตรงไหนต้องขออภัยด้วยครับ

Source Code

https://github.com/prongbang/flutter-to-appex

--

--