Flutter

The Flutter plugin renders Essence 2 and Expression 2 on Android phones, inside a Flutter app.

Renders on the device Physical device API secret Flutter plugin 2.6.21

One Flutter dependency gives your app an avatar widget. On Android the plugin runs the same engines as the Android SDK, so the avatar renders on the phone: 16 kHz mono speech goes in, and a lip-synced picture comes out as a Flutter Texture.

PlatformWhat the plugin does
Android (arm64-v8a, a physical device)renders Essence 2 and Expression 2 on the phone; the engines resolve from Maven Central, and the example app builds from a clone
iOS (16 or newer)builds from the published tag after the plugin’s bootstrap step. For an iPhone or iPad app that renders on the device, use the Swift package
macOS (13 or newer, Apple silicon)builds from the published tag after the bootstrap step and two Homebrew libraries. For a Mac app, the Swift package is the documented path

Before you start

You needNotes
Flutter 3 with the Android toolchainJDK 17 and the Android SDK
A physical Android phone, arm64emulators cannot load the engines; Essence 2 needs Android 10 (API 29) or newer
An API secretthe Creator plan or higher

Install

The plugin is published as a tag in the homebrew-bithuman repository. Pin it in pubspec.yaml:

dependencies:
  bithuman:
    git:
      url: https://github.com/bithuman-product/homebrew-bithuman.git
      path: packages/flutter-plugin
      ref: flutter-plugin-v2.6.21

Then run flutter pub get. On Android, Gradle resolves ai.bithuman:essence2-android and ai.bithuman:expression2-android from Maven Central; nothing else to fetch.

For an iOS or macOS build, three more steps:

  1. Run the plugin’s scripts/bootstrap.sh once. It downloads the published engines and checks their sha256. For a git dependency, the plugin’s folder is packages/flutter-plugin under ~/.pub-cache/git/homebrew-bithuman-….
  2. Raise the deployment targets: platform :ios, '16.0' in ios/Podfile, platform :osx, '13.0' in macos/Podfile, and the Runner targets to match.
  3. On macOS only: brew install llama.cpp onnxruntime. The plugin links both.

Authenticate

The engines check your API secret when an avatar loads: pass it to BithumanAvatar.load(…, apiSecret:). The example app asks for it on first launch and keeps it in the Android Keystore, or you can pass --dart-define=BITHUMAN_API_SECRET=… when you build. A shipped app holds the secret on the device, so give each app its own secret that you can rotate or revoke (API secrets).

Credits pay for active session time, talking or idle, billed to the second (pricing).

First frame

Build the example app for your phone, with the wise-pup sample avatar:

git clone https://github.com/bithuman-product/bithuman-examples.git
cd bithuman-examples/app/avatar_chat
flutter pub get
flutter run --release --dart-define=AGENT_CODE=A23WJF0199
Expected result

The app installs on the connected phone, asks for your API secret once, downloads the avatar (about 160 MB, first run only) and shows it idling full screen. Speak, or type a line, and it answers with its lips in sync.

Complete example

The avatar_chat app is a complete voice conversation with idle motion and interruption, in one layout for every platform. From plugin 2.6.20 its voice session connects through bitHuman’s realtime relay with your API secret; no token is minted.

Integrate into your app

The avatar is a Texture in your widget tree. On Android the first argument to load is the agent code:

// excerpt: app/avatar_chat/lib/main.dart (bithuman-examples)
import 'package:bithuman/bithuman.dart';

await BithumanAvatar.setExpression2AgentDir('A23WJF0199');
final avatar = await BithumanAvatar.load('A23WJF0199', engine: 'expression2', apiSecret: secret);

Texture(textureId: avatar.textureId);   // the avatar in your layout
avatar.pushAudio(pcm);                   // Int16List, 16 kHz mono speech
avatar.interrupt();                      // cut the current reply
await avatar.dispose();                  // release the engine
JobCall
Show the avatarTexture(textureId: avatar.textureId); frameWidth and frameHeight give its size
Stream speechpushAudio(Int16List), 16 kHz mono
Know it is readyisReady; audio pushed before it is dropped
Interrupt the replyinterrupt()
Stopdispose()
Pick the modelengine: 'expression2' or engine: 'essence2'

Platform notes

  • Two models in one app: essence2-android needs minSdk 29; raise the app to 29.
  • Your own voice pipeline: any speech your stack produces works, as 16 kHz mono PCM through pushAudio.
  • Versions: each plugin tag fixes the Android SDK versions it uses. The current tag and its line are on Downloads & versions.

Performance

The plugin runs the Android SDK’s own engines (the same native libraries), so the Android rows apply:

ConfigurationEssence 2Expression 2
Samsung Galaxy S25+ Android
2.0× real timeSamsung Galaxy S25+ · essence2-android 0.7.0 · measured 2026-09-25
2.4× real timeSamsung Galaxy S25+ · expression2-android 0.4.10 · measured 2026-09-23
Samsung Galaxy S25+ Android held 10 min
1.4× real timeSamsung Galaxy S25+ · essence2-android 0.8.1 · held 10 min · measured 2026-09-27
2.2× real timeSamsung Galaxy S25+ · expression2-android 0.5.2 · held 10 min · measured 2026-09-27

Times real time: seconds of avatar video rendered per second. At 1.0× or more, an avatar holds a live conversation. Select a figure for its release and date. All configurations and how we measure.

Troubleshooting

SymptomCauseFix
UnsatisfiedLinkError on an emulatorthe engines are arm64-v8a onlyrun on a physical arm64 phone
The app shows a refusal and asks for a secretno API secret, or a rejected oneenter a valid secret, or build with --dart-define=BITHUMAN_API_SECRET=…
Manifest merge fails on minSdkEssence 2 needs minSdk 29raise the app to 29
pod install fails on iOS or macOSthe deployment target is below iOS 16 or macOS 13raise the Podfile platform and the Runner targets
The iOS or macOS build cannot find the enginesthe bootstrap step was skippedrun scripts/bootstrap.sh in the plugin’s folder, then build again
A macOS link error names llama or onnxruntimethe Homebrew libraries are missingbrew install llama.cpp onnxruntime

Reference