English | Русский
Chat app for OpenRouter.ai and VseGPT.ru written in Flutter (Dart). Builds into an Android APK right on GitHub Actions - no local Android SDK, JDK, or macOS required.
Twin project: the same app implemented in Python (Flet) - see android_flet_aichat.
- Chat with OpenRouter.ai and VseGPT.ru models, balance in $ or ₽ depending on the provider
- 4-digit PIN login: on first launch you enter your API key, the app detects
the provider by the key prefix (
sk-or-v1-- OpenRouter,sk-or-vv-- VseGPT), checks the balance and generates a PIN. The key and the PIN hash are stored in SQLite - PIN login on subsequent launches: a wrong PIN shows an error, "Reset key" suggests entering a new one
- Multi-page app with bottom navigation - 4 pages:
- Chat - the main page with the model selector and per-message cost
- Provider settings - current provider, masked key, balance refresh, key reset
- Token statistics - messages, tokens and spending per model
- Spending chart - bar chart of daily spending for the last 14 days
- Chat history in SQLite, JSON export, copy to clipboard
- Dark theme, Material 3
First login: API key entry
Generated PIN: shown once, required for the next login
Next launches: 4-digit PIN login, key reset button
Chat page: OpenRouter, balance, AI response, per-message cost
Chat page: VseGPT
Provider settings: OpenRouter, masked key, balance, reset
Provider settings: VseGPT
Token statistics: messages, tokens and spending per model
Spending chart: daily costs for the last 14 days
The ready-to-install APK is available in the releases of this repository:
- Open the Releases page and download the APK from the attachments of the latest release (Assets)
- Install the APK on an Android device
- Allow installing from unknown sources on first launch
You can also grab a fresh build from Actions:
- Open the Actions tab of this repository
- Pick the latest Build APK run and download the
aichat-apkartifact - Install the APK on an Android device
The login screen works in two modes:
- First login - enter the API key. The app detects the provider by the
prefix:
sk-or-vv-...- VseGPT,sk-or-v1-...- OpenRouter. Any other prefix is rejected. The key is validated through the balance endpoint (/balancefor VseGPT,/creditsfor OpenRouter). For a valid key the app generates a random 4-digit PIN and shows it once in a dialog - remember it for the next login. If the balance is not positive, a warning is shown but the login is allowed - PIN login - enter the 4-digit PIN. The PIN is checked against the SHA-256 hash stored in SQLite; a wrong PIN shows an error. The "Reset key" button deletes the saved key and PIN and switches the screen back to key entry
Key validation errors are distinguished by cause: "Key not found or invalid" (API responded 401/403) or "No connection to the provider server" - no internet or blocking.
├── lib/
│ ├── api/
│ │ └── api_client.dart - unified API client, provider detection
│ │ by key prefix, key validation
│ ├── models/
│ │ └── message.dart - message model with tokens and cost
│ ├── providers/
│ │ ├── auth_provider.dart - PIN/key login state
│ │ └── chat_provider.dart - chat state, models, balance, history
│ ├── services/
│ │ ├── auth_service.dart - PIN generation, SHA-256 hashing
│ │ ├── database_service.dart - SQLite: messages + auth table (v2)
│ │ └── analytics_service.dart - usage statistics
│ ├── screens/
│ │ ├── login_screen.dart - PIN/key login (first and repeat)
│ │ ├── home_screen.dart - bottom navigation shell (4 pages)
│ │ ├── chat_screen.dart - main chat page
│ │ ├── settings_screen.dart - provider settings, key management
│ │ ├── stats_screen.dart - token statistics per model
│ │ └── spending_screen.dart - daily spending chart (fl_chart)
│ └── main.dart - entry point, theme, AuthGate
├── .github/workflows/
│ └── build-apk.yml - GitHub Actions: flutter build apk
├── assets/ - app icon
├── android/ - Android platform files
├── pubspec.yaml - dependencies
└── README.md
flutter pub get
flutter build apk --releaseThe APK is saved to
build/app/outputs/flutter-apk/app-release.apk. A local Flutter SDK
(3.47.2) and an Android SDK are required.








