# flutter 接上 python (使用Chaquopy) ![](https://i.imgur.com/HKY6WPG.png) Chaquopy 的念法是 cha-ko-pi (掐狗屁...?) [由來](https://chaquo.com/Chaquopy/doc/current/faq.html#faq-name) 可以讓flutter/dart 接上python,可以讓flutter/dart 的技術邊界更加的寬廣。 以下會以介紹Chaquopy 的用途、缺點和建構方式。 [TOC] ## 💡舉例來說 * 利用python 爬蟲,把資訊擷取下來後轉給app 使用 * 使用相機做人臉辨識,影像處理等 比起用dart 的html 來處理DOM,用python 來做爬蟲相當快速也方便;而在python 裡,numpy、scipy 不用說,那些鼎鼎有名的opencv、matplotlib、pytorch等等,都可以支援。 ## 🚨缺點 但Chaquopy 並不是萬能的,少數python pacakge 並不支援,而且目前的Chaquopy 只能使用在android 平台上,所以在支援多平台的專案裡,自行手寫一份stub 是必要的。 * 不支援部分package (所有支援的[package list](https://chaquo.com/pypi-7.0/)) * 僅支援android platform,多平台專案需寫stub * build time、app size 顯著增加 ## 🛠建構方式 ### 0. 安裝python 建議安裝[python 3.8](https://www.python.org/downloads/),更高的版本可能會少一些package 可以安裝。 ### 1. Gradle 設定 Chaquopy 的建構方式,和android 原生的增加依賴是一樣的 在Chaquopy 的[Gradle Setup](https://chaquo.com/Chaquopy/doc/current/android.html) 中,基本上說明的已經非常完備了,這裡只針對flutter 專案會遇到的不同來說明。 1. 首先,要先在top-level 的build.gradle 中(通常是 `android/build.gradle`) 新增下列兩行 ```gradle buildscript { repositories { google() jcenter() maven { url "https://chaquo.com/maven" } //新增這行 } dependencies { ... classpath "com.chaquo.python:gradle:latest-version" //新增這行 } } ``` 2. 在module-level 的build.gradle (通常是 `android/app/build.gradle`)中,在 `Plugins`區塊裡放入 ```gradle apply plugin: 'com.android.application' apply plugin: 'com.chaquo.python' //新增這行,要在Android plugin 後面 ``` 3. 同樣在module-level 的build.gradle裡,設定ndk-abiFilters。 ```gradle defaultConfig { ndk { abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } } ``` :::warning 💣 容量炸彈警告💣 設定這個等於是告訴Chaquopy 需要準備哪些cpu arch 的package,也就是當你的支援度如上,可支援4種cpu arch 時,在執行 `flutter build` 的時候,Chaquopy 就會根據支援度,去下載對應的python package,也是apk 大小開始膨大的第一步。 要避免大小過大,請參考[1.1](#1.1.降低封裝大小) ::: 以下是各個cpu arch 的說明: * armeabi-v7a, 32-bit arch,幾乎涵蓋所有android 裝置。 * arm64-v8a, 64-bit arch,涵蓋2018 後的大部分裝置。 * x86, Android 模擬器使用。 * x86_64, Android 模擬器使用。 4. 在`defaultConfig` 區塊裡,新增需要的package。 如果是要使用`requirements.txt` 的方式,則要把`requirements.txt` 放在`android/app`下 ```gradle defaultConfig { python { pip { // A requirement specifier, with or without a version number: install "scipy" install "requests==2.24.0" // An sdist or wheel filename, relative to the project directory: install "MyPackage-1.2.3-py2.py3-none-any.whl" // A directory containing a setup.py, relative to the project // directory (must contain at least one slash): install "./MyPackage" // "-r"` followed by a requirements filename, relative to the // project directory: install "-r", "requirements.txt" } } } ``` :::warning 💣 容量炸彈警告💣 如同上述的abiFilter,這裡的package 的多寡,會直接的影響到最終封裝的大小。 而當你有10 個package,支援4 種cpu 架構,在初次build 的時候,Chaquopy 會安裝 10 * 4 個package 到專案內,而且build time 也會大幅上升。 要避免大小過大,請參考[1.1](#1.1.降低封裝大小) ::: 5. 在Android Studio 裡Sync Project 或是build 一下專案,在`android\app\src\main` 裡會多出一個`python` 的資料夾,把python 的原始碼放到裡面即可。 ### 1.1.降低封裝大小 ![](https://i.imgur.com/fqPgLmZ.png) 當你單個apk 大小過大,你可以: 1. 檢查你的python 原始碼 (廢話) 2. 檢查cpu arch 支援度,刪除不需要的支援 3. 整理package、requirements.txt。tensorflow 改用tflite 會好很多 4. 將不同abi 分別打包 最後一點比較複雜,因為在flutter 裡也有分abi 打包的指令(`--split-per-abi`) 但如果使用了該指令,因為我們已經在`build.gradle` 裡加上了`abiFilters`,會互相衝突,所以會出現下列錯誤: `abiFilters cannot be present when splits abi filters are set` 但若把`build.gradle` 裡的`abiFilters` 移除,使用`flutter build apk --split-per-abi` 時,則會被警告Chaquopy 需要設定`abiFilters`,而中斷build process。 所以在分割abi 包時,我的作法是: 在module-level 的build.gradle `android` 區塊中, 增加以下code ```gradle flavorDimensions "abi" productFlavors { arm32 { dimension "abi" ndk { abiFilters "armeabi-v7a" } versionCode 1000000 + flutterVersionCode.toInteger() } arm64 { dimension "abi" ndk { abiFilters "arm64-v8a" } versionCode 2000000 + flutterVersionCode.toInteger() } ... } ``` 下指令時使用一般的`flutter build apk` 就可以了。 ### 2. Flutter 依賴 在pub.dev 上有一個[chaquopy](https://pub.dev/packages/chaquopy) 的包,這裡也是要使用他,不過作者神遊(?)的關係,[chaquopy 已經把minSdkVersion 提升到21](https://chaquo.com/chaquopy/doc/current/versions.html),如果直接使用了上面的包,會沒辦法build 成功。 這裡使用BeMain 提供的解法[^來源] 1. clone [作者的repo](https://github.com/jaydangar/chaquopy_flutter) 到自己的專案內 2. 在專案內的`pubspec.yaml` 加入 ```yaml dependencies: chaquopy: path: /path/to/chaquopy ``` 3. 把`android/build.gradle` 的`minSdkVersion` 改至`21` 就可以了。 🎉🎉最後,在`android\app\src\main\python` 裡,放入[scripy.py](https://drive.google.com/file/d/1D4Hjt66f0MXkaeAQ8WLX3DEebX3BrFvM/view),就完成設定步驟了。🎉🎉 ### 3. 使用 在flutter/dart 裡使用時,只要用`Chaquopy.executeCode(code)` 就行了。 ```dart= import 'package:chaquopy/chaquopy.dart'; const String pyCode ="""print("hello from python");""" final Map<String,dynamic> result = await Chaquopy.executeCode(pyCode); ``` 若要查看`result`,則 ```dart=+ print(result['textOutputOrError']); ``` 若是在`android\app\src\main\python`中,有放python 的原始碼的話,則可以直接import 到python ```dart=+ const String importPy =""" import hello hello.world() """; final Map<String,dynamic> importResult = await Chaquopy.executeCode(importPy); print(importResult['textOutputOrError']); ``` 其他進階的使用,請參考[Chaquopy FAQ](https://chaquo.com/chaquopy/doc/current/faq.html#how-do-i) [^來源]: https://github.com/jaydangar/chaquopy_flutter/issues/42#issuecomment-1385991032