これまで何本か、「AIエージェント(Claude Code)がモバイルアプリを操作する」側の検証記事を書いてきた。今回は逆方向で、モバイルアプリ自体にOS純正のオンデバイスAI(LLM)を組み込むとどうなるかを試した。
iOSにはFoundation Modelsフレームワーク(Apple Intelligence)、AndroidにはGemini Nano(ML Kit GenAI/AICore)という、それぞれのOSが持つオンデバイスLLMがある。flutter_gemma_builtin_aiというFlutterパッケージを使うと、この2つを同じDart APIで呼び分けられるらしい。実際にiOS/Android両方で動かして、どこまで「クロスプラットフォーム」なのかを確かめた。
検証環境: Flutter 3.44.4。macOS 26.5 / Apple M5 (Apple Silicon)。iOSシミュレータ(iPhone 17 Pro, iOS 26.5)。Androidエミュレータ(Pixel 10 Pro XL AVD)、およびAndroid実機(Xiaomi Redmi、Android 16)。flutter_gemma 1.4.2 / flutter_gemma_builtin_ai 0.1.0。
まず、このMacでApple Intelligenceが本当に動くか確認した
Flutterアプリを書く前に、そもそもこのマシンでApple純正のオンデバイスLLMが動くのか、swiftコマンドで直接確認してみた。
import FoundationModels
let model = SystemLanguageModel.default
print("availability: \(model.availability)")
let session = LanguageModelSession()
do {
let response = try await session.respond(to: "日本語で一言、挨拶してください。")
print("RESPONSE: \(response.content)")
} catch {
print("ERROR: \(error)")
}
実行結果(実際の出力):
availability: available
RESPONSE: こんにちは!
Apple Siliconのマシンで、日本語のプロンプトに対してオンデバイスで(ネットワーク不要で)実際に応答が返ってきた。これを見てから、Flutterアプリの実装に進んだ。
flutter_gemma_builtin_aiというパッケージ
flutter_gemma本体はGemmaモデルをバンドル/ダウンロードして動かすパッケージだが、flutter_gemma_builtin_aiはOSが最初から持っているモデルを使う。モデルファイルのダウンロードが不要で、「OSの機能を有効にするだけ」という位置づけ。
void main() {
WidgetsFlutterBinding.ensureInitialized();
FlutterGemma.initialize(
inferenceEngines: const [BuiltInAiEngine()],
);
runApp(const OnDeviceAiApp());
}
プラットフォームによってモデル指定を変えるだけで、あとのコードは共通:
final spec = defaultTargetPlatform == TargetPlatform.android
? BuiltInAiModels.geminiNano
: BuiltInAiModels.appleFoundationModels;
await FlutterGemma.installModel(
modelType: ModelType.general,
fileType: ModelFileType.builtIn,
).fromBundled(spec.name).install();
final model = await FlutterGemma.getActiveModel(maxTokens: 1024);
final chat = await model.createChat();
await chat.addQueryChunk(
const Message(text: '日本語で一言、挨拶してください。', isUser: true),
);
final result = await chat.generateChatResponse();
「Availabilityを確認」ボタンと「プロンプトを実行」ボタンだけの、シンプルな検証アプリを作った。
ビルドでハマった2点
- iOS:
flutter runがTarget Integrity (Xcode): The package product 'flutter-gemma' requires minimum platform version 16.0 for the iOS platform, but this target supports 13.0で失敗。デフォルトのIPHONEOS_DEPLOYMENT_TARGET = 13.0を16.0に上げて解決。 - Android: パッケージのREADMEに「Consumer apps require minSdk 26」と明記されていたので、
minSdkを26に固定。
検証結果: iOSは動いた、Androidはハードウェアで弾かれた
iOS(iPhone 17 Proシミュレータ)
「Availabilityを確認」をタップ。
status: BuiltInAiAvailability.available
続けて「プロンプトを実行」。実際にApple Foundation Modelsが生成した応答がアプリ内に表示された。
response: TextResponse("こんにちは!")
(LLMの出力なので、同じプロンプトでも実行のたびに文言は変わりうる。ここに載せているのは実際に得られた1回分の出力。)
シミュレータはホストMacの実行環境をそのまま使うため、Apple SiliconかつApple Intelligenceが有効なMac上であれば、実機のiPhoneを用意しなくてもFoundation Modelsの動作を確認できた。なおflutter_gemma_builtin_aiのREADMEによれば、実機で同じ機能を使うにはiPhone 15 Pro以降かつApple Intelligenceが有効であることが必要で、こちらも無条件で動くわけではない。
Android(Pixel 10 Pro XL AVD)
同じコードのまま「Availabilityを確認」をタップ。
status: BuiltInAiAvailability.unavailableDeviceUnsupported
flutter_gemma_builtin_aiのREADMEには「Gemini Nano(AICore)はPixel 9+ / Galaxy S25+の実機が必要」と書かれている。AVD(ソフトウェアエミュレータ)にはAICoreを担う専用チップが無いので、これは想定通りの結果だった。続けて「プロンプトを実行」をタップすると、ensureReady()が例外を投げてキャッチされた。
response: ERROR: BuiltInAiUnavailableException(BuiltInAiAvailability.unavailableDeviceUnsupported): Built-in AI is not available: BuiltInAiAvailability.unavailableDeviceUnsupported
クラッシュや無限待ちにはならず、型付きの例外として明確に失敗する設計になっていた。
Android実機(Xiaomi Redmi)でも試した
「エミュレータだから動かないだけでは?」という疑問が残るので、手元にあったAndroid実機(Xiaomi Redmi、Android 16)でも同じアプリを動かしてみた。この端末はPixel 9+でもGalaxy S25+でもない。
status: BuiltInAiAvailability.unavailableDeviceUnsupported
結果はエミュレータと同じunavailableDeviceUnsupported。「プロンプトを実行」でも同じBuiltInAiUnavailableExceptionが投げられた。つまり「エミュレータだから動かない」わけではなく、AICore非対応の実機でも同じく弾かれることが確認できた。逆にPixel 9+ / Galaxy S25+のようなAICore対応実機なら動くはずだが、そちらは手元に無く未確認で、READMEの記載を根拠にしている。
まとめ
- 統一Dart API経由でOS純正オンデバイスLLMを呼ぶコードは、iOS/Android共通で書けた。 実際に分岐するのは使用モデルを指定する1行だけ。
-
iOS(Apple SiliconのMac上のシミュレータ)は実際に動いた。
availability()がavailableを返し、実際に日本語の応答をオンデバイスで生成できた。 -
Androidはエミュレータでも、Gemini Nano非対応の実機でも動かない。 Gemini Nano(AICore)はPixel 9+ / Galaxy S25+相当の専用ハードウェアが必要で、AVDだけでなく手元のAndroid実機(Xiaomi Redmi)でも
unavailableDeviceUnsupportedという同じ型付きエラーになった。「実機なら動く」わけではない。 - 「クロスプラットフォームで書ける」ことと「クロスプラットフォームで動く」ことは別で、OS・実機のハードウェア要件によって実際の挙動は大きく非対称になる、という実例だった。
参考リンク
- flutter_gemma: https://github.com/DenisovAV/flutter_gemma
- flutter_gemma_builtin_ai (pub.dev): https://pub.dev/packages/flutter_gemma_builtin_ai




