# Contributing to Streak
Bug fixes, features or a well-written issue — all of it helps.
> [!NOTE]
> **Only here to translate?** You don't need any of this.
> Head to [**TRANSLATING.md**](TRANSLATING.md) — no code required.
---
## Ground rules
| | |
|---|---|
| **Small fix** | Just open a pull request. |
| **Feature or behavior change** | Open an issue first, so we agree on the direction before you spend an evening on it. |
| **Bug** | The [issue templates](.github/ISSUE_TEMPLATE) ask for your app version and device — those two answers save all the guessing. |
Streak is deliberately small, offline and free of trackers. Analytics, ads,
accounts and network backends are out of scope.
---
## Getting set up
```bash
git clone https://github.com/InlitX/streak.git
cd streak
flutter pub get # also generates the localization code
flutter run
```
You'll need the Flutter SDK (stable), the Android SDK and JDK 17. Streak targets
Android 9 (API 28) and up.
Before opening a pull request, keep both of these green:
```bash
flutter analyze # no issues
flutter test # all passing
```
If you touch **completions, streaks, scheduling or the importers**, add a test —
that's where a quiet regression corrupts someone's real history.
---
## Good to know
Three traps that have already bitten this codebase, none of which fail loudly.
- **Date walks need a floor.** Avoidance habits are *clean by default*, so a
loop walking backwards day by day must stop at the habit's `createdAt`.
Unbounded, it freezes the app.
- **Never dispose a `TextEditingController` right after `await showDialog`.**
The future completes when the dialog *starts* closing, while the field is
still mounted and rebuilding. Own it in a `State` instead.
- **Glance draws only the first 10 children** of a `Row` or `Column` — no error,
just half a widget. Chunk anything longer, and run `flutter clean` after
Kotlin changes or you'll keep testing the old one.
---
## The code
**Flutter + Dart**, state in `provider`, storage in `hive_ce`, charts from
`fl_chart`, home widgets in Kotlin with Jetpack Glance.
Features are self-contained — each owns its models, controllers, pages and
widgets. Anything that changes a habit's completions belongs in `CompletionOps`,
which both the UI and the widgets call.
Directory layout
```
lib/
├── main.dart App entry point and home-widget callback
├── app/ App shell, navigation host, theming
├── core/ Storage, date helpers, icons, routing, shared widgets
├── l10n/ Translation files (source of truth)
├── features/ habits · statistics · settings · onboarding
└── services/ Notifications, widgets, backup, import, launcher icon
android/ Android host project and the Glance widgets
fastlane/ Store listing and changelogs — F-Droid reads these
screenshots/ Screenshots used by the READMEs and app stores
test/ Unit tests
```
**Style:** match the code around you. Comments are rare on purpose — prefer a
name that explains itself. Every user-visible string goes in
`lib/l10n/app_en.arb` and is used via `context.l10n.your_key`; leave the other
languages to [Weblate](https://hosted.weblate.org/engage/streak/), and never
rename an existing key or its translations are lost everywhere.
> [!NOTE]
> Versions, tags, signing and publishing are handled by the maintainer — never
> bump a version or push a tag in a pull request. CI builds every PR as a check.
---
By contributing you agree that your work ships under the project's
[MIT License](LICENSE).