Skip to content

Repository files navigation

SBA

A privacy-respecting bookmarking app for students — built with Flutter for Android and iOS. Save links and text snippets, organise them into collections, set reminders, and lock the app behind biometrics. Everything works offline; signing in only adds cross-device sync.

SBA is a Flutter port of an earlier React Native app. Most Dart files keep a // Ported from src/... header pointing at their RN origin.

  • Package / bundle id: com.kevin.sba
  • Flutter: 3.44.2 stable · Dart SDK: ^3.12.2
  • State: provider (ChangeNotifier) · Local: shared_preferences · Cloud: Supabase (Postgres + Auth)

Core principle: local-first

Every feature works without an account. Bookmarks live in shared_preferences and are fully usable offline. Signing into Supabase only adds cross-device sync — it is never a gate. Any Supabase write is guarded by if (_session != null) in lib/providers/bookmark_provider.dart.

Features

  • Save URLs (auto-fetches page title, favicon, and description) and plain-text snippets
  • Save straight from any app via the share sheet (receive_sharing_intent)
  • Collections — group bookmarks; empty collections persist until deleted
  • Reminders — schedule a local notification per bookmark
  • Filter: All · Text · Websites · Scheduled · full-text search
  • Dark / Light / System theme, persisted
  • Biometric app lock (Face ID / Touch ID / fingerprint), re-locks on background
  • Cloud sync via Supabase (sign up / in / out) with offline-first merge

Quick start

flutter pub get          # first run, or after pulling new deps
flutter run              # pick a device interactively

Supabase credentials are baked in as safe defaults (the publishable/anon key — RLS protects rows), so the app runs out of the box with sync enabled. To point at your own project, override at build time:

flutter run \
  --dart-define=SUPABASE_URL=https://<project-id>.supabase.co \
  --dart-define=SUPABASE_ANON_KEY=<publishable-or-anon-key>

Run on a specific device

flutter devices                              # list what's connected
flutter emulators                            # list Android emulators / iOS simulators
flutter emulators --launch Pixel_10_Pro_XL   # boot one
flutter run -d Pixel_10_Pro_XL               # run on it

While running: r hot reload · R hot restart · q quit.

See the run-app skill for device gotchas and release-apk for building a shippable build.

Project layout

lib/
├── main.dart                  # Entry: providers, splash → lock → home, share + deep-link wiring
├── models/bookmark.dart       # Bookmark + sealed BookmarkCategory (text | url | webPage)
├── providers/                 # ChangeNotifier state
│   ├── bookmark_provider.dart # CRUD, collections, reminders, Supabase sync, auth
│   ├── theme_provider.dart    # dark / light / system
│   └── lock_provider.dart     # biometric app lock
├── storage/bookmark_storage.dart  # shared_preferences CRUD + Supabase row mapping
├── services/                  # supabase, notifications, sharing
├── screens/                   # home, add-bookmark sheet, auth, lock, settings, splash
├── widgets/                   # bookmark_card, search_bar, empty_state, skeleton
├── utils/                     # web-metadata fetch, date/reminder/collection helpers
└── design/tokens.dart         # Spacing, Radii, AppType, AppColors, kPrimary (#0891B2)

Deeper detail: .claude/docs/ARCHITECTURE.md. Build/test/ship workflow: .claude/docs/WORKFLOW.md.

Before committing a change

dart format <changed files>
flutter analyze          # must be clean — no new issues
flutter test             # smoke tests in test/widget_test.dart must pass

If you touched UI, run it on a real device — don't claim it works untested.

Conventions

  • Design tokens only. Pull spacing, radii, colors, and text styles from lib/design/tokens.dart. No magic numbers or hardcoded hex in widgets. Brand primary is cyan #0891B2 (kPrimary).
  • Theme-aware. Read colors via context.watch<ThemeProvider>().colors so light/dark both work.
  • Models are immutable. Bookmark and BookmarkCategory use copyWith; never mutate in place.
  • Don't rename storage keys. They use a legacy @lynk/ prefix (@lynk/bookmarks, @lynk/collections). Renaming orphans existing users' local data.

Author

Kalisa Ngabo Kevin — designed & built SBA.

License

Private project — publish_to: 'none'.

About

A student bookmarking app for students built with Flutter. This Save links and text snippets, organise them into collections, set reminders, and lock the app behind biometrics

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages