Table Habit stores habits in SQLite and syncs them over your own WebDAV server
Open-source Flutter habit tracker with smart scoring, WebDAV sync, local-first storage, privacy-focused design, and support for Android, iOS, Windows, macOS, and Linux.
At a glance
- What is it?
- Table Habit is a Flutter habit tracker for micro habits with its own scoring system and growth charts, running on Android, iOS, macOS, Windows and Linux with no account and no ads. State lives in SQLite on the device, and cross-device sync goes through a WebDAV server you control.
- Who is it for?
- Table Habit fits a person who wants a habit tracker with no subscription, no account and no telemetry, and who is willing to run or rent a WebDAV endpoint to keep several devices in sync. It does not fit a team that needs shared accounts, server-side history, or a habit tracker whose data is provably in someone else's hands, since everything is local SQLite plus a WebDAV directory you control.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Storage is a SQLite file on the device, and the account is the absence of one
The specification table in the README is compact and decides most of the design. Price is free with no ads and no in-app purchases. Account is not required, with no sign-up and no telemetry. Offline-first is described as fully functional without internet. Tech stack is Flutter, Dart, SQLite and Provider.
Put those together and the architecture is a local-first app with no server component of its own. Habits are records in a local SQLite database, charts are computed from that database, and nothing requires a round trip to work. The app is usable on a plane, and deleting the app deletes the data with it, which is the flip side of having no account.
Provider as the state management layer is the ordinary Flutter choice, and the presence of pubspec.yaml, pubspec.lock and lib/ at the root is the standard Dart package layout. Nothing here suggests a hosted backend, and there is no API to authenticate against because there is no API.
The privacy claims are stated as project facts rather than as audit results: no sign-up, no telemetry, and a PRIVACY.md at the repository root that is the place to read the actual policy text rather than a summary of it.
WebDAV sync puts the server choice on the user, and the responsibility too
Sync is the one feature that introduces a network dependency, and the README is specific about what it is: WebDAV, with Nextcloud, Koofr and self-hosted named as the targets.
That is a materially different design from a habit tracker with a vendor account. The app is a WebDAV client, so the storage is wherever the user says it is. A Nextcloud instance the user already runs is the natural choice, Koofr covers people who do not want to run a server, and self-hosted covers the rest.
The trade-off is direct and worth stating plainly. The user gains control over where the data lives and gains no vendor relationship when something goes wrong. A Nextcloud account that expires, a quota that fills, or a server that changes its authentication takes the sync with it, and the data lives in the WebDAV directory rather than in a place the app can recover on its own.
Because the app is offline-first, a broken sync server is a degraded mode rather than a dead app. Records keep being written locally. That is the design working as intended, and it is also why nothing in the README frames sync as a required step.
Getting the app: seven distribution channels for one codebase
The README carries download badges for GitHub Releases, F-Droid, Flathub, Google Play, the App Store, TestFlight and the Microsoft Store. That is a lot of channels for one project, and each answers a different need.
The package identifier is io.github.friesi23.mhabit, visible in the F-Droid, Flathub and Google Play links, which means the F-Droid and Flathub builds are from source. GitHub Releases is the direct route and carries both stable and pre-release tags: v1.27.13+202 on 2026-10-10, pre-v1.27.12+201 on 2026-10-09, pre-v1.27.11+200 on 2026-10-02. The build number suffix in those tags suggests every build is tagged rather than only released ones.
TestFlight is the beta channel, which is worth knowing since the stable tags and the pre-release tags both move fast.
The build environment is pinned by the badge to Flutter 3.35.7, and the repository backs that up on disk with a .flutter directory, .fvmrc and .fvm/, which is FVM's version pinning. So a contributor builds against a specific Flutter SDK checked into the project rather than whatever is on the machine.
The Makefile is a melos monorepo entrypoint, not a build shortcut
The repository root carries a Makefile that is worth reading before contributing, because it explains the shape of the project. It defaults to help, and it declares a .PHONY list that includes init, bootstrap, normalize-l10n, build-runner, format, fix, gen-icons, test, gen, verify-generated, verify-submodules, aio, aio-full, sync-rules, unsync-rules, gen-screenshot-data and gen-natural-sort-data.
The help target describes init as configuring git hooks and aliases, initializing the submodule and fetching packages, with bootstrap as an alias of it. That matches the .githooks/ directory and .gitmodules at the root, and init therefore is the first command a contributor runs.
The platform split is handled in the Makefile itself rather than in scripts. On Windows_NT it sets SHELL to cmd.exe, resolves ./.flutter/bin/flutter.bat and dart.bat, and calls scripts\$(1).cmd. Everywhere else it uses /bin/bash with ./.flutter/bin/flutter and scripts/$(1).sh. The same target names work on both, which is why the run_script macro exists.
melos is invoked through the pinned Dart binary as MELOS := "$(LOCAL_DART)" run melos, and packages/ at the root is the monorepo it manages. The verify-generated and verify-submodules targets exist so generated code and the submodule state are checked, which is the discipline you want in a repository where build_runner output and localisation are committed.
Localisation, packaging and the rest of the root directory
Several root entries describe how the app is shipped rather than how it works.
l10n.yaml and a pubspec.lock sit alongside the Flutter platform directories android/, ios/, linux/, macos/ and windows/. The normalize-l10n and gen-l10n targets are the localisation path, and the README names the languages it ships, listing Arabic, Chinese, Czech, French, German, Hebrew and Japanese among others. The Weblate badge is the live translation status widget for the hosted.weblate.org project.
Packaging is covered by flatpak/ for the Linux store build, installers/ for desktop installers, fastlane/ for store submission automation, and flutter_launcher_icons.yaml plus flutter_launcher_momo_icons.yaml and flutter_launcher_notification_icons.yaml for icon generation, which is why gen-icons is a make target rather than a manual step.
Two other files earn their place. LICENSE_THIRDPARTY.md sits next to LICENSE, which is normal for an Apache 2.0 project shipping third-party assets. build.yaml is the source code generation manifest, matching build.yaml at the root alongside the code gen workflow.
And there is a Python file in a Dart project, demo/habit_group_curve_demo.py, under demo/debug/. That is a script for producing or checking habit group curve data, which is the kind of artifact that also explains the gen-screenshot-data make target.
Where Table Habit is the wrong choice
Four boundaries.
Shared or multi-user tracking is the first. There is no account, so there is nothing to log into and no way for a household or a small team to share one habit list. Syncing via WebDAV shares files between devices you control, not records between people.
Server-side backup is the second. If you lose the phone and the WebDAV server was the copy, the data is gone. Keeping a third copy means running your own backup of the WebDAV directory, which is work the app does not do for you.
A rich analytics history is the third. The README describes a scoring system and growth charts, which suits tracking micro habits over weeks. It does not claim per-habit cohort analysis, and the repository layout contains nothing suggesting a server that could aggregate across users, because there is no server.
Frictionless onboarding is the fourth. This is a self-hosted-capable, FVM-pinned, melos-managed Flutter repository. Using the app is easy because binaries are published, but reading the source to verify a claim about your data is a real effort, not a web page visit.
One more note on the scoring. A unique scoring system is the project's own design, and the README does not publish the formula, so treat the score as an opaque number the app computes rather than something to reason about or compare against another tracker.
Editorial conclusion
Table Habit fits a person who wants a habit tracker with no subscription, no account and no telemetry, and who is willing to run or rent a WebDAV endpoint to keep several devices in sync. It does not fit a team that needs shared accounts, server-side history, or a habit tracker whose data is provably in someone else's hands, since everything is local SQLite plus a WebDAV directory you control. Before adopting, decide where the WebDAV server lives, because the README lists Nextcloud, Koofr and self-hosted as the targets and the sync is only as private as that server, and read PRIVACY.md before you import an existing habit history.
Frequently asked questions
How do I install Table Habit?
Download badges point to GitHub Releases, F-Droid, Flathub, Google Play, the App Store, TestFlight and the Microsoft Store. The package identifier is io.github.friesi23.mhabit, and the app is built with Flutter 3.35.7.
Does Table Habit need an account?
No. The README states that no account is required, with no sign-up and no telemetry, and that the app is fully functional without internet. Storage is a local SQLite database and results come back as growth charts computed from it.
How does WebDAV sync work in Table Habit?
Sync uses WebDAV, and the README names Nextcloud, Koofr and self-hosted servers as targets. Because the app is offline-first, a server that is unavailable degrades sync while the app keeps recording locally.
What licence is Table Habit released under?
Apache 2.0, with LICENSE at the repository root and a separate LICENSE_THIRDPARTY.md for third-party components. The README states there are no ads and no in-app purchases.
Which platforms does Table Habit support?
Android, iOS, macOS, Windows and Linux. The repository contains android/, ios/, macos/, windows/ and linux/ directories, plus flatpak/ and installers/ for Linux and desktop packaging.
How do I build Table Habit from source?
The Makefile is the entrypoint and its default goal is help. Its init target, aliased as bootstrap, configures git hooks and aliases, initializes the submodule and fetches packages, and the pinned SDK lives in .flutter, .fvmrc and .fvm/. Melos is run through the local Dart binary to manage packages/.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/friesi23-mhabit)Was this useful?