A Flutter app for calculating and timing colloidal silver electrolysis sessions. Enter your water volume, electrode current, and target PPM concentration — the app computes the required electrolysis time using Faraday's Law and guides you through the session with a live countdown, electrode cleaning reminders, and push notifications.
Supports Android, iOS, and Web.
- Enter water volume in mL or liters
- Pick a saved device to set electrode current automatically, or enter it manually via Custom (0–5,000 mA)
- Choose target concentration in PPM
- Quick-select common PPM presets
- Remembers your last-used values (including selected device) between sessions
- Maintain a personal library of electrolysis generators: name, current in mA, and whether the device auto-switches electrode polarity
- Ships with 3 seeded generic devices (5mA/10mA/20mA); add, edit, or delete devices freely
- Devices with auto polarity switching skip the electrode-cleaning-reminder alarms during a run
- Circular countdown display with elapsed/remaining time
- Start, pause, and resume support — timer is wall-clock anchored so it stays accurate if the app is backgrounded
- Electrode cleaning alarm markers displayed on the ring at each scheduled interval
- Session automatically saved to history on completion
- Persistent log of all completed sessions (SQLite via Drift)
- Shows volume, current, PPM, duration, and completion time
- Records a snapshot of the device used (name, current, auto polarity), accurate even if the device is later edited or deleted
- Push notification when electrolysis completes (works when the app is backgrounded)
- Scheduled electrode cleaning reminders at configurable intervals (5, 10, 15, 20, or 30 minutes)
- All notifications managed via OS scheduler — fire reliably even if the app is killed
- Light / Dark / System theme
- Default volume unit, PPM, and current
- Enable/disable notifications and cleaning alarms
- Configure cleaning alarm interval
- Flutter SDK ≥ 3.47 (Dart ≥ 3.13)
- For Android: Android SDK 36, JDK 17,
minSdk24 (Android 7.0); toolchain is AGP 9.1 / Gradle 9.3 / Kotlin 2.4 - For iOS: Xcode ≥ 26 (iOS 26 SDK), deployment target iOS 15.0 — Swift Package Manager only, no CocoaPods
flutter pub get# List connected devices/emulators
flutter devices
# Run on a specific device
flutter run -d <device-id>
# Run on the first available device
flutter run# Android APK
flutter build apk
# Android App Bundle (for Play Store)
flutter build appbundle
# iOS (requires macOS + Xcode)
flutter build ios
# Web
flutter build webThe required electrolysis time is derived from Faraday's Law of Electrolysis:
time (s) = (PPM × Volume (L) × F) / (M_Ag × I (A) × 1000)
| Symbol | Value | Description |
|---|---|---|
| F | 96,485 C/mol | Faraday constant |
| M_Ag | 107.87 g/mol | Molar mass of silver |
| I | user input | Electrode current in amperes |
| PPM | user input | Target concentration in mg/L |
Several packages require generated code. After modifying any file annotated with @freezed, @riverpod, @JsonSerializable, or a Drift table class, regenerate:
dart run build_runner build --delete-conflicting-outputsNever manually edit *.g.dart or *.freezed.dart files.
# Run all tests
flutter test
# Run a single file
flutter test test/core/silver_calculator_test.dartflutter analyzelib/
├── main.dart # App entry point; initialises SharedPreferences & NotificationService
├── app.dart # MaterialApp.router with theme + go_router wiring
├── core/
│ ├── constants/ # AppConstants (SharedPreferences keys, notification IDs), PhysicsConstants
│ ├── routing/ # go_router setup; /timer route has a guard requiring a calculation result
│ ├── theme/ # AppTheme (light/dark), AppColors
│ ├── extensions/ # DurationExtensions
│ └── utils/
│ └── silver_calculator.dart # Pure calculation service (Faraday's Law)
├── features/
│ ├── calculator/ # Input form, device picker, PPM presets, result card
│ ├── devices/ # Device library — model, repository, manage/pick UI
│ ├── timer/ # Countdown controller, circular timer widget, notification service
│ ├── history/ # Drift/SQLite session log (also hosts the Devices table)
│ └── settings/ # SharedPreferences-backed settings with freezed model
└── shared/
└── widgets/
└── app_scaffold.dart # Bottom navigation shell (Calculator / Timer / History)
| Concern | Package |
|---|---|
| State management | flutter_riverpod + riverpod_annotation |
| Navigation | go_router |
| Immutable models | freezed |
| Persistence (history, devices) | drift (SQLite) |
| Persistence (settings) | shared_preferences |
| Notifications | flutter_local_notifications + timezone |
| Animations | flutter_animate, percent_indicator |
| Permission | Reason |
|---|---|
POST_NOTIFICATIONS |
Timer completion and cleaning alarm notifications (Android 13+) |
SCHEDULE_EXACT_ALARM / USE_EXACT_ALARM |
Precise notification scheduling |
RECEIVE_BOOT_COMPLETED |
Reschedule exact alarms after device reboot |
WAKE_LOCK |
Wake screen when the completion alarm fires |
USE_FULL_SCREEN_INTENT |
Show alarm on the lock screen (Android 14+) |
The version is defined in one place: version: X.Y.Z+N in pubspec.yaml. Bump it with tool/bump_version.sh, merge the
release PR into main, then push a vX.Y.Z tag: .github/workflows/release.yml builds and publishes web, Android and iOS and
creates the GitHub release. The full process, dry runs and required secrets are in docs/release.md.
See docs/README.md: release process and the architecture decision records (ADRs).
Issues and pull requests are welcome. See CONTRIBUTING.md for the workflow (feature branch, squash-merged PR), the checks CI runs and how to add translations, and the Code of Conduct. Security problems go through SECURITY.md.