Teman Isyarat (Indonesian for "Sign Friend") is an on-device, real-time Indonesian Sign Language (BISINDO) recognition app built with Flutter and Kotlin. It captures hand and pose landmarks via CameraX and MediaPipe, runs inference through a custom LiteRT model, and displays predictions — all fully offline.
- Overview
- Features
- Architecture
- Project Structure
- Tech Stack
- Getting Started
- Building
- Project Ecosystem
- License
The app is the mobile endpoint of a three-part system:
lm/— MediaPipe landmark extraction pipeline from raw videomodel/— GRU-based neural network training & LiteRT exportandroid/(this repo) — Flutter app wrapping the exported LiteRT model for on-device inference
It recognizes 20 BISINDO vocabulary words (Central Java dialect) through live camera feed, without requiring internet connectivity. Articles are fetched from Sanity CMS via HTTP.
| Category | Words |
|---|---|
| Pronouns | Aku, Kamu, Dia |
| Common | Salam, Terima Kasih, Maaf, Nama |
| Time | Hari Ini, Besok |
| Color | Merah, Kuning |
| Family | Ayah, Ibu |
| Count | Satu, Dua, Tiga |
| Other | Teman, Buku |
| Fruit | Apel, Pisang |
- Real-time camera translation — Live CameraX preview with on-screen prediction overlay
- On-device ML — All inference runs locally via LiteRT (Google AI Edge); no network required
- MediaPipe landmark tracking — Pose (9 upper-body keypoints) + hands (2 × 21 landmarks)
- Temporal smoothing — Majority-vote over a 2-frame history window for stable output
- Circular frame buffer — 110-frame native
FloatArraybuffer feeding the LiteRT model - Camera switch — Toggle between front and rear cameras
- Skeleton overlay — Live canvas rendering of detected hand and pose landmarks
┌────────────────────────────────────────────────────────────┐
│ Flutter / Dart (UI) │
│ ┌─────────┐ ┌──────────────┐ ┌──────────┐ ┌─────────┐ │
│ │ Home │ │ Translate │ │ Artikel │ │Settings │ │
│ │ Page │ │ Page │ │ Page │ │ Page │ │
│ └─────────┘ └──────┬───────┘ └──────────┘ └─────────┘ │
│ │ MethodChannel │
├──────────────────────┼─────────────────────────────────────┤
│ Android PlatformView (Kotlin) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ CameraX (PreviewView) ◄──► MediaPipe Landmarker │ │
│ │ (Pose + Hand) │ │
│ │ │ │ │
│ │ ┌────────────────────────────────────┘ │ │
│ │ │ assembleFrame() → 51 landmarks × 3 = 153-dim │ │
│ │ │ │ │
│ │ │ Circular Buffer (110 frames) ──► LiteRT Interp. │ │
│ │ │ Softmax + Temporal Smooth. │ │
│ │ │ ──► MethodChannel callback │ │
│ │ └───────────────────────────────────────────────────┘ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
- CameraX frames are fed to MediaPipe Hand + Pose Landmarker
- 51 landmarks (9 pose + 21 left hand + 21 right hand) × 3 (x, y, z) are assembled per frame
- Landmarks are pushed into a circular 110-frame
FloatArraybuffer - When full, the buffer is sent to LiteRT
Interpreter.run()with input shape[1, 110, 153] - Raw logits (20 classes) go through softmax → confidence threshold (0.5) → majority-vote temporal smoothing (2-frame window)
- The predicted label is sent back to Dart via
MethodChanneland rendered in the UI
temanisyarat/
├── lib/
│ ├── main.dart # App entry, MainPage, navigation, articles, settings (972 lines)
│ ├── constants.dart # Color palette constants (C class)
│ ├── pages/
│ │ └── translate/
│ │ ├── translate_page.dart # Camera + sign translation UI (299 lines)
│ │ ├── translate_controller.dart # Business logic, channel bridge, state (121 lines)
│ │ └── widgets/
│ │ └── scanning_dots.dart # Animated scanning indicator (55 lines)
│ └── services/
│ └── sanity_service.dart # Sanity CMS article fetching via HTTP (257 lines)
├── android/app/src/main/kotlin/com/hibah/temanisyarat/
│ ├── handlandmarker/
│ │ ├── HandLandmarkerPlugin.kt # Flutter plugin registration (PlatformViewFactory)
│ │ ├── HandLandmarkerView.kt # PlatformView: CameraX + landmarks + LiteRT (498 lines)
│ │ ├── HandLandmarkerHelper.kt # MediaPipe Hand/Pose Landmarker wrapper
│ │ └── HandLandmarkerOverlay.kt # Canvas skeleton overlay
│ └── MainActivity.kt # FlutterActivity entry
├── android/app/src/main/assets/models/
│ └── model_raw.tflite # Trained LiteRT classification model
├── android/ # Android native project root
├── assets/ # SVG illustrations, icons, launcher icon
├── pubspec.yaml # Flutter dependencies & config
└── .tool-versions # Tool version pinning
| Component | Technology |
|---|---|
| Framework | Flutter 3.44 / Dart |
| State Management | StatefulWidget + setState |
| Navigation | Navigator.push / Bottom Navigation |
| HTTP Client | http (Sanity CMS article fetching) |
| Persistence | path_provider (file I/O) |
| Permissions | permission_handler (camera) |
| Assets | flutter_svg (SVG rendering) |
| Component | Technology |
|---|---|
| Camera | CameraX 1.6.1 (core, camera2, lifecycle, view) |
| Landmark Detection | MediaPipe Tasks Vision 0.10.35 (Pose + Hand) |
| ML Runtime | LiteRT 1.4.1 + TensorFlow Select TF Ops 2.16.1 |
| Platform Bridge | Flutter MethodChannel + PlatformView |
| Min SDK | 24 |
| Kotlin | 2.3.21 |
| Gradle | 9.5.1 |
| Property | Value |
|---|---|
| Input shape | [1, 110, 153] |
| Output shape | [1, 20] (logits) |
| Classes | 20 BISINDO words |
| Architecture | GRU + 1D Conv + TempAttention |
| Model size | ~2.6 MB (LiteRT FP16) |
| Temporal smoothing | 2-frame majority vote |
| Confidence threshold | 0.5 |
- Flutter 3.44+ (see
.tool-versions) - JDK 17+ (OpenJDK 26 recommended)
- Android SDK (compileSdk from Flutter Gradle plugin)
- Android device or emulator (API 24+)
# Clone the repository
git clone https://github.com/temanisyarat/android.git
cd android
# Install Flutter dependencies
flutter pub get
# Run on connected device
flutter runThe LiteRT model is bundled in android/app/src/main/assets/models/model_raw.tflite and is copied to the device cache directory on first launch.
flutter analyze # Static analysis
flutter test # Run widget tests# Debug APK
flutter build apk --debug
# Release APK
flutter build apk --release
# App Bundle
flutter build appbundle --release
# iOS (macOS only)
flutter build ios --releaseNote: Release signing currently uses the debug configuration. Configure a proper release keystore before publishing.
This app is part of the Teman Isyarat monorepo, which includes:
| Repository | Purpose |
|---|---|
lm/ |
MediaPipe landmark extraction pipeline — converts raw BISINDO videos to .npz landmark arrays |
model/ |
GRU-based neural network training, evaluation, and LiteRT export |
android/ (you are here) |
Flutter + Kotlin Android app for on-device real-time recognition |
manager/ |
Obsidian vault with ADRs, specs, sprint tracking, and team documentation |
Video (raw MP4) ──► lm/ ──► .npz landmarks ──► model/ ──► .tflite ──► android/ ──► Live predictions
(MediaPipe extract) (Train GRU) (LiteRT on-device inference)
This project is developed for academic purposes under the Hibah Jarprak program at Universitas Sebelas Maret (UNS), in partnership with GERKATIN Solo.
Built with Flutter, MediaPipe, and LiteRT for Indonesian Sign Language (BISINDO) recognition.