จะเอา Flutter มาใส่ใน Android/iOS Native ต้องทำยังไงนะ?
บล็อคนี้เกิดจากความคิดที่ว่าไม่อยากให้ 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 แบบ
- Add a single Flutter screen
- Add a Flutter Fragment
- Add a Flutter View
เพื่อเป็นตัวอย่างง่าย ๆ เราจะใช้วิธีที่ 1 ซึ่งจะมีวิธีเรียกใช้ 2 วิธี
- เรียกใช้งานไฟล์ .aar
- เรียกใช้งาน 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
แล้วทำตามขั้นตอนที่เค้าแนะนำ หรือทำตามนี้แบบสรุปมาให้แล้ว
- เปิดไฟล์ <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.FlutterActivityLaunchConfigsstartActivity(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 วิธี
- Embed ด้วย CocoaPods และ Flutter SDK
- Embed frameworks ใน Xcode
- Embed application และ plugin frameworks ใน Xcode และ Flutter framework ด้วย CocoaPods
เพื่อความง่ายเราจะใช้วิธีที่ 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