NewsWave
Offline-First Bilingual News Platform built with Flutter, Supabase & Clean Architecture

Overview
NewsWave is a production-grade, offline-first bilingual (English/Arabic) news platform built with Flutter. Designed for readers who move between languages and unstable network environments, the application combines breaking headlines, category-curated feeds, title-scoped search, and an in-app article reader into a cohesive experience that adapts its layout direction (LTR/RTL) and typography (Poppins/Cairo) instantaneously at runtime.
Architecturally, NewsWave enforces strict Clean Architecture and Dependency Inversion across its modular feature domains (splash, auth/onboarding flow, home, headlines, search, favorites, and profile) wired through a centralized GetIt service locator (sl). High-level repositories depend exclusively on abstract contracts such as HomeRepositoryContract, NetworkInfo, and TranslationService, allowing networking, caching, and translation providers to evolve independently of presentation Cubits.
Because NewsAPI's /v2/top-headlines endpoint primarily serves English-language articles for the US region while /v2/everything supports native Arabic queries, HomeRepository implements Dual-Strategy Arabic News Fetching: when Arabic is active, category feeds query /v2/everything using curated Arabic boolean expressions from _getArabicQueryForCategory, then pass results through the localization/translation pipeline. Articles already containing two or more Arabic fields (arabicCount >= 2) are skipped, while non-Arabic fields (title, cleanDescription, and cleanContent) are translated in parallel via Future.wait across batches of chunkSize = 3 (using \n<<<SEP>>>\n as the batch separator) and cached persistently.
Beyond news consumption, NewsWave features a complete user lifecycle powered by Supabase Auth, PostgreSQL, and Storage. Users can sign in, register with E.164 phone normalization, recover passwords end-to-end via the Android io.newswave://reset-password deep-link intent-filter—where supabase_flutter processes the recovery session and emits AuthChangeEvent.passwordRecovery for AuthListenerCubit to route to updatePasswordRoute—or browse immediately in Guest Mode. Authenticated users complete a 3-step personalization onboarding wizard whose preferences and avatar are dual-persisted via AuthRepositoryImpl to Supabase and local storage for zero-latency cold starts.
NewsWave
Offline-First Bilingual News Platform built with Flutter, Supabase & Clean Architecture
Breaking News Carousel & Curated Home Feed
Custom-sliver home experience combining a floating SliverAppBar, a 0.88-viewport PageView breaking-news carousel with animated dot indicators and Hero transitions, category filter chips across 7 NewsCategory domains, and a paginated 'For You' recommended feed.
Technical Architecture
A high-level overview of how the system is structured to ensure scalability and maintainability.
Feature-First Clean Architecture & GetIt Dependency Injection
Organized into core infrastructure and self-contained feature modules (splash, auth/onboarding flow, home, headlines, search, favorites, and profile), all wired deterministically in lib/core/di/service_locator.dart.
Offline-First Locale-Namespaced Hive Caching Layer
Multi-box Hive persistence storing binary TypeAdapters alongside JSON-serialized feed pages and translated article maps.
Pluggable Multi-Provider Article Translation Pipeline
Abstract TranslationService interface decoupled from ArticleTranslationRepository to translate non-Arabic article fields into Arabic on demand.
Dependency-Free Multi-Probe Connectivity Engine
Custom HTTP reachability verifier (NetworkInfoImpl) and periodic ConnectivityCubit replacing platform-specific connectivity plugins.
Reactive BLoC/Cubit State & Cross-Cubit Stream Coordination
Predictable unidirectional data flow across global app Cubits and screen-scoped feature Cubits.
Supabase Auth, Deep-Link Recovery & Dual-Write Profile Sync
Cloud identity, 3-step onboarding persistence, and avatar storage backed by Supabase and mirrored locally.
Engineering Insights
Key technical decisions and challenges encountered during the development process.
Key Decisions
Custom Multi-Probe HTTP Reachability Instead of connectivity_plus
Standard platform plugins like connectivity_plus only report whether Wi-Fi or cellular radio interfaces are enabled—not whether actual internet traffic can pass through captive portals or ISP outages.
Engineered a dependency-free NetworkInfoImpl using Dio that races three geographically distributed endpoints (NewsAPI, Google generate_204, and Cloudflare trace) in parallel. Any HTTP status code resolves the Completer immediately as online, while concurrent callers share a single in-flight Future—backed by unit tests with a fake HttpClientAdapter.
Dual-Strategy Arabic News Fetching & Parallel Translation Pipeline
NewsAPI's /v2/top-headlines endpoint for US categories primarily returns English content, so relying solely on translating top-headlines responses produces unnatural Arabic feeds and quickly exhausts free translation API rate limits.
Implemented Dual-Strategy Arabic News Fetching in HomeRepository—switching Arabic category feeds to /v2/everything with curated Arabic boolean queries from _getArabicQueryForCategory—before passing results through ArticleTranslationRepository. Articles with arabicCount >= 2 are skipped outright, while remaining non-Arabic fields (title, cleanDescription, and cleanContent) are translated in parallel via Future.wait across batches of chunkSize = 3 (with \n<<<SEP>>>\n batch separator support) and cached persistently.
Locale-Namespaced Hive Keys & Translation Cache Invalidation
Caching headlines under a single key per category caused stale English articles to appear after switching to Arabic (or vice versa) when offline or before a refresh completed.
Namespaced every Hive feed key by both category/page and active language code (e.g., cached_headlines_technology_ar vs cached_headlines_technology_en) and wired LocaleCubit.toggleLocale() to invoke ArticleTranslationRepository.clearCache(), purging transient tr_ keys while preserving raw feed caches.
Dual-Write Profile Persistence via AuthRepositoryImpl
Awaiting a remote Supabase query on every app launch to check whether a user has completed onboarding introduces unnecessary splash latency and fails when launching offline.
Implemented a local-first read / dual-write strategy in AuthRepositoryImpl across AuthLocalDataSource and AuthRemoteDataSource: cached profile data is hydrated instantaneously on startup to route SplashView, while background refresh, profile updates, and avatar uploads (picked via ImagePicker with imageQuality: 80 and uploaded to avatars/$userId/avatar_$timestamp.$ext) synchronize with Supabase's profiles table and local cache.
Technical Challenges
Cross-Language Favorites Consistency When Switching Locales
Users often bookmark an article while browsing in English and later open their Saved Articles screen after switching the app to Arabic, expecting their saved reading list to match the active language.
Stored the canonical Article model in the Hive favoritesBox keyed by a deterministic 50-character uniqueId, and enhanced FavoritesCubit.loadFavorites(locale) to pipe saved articles through ArticleTranslationRepository.translateArticles() whenever the active locale is Arabic. Saved articles remain permanently accessible offline and automatically render in Arabic or English to match the user's current locale.
Preventing Hero Tag Collisions Across Multi-Surface Article Lists
The same breaking news article can appear simultaneously in the Home PageView carousel, the 'For You' recommended sliver list, and Search results—causing Flutter's Hero controller to throw duplicate tag exceptions during navigation.
Constructed context-scoped Hero tags (article-image-<uniqueId>-carousel, article-image-<uniqueId>-recommended, article-image-<uniqueId>-search) passed via ArticleDetailArgs into ArticleDetailView, paired with a custom flightShuttleBuilder that smoothly lerps BorderRadius from 24px to 0px. This achieved zero Hero tag collisions across overlapping feeds and 60fps shared-element transitions into the article reader.
Cold-Start vs. Warm-State Deep Link Password Recovery
Tapping the io.newswave://reset-password link from an email client can either cold-start the app (while SplashView's 3.6-second animation is running) or resume an already-mounted Navigator.
Configured the io.newswave://reset-password intent-filter in AndroidManifest.xml so supabase_flutter recovers the auth session and emits AuthChangeEvent.passwordRecovery, while AuthListenerCubit listens with a _navigatingToReset guard to push updatePasswordRoute onto the global navigatorKey once the frame mounts. This guarantees reliable end-to-end password reset navigation regardless of whether the app was terminated or running in the background.
Coordinating Reactive Refreshes Across Category and Locale Streams
Changing the language in the AppDrawer or Profile screen must simultaneously update the RTL/LTR layout, switch fonts between Poppins and Cairo, and re-fetch/translate active feeds in HomeCubit, HeadlinesCubit, and SearchCubit.
Injected LocaleCubit and CategoryCubit streams into feature Cubits via GetIt, cancelling StreamSubscriptions cleanly in close() and triggering force-refreshed, locale-scoped queries with shimmer skeleton states. This delivers seamless, flicker-free transitions between English and Arabic across every active screen in the navigation stack.
Screens Gallery
A comprehensive look at the user interface and interactions.
Download NewsWave
Install the latest Android build directly to your device. All builds are optimized for performance and security.
NewsWave — ARM64 (arm64-v8a)
Recommended for most modern Android phones (64-bit ARM).
Android 6.0+ (API 23+). Unknown sources must be enabled.
Other Architectures
NewsWave — ARM32 (armeabi-v7a)
For older 32-bit Android devices.
NewsWave — x86_64
For Android emulators, Chromebooks, and x86_64 devices.
Ready to dive deeper?
Explore the source code or see how this architecture translates to other projects in my portfolio.