Skip to content

Repository files navigation

better_whatsapp

A Material 3 Expressive WhatsApp client built with Flutter.

Status: early development. Phase A (UI polish + Riverpod conversion) is complete. The Go/whatsmeow backend (Phase B) is being ported from the legacy rzi_whatsapp repo — the FFI layer + ConnectScreen are now ported; the mock→real data swap is next. See ROADMAP.md and TASKS.md for the plan and actionable tasks.

Features

  • Material 3 Expressive UI with m3e_core animations
  • Responsive split-view shell (chat list + chat thread)
  • Riverpod state management (hooks_riverpod + code generation)
  • Search, filters, and pagination over mock data
  • M3E-style dismissible behavior with undo semantics

Currently backed by mock data; the real WhatsApp (whatsmeow) backend is the next milestone (Phase B).

Prerequisites

  • Nix with devenv (recommended)
  • Or a manual Flutter SDK install (stable channel)

Setup with devenv

devenv shell    # enters a shell with the Flutter SDK and Git pinned by devenv.nix
flutter pub get

Inside the devenv shell, FLUTTER_ROOT is exported automatically (derived from which flutter), so the Flutter SDK path is handled for you. If you need to point tooling at a specific SDK:

export FLUTTER_ROOT=/path/to/flutter

Setup without Nix

  1. Install the Flutter SDK (stable) and add bin/ to your PATH.
  2. export FLUTTER_ROOT="$(dirname "$(dirname "$(which flutter)")")"
  3. flutter pub get

Run

flutter run -d linux

Other targets (Android, iOS, macOS, Windows, web) are scaffolded; only Linux is actively developed at the moment.

Test & lint

flutter test      # unit and widget tests
flutter analyze   # static analysis, including riverpod_lint

Code generation

Providers use Riverpod code generation. Regenerate after editing provider annotations:

dart run build_runner watch

Generated *.g.dart files are committed to the repo.

Running the Go/FFI backend

The app talks to a WhatsApp (whatsmeow) Go library compiled to a shared library (libwhatsapp.so) via dart:ffi. The Go source lives in src/go/.

Building the shared library

# From the devenv shell (Go + gcc are provided by devenv.nix):
./src/build_go.sh

This runs go get whatsmeow@latest + go mod tidy, builds build/libwhatsapp.so (and the libwhatsapp.h header), and verifies the WA_* exports with nm. The output goes to src/build/ (gitignored).

Regenerating the FFI bindings

lib/src/ffi/whatsapp_bindings.dart is ffigen-generated output — don't edit it by hand. After changing the Go exports, rebuild the .so and run:

./generate_ffi_bindings.sh   # runs: flutter pub run ffigen --config ffigen.yaml

ffigen.yaml points at the nix-store libclang and glibc headers for this machine; the paths may need adjusting on another setup.

How it runs

lib/main.dart boots into ConnectScreen, which:

  1. Loads libwhatsapp.so from ./src/build/ (Linux dev path)
  2. Resolves the per-platform app data dir via path_provider (getApplicationSupportDirectory()) and passes it to WA_Init
  3. WA_Connect → QR code event → scan with WhatsApp > Linked Devices
  4. On "connected", hands off to ChatShell (currently still mock-data UI until the Phase B mock→real swap lands)

Data stores

The app data dir is resolved per-platform via path_provider (getApplicationSupportDirectory()): ~/.local/share/better_whatsapp on Linux, the app support dir on macOS/iOS, the app's internal data dir on Android. It contains two SQLite files, plus an avatars/ subdirectory for cached profile pictures:

  • wa-session.db — whatsmeow's own device/session store (CGO sqlite3)
  • app.db — the app's message/chat store (pure-Go sqlite): chats, messages, message_edits, contacts, jid_mappings

Avatar caching: profile pictures are downloaded and cached to disk (<data-dir>/avatars/<jid>.jpg) so the app doesn't re-download them on every startup. whatsmeow's picture ID acts as an etag — it changes exactly when the image changes — so revalidation is a cheap metadata round-trip that transfers no image bytes when the picture is unchanged:

  • Startup sweep (WA_FetchMissingAvatars, called on "connected"): backfills chats that have no avatar yet or whose image file is missing.
  • Periodic recheck (every 5 min while connected): re-validates every chat via etag; downloads only on change. Staggered per chat to avoid hammering WhatsApp (whatsmeow #672).
  • Eviction: ErrProfilePictureUnauthorized/NotSet (picture gone or private) clears both the DB row and the image file — UI falls back to initials. Real network errors keep the stale image and retry next cycle. Downloads are written atomically (tmp + rename) so a reader never sees a half-written file.

Message history export: WA_GetMessages (paginated read of stored messages) plus WA_RequestMoreHistory (asks the phone for older messages on demand; WhatsApp doesn't always respond to these from linked devices).

Releasing

CI (.github/workflows/ci.yml) runs on every push to main and on PRs: analyze, tests, build-linux, build-ios. A green CI is the gate for a release.

Release checklist (how to produce release artifacts):

  1. Preflight — make sure main is green: all four CI jobs pass (analyze, tests, build-linux, build-ios).
  2. Bump the version in pubspec.yaml (version: x.y.z+n). Follow semver — the +n suffix is the build number.
  3. Build the backend shared library (Linux):
    ./src/build_go.sh
    Confirmed exports: 13 WA_* symbols in src/build/libwhatsapp.so.
  4. Tag the release:
    git tag v$(grep '^version:' pubspec.yaml | awk '{print $2}' | cut -d+ -f1)
    git push origin v$(grep '^version:' pubspec.yaml | awk '{print $2}' | cut -d+ -f1)
  5. Build per-target artifacts:
    • Linux: flutter build linux --releasebuild/linux/x64/release/bundle/
    • Android: flutter build apk --releasebuild/app/outputs/flutter-apk/
    • iOS: flutter build ipa --release (codesign needs the team set up; CI builds --no-codesign only)
  6. Create the GitHub release (gh release create) and attach the artifacts above.

Note: targets may be added later (e.g. Android in CI). The Go FFI backend currently builds for Linux only; see Running the Go/FFI backend.

Project layout

  • lib/providers/ — Riverpod providers (chat list, messages, search, selection)
  • lib/screens/ — connect (pairing/QR), chat shell, picker, and thread views
  • lib/widgets/ — shared widgets (chat tile, pull-to-refresh, M3E indicator)
  • lib/models/Chat / ChatMessage data models (mirror Go's db schema)
  • lib/src/ffi/whatsapp_client.dart (hand-written FFI wrapper) + whatsapp_bindings.dart (ffigen-generated)
  • src/go/ — Go whatsmeow library (lib.go, internal/client, internal/db)
  • test/ — unit and widget tests

Contributing

See CONTRIBUTING.md — and please follow the CODE_OF_CONDUCT.md.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages