???? AniMatch — The Anime Decision Engine
A high-performance, premium Anime & Manga companion app built with Flutter, Riverpod & Firebase. AniMatch is not just another tracker — it's an Anime Decision Engine. Designed to cure choice paralysis, it combines a stunning Glassmorphic UI with a sophisticated Hybrid Recommendation Engine, real-time streaming data, and seamless cloud sync across all devices. Features · Architecture · Getting Started · Contributing · Roadmap
???? Demo
Home Screen
https://github.com/SUTHARG/AniMatch/raw/main/assets/demo/home_screen.mp4
Mood Quiz
https://github.com/SUTHARG/AniMatch/raw/main/assets/demo/Quiz_Flow.mp4
Where to Watch
https://github.com/SUTHARG/AniMatch/raw/main/assets/demo/where_to_watch_and_watch_trailer.mp4
Watchlist
https://github.com/SUTHARG/AniMatch/raw/main/assets/demo/Watchlist_saving.mp4
✨ Features
???? Home Dashboard
Anime of the Day — deterministically selected daily ( dayOfYear % topAnime.length ), changes at midnight
— deterministically selected daily ( ), changes at midnight Spotlight Banner — curated trending picks from AniList GraphQL
— curated trending picks from AniList GraphQL Currently Airing — live seasonal data from AniList
— live seasonal data from AniList Top Rated All Time — highest-ranked titles from MyAnimeList via Jikan
— highest-ranked titles from MyAnimeList via Jikan Anime / Manga mode toggle — switches the entire feed, synced to cloud
???? The Recommendation Engine
A production-grade On-Device Hybrid Recommendation Engine:
3 Recommendation Modes — Standard, Quiz-targeted, Discovery (hidden gems)
— Standard, Quiz-targeted, Discovery (hidden gems) Mathematical Scoring Pipeline:
S(a,u) = α(Content Similarity) + β(Behavioral Match) + γ(Temporal Recency) + δ(Rating) + ε(Novelty)
Deterministic Jitter — stable picks over a 24-hour window, different every day
— stable picks over a 24-hour window, different every day Results are filtered, boosted for decisive margins, and never repeat the same show two days running
???? Mood-Based Quiz
A 4-step animated quiz acting as a rapid filter for the recommendation engine:
Step Options 1 — Mood Dark & Intense · Fun & Lighthearted · Romantic · Action-packed · Relaxing · Epic Adventure 2 — Genres Action · Adventure · Comedy · Drama · Fantasy · Romance · Sci-Fi · Slice of Life · Thriller · Mystery · Horror · Sports 3 — Length Short (< 13 eps) · Medium (13–50) · Long (50+) · Any 4 — Status Completed · Ongoing · Either
???? Where to Watch
Live streaming availability fetched from Jikan v4 per anime
Platform tiles with favicons (Crunchyroll, Netflix, Funimation, Disney+, HIDIVE, Amazon)
One tap opens the native streaming app via URI deep-links
via URI deep-links Falls back gracefully: native app → browser → MyAnimeList page
Results cached in Firestore per user with 7-day TTL
Platform Deep-link Crunchyroll crunchyroll:// Netflix netflix:// Funimation funimation:// Disney+ disneyplus:// Amazon Prime aiv:// Others Browser fallback
???? Cloud-Synced Watchlist
Status: Watching · Completed · On Hold · Dropped · Plan to Watch
Episode-by-episode progress tracking
Personal ratings (0–10 stars) and text reviews
Filter tabs by status — synced to Firestore in real time with offline persistence
???? Search
Real-time search with 600ms debounce
Recent search history stored in Firestore
History chips for one-tap re-search + Clear history button
Results sorted by MAL score
???? Personal Stats
Total anime tracked, episodes watched, estimated watch time
Average personal rating · Top 3 favourite genres
???? Profile & Auth
Email/Password + Google Sign-In (one-tap)
(one-tap) Display name management · App mode synced to cloud
???? Onboarding
First-launch screen shown exactly once via SharedPreferences
via SharedPreferences Uses Navigator.pushReplacement — user can never navigate back
????️ Crashlytics
All uncaught Flutter framework errors captured automatically
All async zone errors captured via runZonedGuarded
Non-fatal API errors logged with context (endpoint, reason)
Disabled in debug mode — only active in release builds
— only active in release builds Only uid attached — no PII collected
???? Architecture
AniMatch follows a strictly layered, unidirectional architecture.
High-Level Data Flow
┌──────────────────────────────────────────────────────────────────┐ │ Flutter UI Layer │ │ Screens · Widgets · Bottom Sheets · Navigation │ └──────────────────────────────┬───────────────────────────────────┘ │ watches / reads ▼ ┌──────────────────────────────────────────────────────────────────┐ │ Riverpod Providers Layer │ │ RecommendationProvider · WatchlistProvider · AuthProvider │ │ SearchProvider · StatsProvider · AppModeProvider │ └──────────┬───────────────────┬───────────────────┬──────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │ AnimeRepository │ │ MangaRepository │ │ UserRepository │ │ Single source │ │ Single source │ │ Single source │ │ of truth │ │ of truth │ │ of truth │ └────────┬────────┘ └────────┬─────────┘ └──────────┬───────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │ JikanService │ │ AniListService │ │ FirebaseService │ │ REST client │ │ GraphQL client │ │ Auth + Firestore │ │ 400ms throttle │ │ Trending/Spot │ │ Crashlytics │ │ 5min mem cache │ │ 5min mem cache │ │ Streaming cache │ └────────┬────────┘ └────────┬─────────┘ └──────────┬───────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │ Jikan REST v4 │ │ AniList GraphQL │ │ Firebase Cloud │ │ api.jikan.moe │ │ graphql.anilist │ │ Auth + Firestore │ │ Free, no key │ │ Free, no key │ │ Crashlytics │ └─────────────────┘ └──────────────────┘ └──────────────────────┘ │ │ └─────────────────┬──────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────────────────┐ │ Local Cache Layer │ │ Hive (offline anime data) · SharedPreferences (flags) │ │ cached_network_image (cover art) · Firestore offline SDK │ └──────────────────────────────────────────────────────────────────┘
Folder Structure
lib/ ├── core/ │ ├── constants/ # Colors, API endpoints, string keys │ ├── error/ # Custom exception classes │ ├── theme/ # Material 3 light/dark themes │ └── utils/ │ └── snackbar_utils.dart # showError() and showSuccess() │ ├── data/ │ ├── models/ │ │ ├── anime.dart # Anime + StreamingLink models │ │ ├── manga.dart # Manga model │ │ └── media_base.dart # Abstract base for polymorphism │ ├── repositories/ │ │ ├── anime_repository.dart │ │ ├── manga_repository.dart │ │ └── user_repository.dart │ └── sources/ │ ├── jikan_service.dart # Jikan REST (throttled, cached, retry) │ ├── anilist_service.dart # AniList GraphQL (trending, spotlight) │ ├── firebase_service.dart# Auth, Firestore, Crashlytics helpers │ └── streaming_utils.dart # Deep-link URI resolver map │ ├── presentation/ │ ├── providers/ │ │ ├── recommendation_provider.dart │ │ ├── watchlist_provider.dart │ │ ├── search_provider.dart │ │ ├── stats_provider.dart │ │ └── app_state.dart │ ├── screens/ │ │ ├── home_screen.dart │ │ ├── quiz_screen.dart │ │ ├── results_screen.dart │ │ ├── detail_screen.dart │ │ ├── search_screen.dart │ │ ├── watchlist_screen.dart │ │ ├── stats_screen.dart │ │ ├── profile_screen.dart │ │ └── onboarding_screen.dart │ └── widgets/ │ ├── anime_card.dart │ ├── watch_status_sheet.dart │ ├── rating_sheet.dart │ └── web_image.dart # CORS fix for Flutter Web │ └── main.dart # Entry point
Firestore Data Structure
users/ └── {uid}/ ├── displayName ├── appMode # "anime" | "manga" ├── watchlist/ │ └── {malId}/ │ ├── title │ ├── imageUrl │ ├── score │ ├── status # watching | completed | │ │ # on_hold | dropped | plan_to_watch │ ├── episodeProgress │ ├── rating # 0–10 │ ├── review │ └── addedAt ├── manga_watchlist/ │ └── {malId}/ │ └── (same + chapterProgress) ├── streamingCache/ │ └── {malId}/ │ ├── links # [{ name, url }] │ └── cachedAt # 7-day TTL └── metadata/ └── search/ └── recentTerms # string[] last 10 searches
Caching Strategy
Cache Location TTL API responses In-memory Map in service classes 5 minutes Streaming links Cloud Firestore per user 7 days Anime/manga data Hive local database Until evicted Cover images Device via cached_network_image Until evicted Firestore docs Device via Firestore offline SDK Until sync
App Boot Sequence
main() ├── WidgetsFlutterBinding.ensureInitialized() ├── SystemChrome.setPreferredOrientations([portrait]) ├── dotenv.load() ├── Firebase.initializeApp() ├── Crashlytics.setCrashlyticsCollectionEnabled(!kDebugMode) ├── FlutterError.onError = recordFlutterFatalError ├── PlatformDispatcher.onError = recordError ├── SharedPreferences.getBool('seen_onboarding') ├── AppState.init() └── runZonedGuarded( () => runApp(ProviderScope(child: AniMatchApp())), (e, s) => Crashlytics.recordError(e, s, fatal: true) )
Security Model
1. Secrets → .env via flutter_dotenv (never hardcoded) 2. Firebase files → google-services.json in .gitignore 3. Firestore → Rules: users read/write own data only 4. Crashlytics → uid only, no PII, debug-mode disabled 5. APIs → Jikan & AniList are fully public, no keys
????️ Tech Stack
Layer Technology Version Framework Flutter ≥ 3.11 State Management Riverpod ^3.3.1 Authentication Firebase Auth ^5.3.1 Database Cloud Firestore ^5.4.4 Local Cache Hive + Hive Flutter ^2.2.3 Crash Reporting Firebase Crashlytics ^4.1.0 Anime Data Jikan REST API v4 free Trending Data AniList GraphQL free Image Caching cached_network_image ^3.4.0 URL Launching url_launcher ^6.3.0 Loading UI shimmer ^3.0.0 Environment flutter_dotenv ^5.1.0 Google Sign-In google_sign_in ^6.2.1 Design System Material You (Material 3) — Seed Color #6C5CE7 purple —
???? Supported Platforms
Platform Status Android ✅ Supported (min SDK 21) iOS ✅ Supported Web (Chrome / Edge) ✅ Supported Windows ✅ Supported macOS ✅ Supported Linux ✅ Supported
???? Getting Started
1. Clone
git clone https://github.com/YOUR_USERNAME/AniMatch.git cd AniMatch
2. Install dependencies
flutter pub get
3. Environment variables
cp .env.example .env # Edit .env and fill in your Firebase credentials
4. Firebase Setup (5 minutes)
Create project at Firebase Console Enable Authentication → Email/Password + Google Sign-In Enable Cloud Firestore in Production mode Apply security rules:
service cloud . firestore { match / databases / { database } / documents { match / users / { userId } / { document = ** } { allow read , write : if request . auth != null && request . auth . uid == userId ; } } }
Download google-services.json → place in android/app/ Download GoogleService-Info.plist → place in ios/Runner/
⚠️ These files are in .gitignore . Never commit them to a public repo.
5. Run
flutter run # Debug flutter build apk --release # Android release flutter run -d chrome --web-renderer html # Web
???? Troubleshooting
Problem Solution Jikan 429 errors Built-in 400ms throttle + auto-retry handles this Images broken on web Use --web-renderer html flag Google Sign-In fails on release Add release SHA-1 to Firebase Console Crashlytics not showing data Only active in release builds Onboarding shows every launch Check setBool('seen_onboarding', true) is awaited Build fails — missing config Add google-services.json to android/app/
???? Contributing
Contributions are welcome! Please read our Contributing Guide and Code of Conduct.
????️ Roadmap
✅ Completed
Flutter multi-platform setup · Riverpod architecture
Jikan REST v4 + AniList GraphQL integrations
On-Device Hybrid Recommendation Engine (3 modes)
Mood-Based Quiz · Where to Watch deep-links
Firebase Auth (Email + Google) · Firestore sync
Firebase Crashlytics (release-only)
Shimmer loading · App icon · Splash screen
Onboarding (show once) · flutter analyze passing ✅
passing ✅ Release APK verified (55.7 MB) ✅
???? Upcoming
Firebase Analytics · Collaborative filtering
Social layer (friend lists, watchlist sharing)
CI/CD pipeline (GitHub Actions + Fastlane)
Play Store + App Store launch
See roadmap.md for full details.
???? License
MIT License — see LICENSE for details.