Mirath (ميراث) is a production-grade academic social platform built for researchers and academics. It combines a structured research-paper library with a rich social layer: users can follow peers, create and share reading lists, annotate papers with color-coded XPath highlights, engage in threaded discussions, converse with an AI assistant that understands multi-modal input (text, images, and voice) with real-time streaming responses, and study papers with an AI assistant that can explain, summarize, and translate research content.
Repository: Mirath (Flutter)
Core app entry: lib/main.dart
Dependency / DI root: lib/injection/injection_container.dart
Router and route policy: lib/app_router.dart
Network client & auth interceptor: lib/core/network/dio_client.dart, lib/core/network/dio_auth_interceptor.dart
Table of contents
- Project Overview
- Features
- Tech Stack
- Architecture (detailed)
- State Management
- Authentication Flow
- API & Networking
- Local Storage & Caching
- Feature Modules (per major feature)
- UI / Design System
- Chatbot / AI integration
- Navigation & Deep linking
- Security considerations
- Performance optimizations
- Environment & Setup
- Running & Building
- Project structure (real tree)
- Important packages (why used)
- Developer onboarding (concise)
- Production readiness assessment
- conclusion
Project Name + Overview
Project name: Mirath
Overview
- Mirath is a knowledge preservation and research companion mobile/web app implemented in Flutter. Users can browse and search papers, annotate and highlight, create reading lists, participate in discussions, and use an integrated chatbot for research-related questions. The app targets researchers, students, and knowledge workers.
- Primary runtime targets are Android, iOS and Web (CI includes a Firebase Hosting web deployment workflow: .github/workflows).
Who it's for
- Researchers, academics, and serious readers who need to curate, annotate, discuss, and retrieve research content.
Features
-
Core reading & discovery
- Search and browse recent papers, categories, and recommendations. (Home module)
- Paper details and full reading UI with annotations. (Paper + Paper Annotations)
-
Content curation
- Save/unsave papers, reading lists creation and sharing, reading history. (Reading Lists, Library)
-
Social / community
- Discussions, comments, voting, and community search. (Community / Discussions module)
-
Profile & Onboarding
- Multi-step onboarding; profile setup and interest selection. (Onboarding, Profile)
-
AI / Chatbot
- Chatbot with file uploads and server-streaming (SSE) support. Uses server-side streaming endpoints and supports audio/image uploads. (features/chatbot)
-
Local-first & offline resilience
- Hive-based cache service with TTL, retry queue & sync manager for queued offline operations. (core/cache, core/sync)
-
Platform niceties
- Google Sign-In support, deep linking handling, native web hosting via Firebase (CI). (Auth + CI)
Tech Stack
| Area | Technology / Package |
|---|---|
| Flutter | stable channel (project targets cross-platform mobile & web) |
| Dart SDK | ^3.10.0 (pubspec environment) |
| State management | bloc, flutter_bloc, equatable |
| Dependency injection | get_it (service locator) |
| Routing | go_router |
| Networking | dio, dio_cookie_manager, cookie_jar |
| Storage / cache | hive, shared_preferences, flutter_secure_storage |
| Authentication | google_sign_in, backend session cookie-based refresh |
| File & media | image_picker, image_cropper, record, webview_flutter |
| Localization | flutter_localizations, intl, intl_utils |
| Logging | logger (MyLogger wrapper in core/utils) |
| CI / Hosting | GitHub Actions + Firebase Hosting (web) |
Refer to pubspec.yaml for full dependency list: pubspec.yaml
Architecture
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ (UI / Cubits / State Management / Navigation) │
│ │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │ Screens │ Widgets │ Cubits │ │
│ │ (Pages) │ (UI Comps) │ (BLoC/ │ │
│ │ │ │ State) │ │
│ └──────────────┴──────────────┴──────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ DOMAIN LAYER │
│ (Business Logic / Use Cases / Entities) │
│ │
│ ┌──────────────┬──────────────────┬──────────────┐ │
│ │ Entities │ Repositories │ UseCases │ │
│ │ (Models) │ (Interfaces) │ (Logic) │ │
│ │ │ │ │ │
│ └──────────────┴──────────────────┴──────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ (API / Local Storage / Repositories Implementation) │
│ │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │Remote Data │ Local Data │Repository │ │
│ │Sources │ Sources │Impl │ │
│ │(Dio API) │(Hive/Prefs) │ │ │
│ └──────────────┴──────────────┴──────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ CORE LAYER │
│ (Infrastructure / Cross-cutting concerns) │
│ │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │ Network Mgr │ Error Handler│ Cache Layer │ │
│ │ (Dio/Cookies)│ (Exceptions/ │ (Hive/ │ │
│ │ │ Failures) │ SharedPref) │ │
│ └──────────────┴──────────────┴──────────────┘ │
└─────────────────────────────────────────────────────────────┘
High level
- The app uses a feature-first, layered architecture with Clean Architecture and reactive state management with BLoC. Major characteristics:
- Feature-first folders under
lib/features/*(e.g.,chatbot,auth,home,discussions). - Each feature is split into
data,domain, andpresentationlayers where present (typical Clean Architecture). Seefeatures/chatbotfor an example. - Centralized dependency registration with GetIt in
lib/injection/injection_container.dartwhich wires data sources, repositories, use cases, and cubits. - Repository pattern abstracts network/local storage behind use cases. Use cases are registered per feature and injected into Cubits.
- State management is implemented with Cubits/BLoCs (
flutter_bloc) for predictable state transitions.
- Feature-first folders under
Key files
- DI & Service registration: lib/injection/injection_container.dart
- App router configuration and route guards: lib/app_router.dart
- Network client and auth handling: lib/core/network/dio_client.dart and lib/core/network/dio_auth_interceptor.dart
- Cache & offline queue: lib/core/cache/hive_cache_service.dart and lib/core/sync/sync_manager.dart
ASCII architecture diagram
App
├─ lib/main.dart (bootstrap: NetworkManager, DI init, root cubits)
├─ lib/app_router.dart (GoRouter + auth redirects)
├─ lib/injection/injection_container.dart (GetIt service registration)
└─ features/
├─ auth/
│ ├─ data/ (remote data source, models)
│ ├─ domain/ (entities, usecases)
│ └─ presentation/ (cubit, screens)
├─ chatbot/ <-- SSE streaming + upload + cubit
├─ home/
└─ ...
Data flow (example: send message via chatbot) UI → Cubit (ChatbotCubit) → UseCase → Repository → RemoteDataSource (DioClient) → Backend
State Management
- Library:
bloc+flutter_blocwith Cubits used widely. Cubits are registered in DI and provided where needed (seelib/main.dartandlib/app_router.dartwhere manyBlocProviderorBlocProvider.valuewrappers are created). - State flows follow the standard unidirectional pattern: UI dispatches actions to Cubit → Cubit calls usecases → usecases call repository → repository interacts with remote/local sources → Cubit emits new state.
Files to inspect for state patterns: many cubits under lib/features/*/presentation/cubit. Example: lib/features/chatbot/presentation/cubit/chatbot_cubit.dart.
Authentication Flow
Observed behavior and implementation details:
-
Sign-in / Sign-up
features/auth/data/data_sources/auth_remote_data_source_impl.dartcalls backend endpoints (login, signup, verify, etc.). Login response may contain anaccessTokenwhich the app stores locally.AuthRepositoryImplpersistsaccessTokenusingSecureStorageServiceand caches user info usingUserCacheService.
-
Token handling and refresh
- The app uses
Dioand anAuthInterceptor(lib/core/network/dio_auth_interceptor.dart) that injectsAuthorizationheaders for requests and reacts to 401 responses. - Refresh is implemented as a POST to the configured
refreshTokenendpoint; interceptor retries the original request on success and clears secure storage and triggersAuthCubit.signOut()on irreversible auth failures. - Refresh assumes server stores a refresh token in an HttpOnly cookie (the interceptor uses a cookie jar for non-web platforms and sends a refresh POST with
skipAuth: true). SeeDioClientcookie jar setup at lib/core/network/dio_client.dart.
- The app uses
-
Social sign-in
- Google Sign-In is implemented in
AuthRepositoryImplusinggoogle_sign_inand then exchanging the ID token with backend viaAuthRemoteDataSourceImpl.googleAuth.
- Google Sign-In is implemented in
-
Secure storage
- Sensitive tokens and some state (email, setup-status) are stored with
flutter_secure_storagebySecureStorageService(lib/core/services/secure_storage_service.dart).
- Sensitive tokens and some state (email, setup-status) are stored with
-
Session persistence
AuthRepositoryImpl.isSignedIn()uses secure storage to check for access token existence.
API Layer
Core client
lib/core/network/dio_client.dartwrapsDioand provides typedget/post/put/delete/patch/uploadFilehelpers and initializes interceptors.- Cookie persistence via
PersistCookieJaron mobile platforms; for web the client setswithCredentials.
Auth interceptor & error handling
lib/core/network/dio_auth_interceptor.dartattaches access token, handles 401, retries using a refresh endpoint, employs retry/backoff for refresh attempts, and calls anonAuthFailurecallback to clear local auth state when necessary.
Request lifecycle and serialization
- API responses are expected as JSON map structures; many remote data sources parse
resp.data as Map<String, dynamic>and convert to models.
Interceptors & logging
Diois configured withLogInterceptorenabled during init to log requests/responses;MyLoggeris used throughout the app for structured logs.
Local Storage
HiveCacheService(lib/core/cache/hive_cache_service.dart) is the primary JSON cache with TTL support, upsert/remove helpers and list utilities. This is used heavily for caching lists (messages, discussions, papers) and enabling offline-first UX.flutter_secure_storageviaSecureStorageServicestores sensitive tokens and email.shared_preferencesviaLocalStorageServicestores onboarding and lightweight flags (hasSeenOnboarding, profileSetup).UserCacheServicewraps user-specific caching; see DI registration forUserCacheService.
Hive Cache Service
class HiveCacheService {
// TTL-aware caching with auto-expiration
Future<void> putJson(String key, Map<String, dynamic> payload, {
Duration ttl = defaultTtl,
});
// Retrieve fresh or stale data
Future<Map<String, dynamic>?> getJson(String key, {
bool allowStale = false,
});
// List caching
Future<void> putJsonList(String key, List<Map> items);
Future<List<Map>?> getJsonList(String key, {bool allowStale});
// Invalidation
Future<void> delete(String key);
Future<void> clear();
}class CacheKeys {
static String recentPapers({String? category, int page = 1}) =>
'recent_papers_${category ?? 'all'}_page_$page';
static String recommendations({int page = 1}) =>
'recommendations_page_$page';
static String libraryStats() => 'library_stats';
static String readingHistory() => 'reading_history';
static String currentUser() => 'current_user';
}Real-time cache invalidation via CacheNotifier:
// When something changes, notify listeners
CacheNotifier.instance.notify(CacheKeys.libraryStats());
// Cubits listen and reload
CacheNotifier.instance.stream.listen((key) {
if (key == CacheKeys.libraryStats()) {
_reloadLibraryData();
}
});Cache records store:
payload(JSON data)cachedAt(timestamp)ttlSeconds(expiration duration)
Freshness check:
bool isFresh(DateTime now) =>
now.difference(cachedAt).inSeconds < ttlSeconds;- User opens app
- App returns stale cached data immediately (fast UI)
- In background, app fetches fresh data
- When fresh data arrives, cache updated
- If UI is still showing old data, next interaction shows fresh data
This provides perceived instant loading while keeping data fresh.
Feature Modules (Key modules — purpose, important screens, cubits, repos)
-
Chatbot
- Purpose: Conversational research assistant, supports file uploads, streaming responses.
- Screens:
lib/features/chatbot/presentation/screens/chatbot_screen.dart. - Cubits:
ChatbotCubit(lib/features/chatbot/presentation/cubit/chatbot_cubit.dart),SessionsCubitfor session list. - Repository:
ChatbotRepositoryImpl(lib/features/chatbot/data/repositories/chatbot_repository_impl.dart). - Data sources:
ChatbotRemoteDataSourceImplsupports multipart uploads and SSE streaming.
-
Auth
- Purpose: Sign in/up, social auth, account verification, password reset.
- Screens:
signin_screen,signup_screen,verify_account,otp_screen,reset_password_screen. - Cubit:
AuthCubit. - Data source & repo:
AuthRemoteDataSourceImpl,AuthRepositoryImpl.
-
Home / Discovery
- Purpose: Home feed, search, categories, recommendations.
- Cubits:
HomeCubit,SearchCubit.
-
Discussions (Community)
- Purpose: Create discussions, comment, vote, and offline sync of created items.
- Important files:
features/discussions/*(cubits, models, data sources). Uses SyncManager for queued discussion creation.
-
Paper Annotations
- Purpose: Highlighting and annotating papers, stored locally and synced.
- Important files:
features/paper_annotations/*.
For a complete list of features and their folders, see lib/features/.
UI / Design System
- Theme provider:
lib/core/themes/my_theme.dartdefines light/dark themes and design tokens. - Reusable widgets live under
lib/core/ui/widgets/(e.g.,MyAppBar,MyBody) andlib/core/ui. - Fonts:
google_fontsis used; assets/fonts are configured inpubspec.yamlandflutter_intlis enabled for localization. - Responsive design: The app uses MaterialApp.router with GoRouter; many screens use responsive layout patterns but there is no single CSS-like responsive system — responsiveness is handled per widget.
AI / Chatbot System
- Chatbot is implemented as a frontend client to a server-side chatbot API. The app supports:
- Multipart file uploads (
uploadFile,uploadFiles). - Sending messages and receiving either immediate JSON responses or a streaming SSE response parsed by
streamSessionMessagesinChatbotRemoteDataSourceImpl. - Streaming implementation parses SSE 'data:' messages and yields decoded strings.
- Multipart file uploads (
Navigation & Routing
Mirath uses GoRouter for declarative, type-safe routing with deep linking support.
// app_router.dart
final appRouter = GoRouter(
initialLocation: RouteNames.splash,
redirect: (context, state) {
// Auth guard: redirect unauthenticated users
final authState = context.read<AuthCubit>().state;
if (authState.status == AuthStatus.unauthenticated &&
!state.location.startsWith('/signin')) {
return RouteNames.signin;
}
return null; // No redirect
},
routes: [
GoRoute(
path: RouteNames.splash,
builder: (context, state) => const SplashScreen(),
),
// ... more routes
],
);// core/constants/route_names.dart
class RouteNames {
static const String splash = '/splash';
static const String signin = '/signin';
// Helper methods
static String paperDetailsRoute(String paperId) => '/papers/$paperId';
static String discussionDetailsRoute(String id) => '/discussions/$id';
}Simple Navigation:
appRouter.go(RouteNames.home);Push with Return Value:
final result = await appRouter.push(RouteNames.editProfile);Replace (no back button):
appRouter.replace(RouteNames.home);Named Route with Parameter:
appRouter.push(RouteNames.paperDetailsRoute('paper-123'));The app handles deep links from:
- Email links (shared papers, discussions)
- Web URLs
- Native app links
// main.dart handles deep link on app start
void _handleDeepLinkIfAny() {
final uri = Uri.base;
final path = uri.fragment; // For web hash routing
if (path.contains('/papers/')) {
// Navigate to paper after auth resolves
appRouter.push(target);
}
}Supported Deep Links:
- mirath.app/papers/:paperId
- mirath.app/discussions/:discussionId
- mirath.app/reading-lists/:listId
- mirath.app/users/:userId
Network & Connectivity
Singleton NetworkManager monitors real-time connectivity:
// Checks actual internet access (not just WiFi connection)
Future<bool> get isConnected async {
// 1. Check if WiFi/mobile available
final results = await _connectivity.checkConnectivity();
if (results.contains(ConnectivityResult.none)) return false;
// 2. Verify real internet by DNS lookup
for (final domain in ['google.com', 'cloudflare.com', '1.1.1.1']) {
try {
final lookup = await InternetAddress.lookup(domain);
if (lookup.isNotEmpty) return true;
} catch (_) {}
}
return false;
}
// Stream real-time connectivity changes
Stream<bool> connectionStream => _connectionController.stream;Connected in main.dart, displays banner when offline:
StreamBuilder<bool>(
stream: NetworkManager.instance.connectionStream,
builder: (context, snapshot) {
final isConnected = snapshot.data ?? true;
return Column(
children: [
Expanded(child: child),
if (!isConnected)
RedBanner(text: 'No internet connection'),
],
);
},
)Strategy:
- Check connectivity before making requests
- If offline, try to return cached data
- Show "offline" indicator to user
- Queue changes for sync when online
Failed operations automatically queued for retry:
// From ChatbotCubit - queue failed message
await retryService.enqueue('send_message', {
'sessionId': currentSessionId,
'content': message,
'files': attachedFiles,
});
// Automatically retries when connectivity restoredSecurity
- Access tokens are stored in
flutter_secure_storageviaSecureStorageService. - Refresh tokens are expected to be HttpOnly cookies (cookie jar management in Dio client); the interceptor performs POST-based refresh flows while using cookie persistence for non-web platforms. This reduces exposure of refresh tokens in JavaScript contexts.
- Network calls are guarded with
NetworkManager.ensureConnected()and many repositories checknetworkManager.isConnected.
Performance Optimizations Observed
- Caching: TTL-based Hive cache reduces redundant API calls.
- SyncManager + RetryQueue provide offline resilience and controlled retry/backoff.
- Dio
LogInterceptorcan be disabled in release builds for performance. - Use of
constwidgets and localized incremental fetching (pagination) in repositories contribute to efficient UI updates.
Environment Setup
Prerequisites
- Flutter (stable) and Dart SDK compatible with
sdk: ^3.10.0(seepubspec.yaml). - Android SDK / Xcode for device builds.
- For web preview, Firebase CLI is used in CI; to deploy manually configure Firebase with
firebase initandfirebase deploy.
Recommended steps (local)
flutter pub get
# Run on a connected device/emulator
flutter run
# Build
flutter build apk
flutter build appbundle
flutter build web --no-tree-shake-iconsFirebase Hosting CI is configured in .github/workflows and firebase.json for web builds.
Running the Project
- Install packages:
flutter pub get- Run (debug) on device/emulator:
flutter run- Build commands:
# Android
flutter build apk
flutter build appbundle
# iOS
flutter build ios
# Web
flutter build web --no-tree-shake-iconsCI build for web is defined in .github/workflows and deploys to Firebase Hosting.
Project Structure (actual snapshot)
Top-level lib/ tree (trimmed):
lib/
├─ main.dart
├─ app_router.dart
├─ generated/
├─ injection/
│ └─ injection_container.dart
├─ core/
│ ├─ network/ (dio_client, auth interceptor, network manager)
│ ├─ cache/ (hive cache service)
│ ├─ services/ (secure/local/image/audio services)
│ └─ ui/
└─ features/
├─ auth/
├─ chatbot/
├─ home/
├─ discussions/
├─ paper_annotations/
├─ reading_lists/
└─ ...
You can explore the full structure starting at lib/ in your editor — the codebase follows feature-first organization.
Important Packages & Why They Exist
| Package | Purpose |
|---|---|
| bloc / flutter_bloc | State management with Cubits/Blocs for predictable state and testability |
| get_it | Dependency injection / service locator used in injection_container.dart |
| dio | Robust HTTP client with interceptors and streaming support |
| cookie_jar / dio_cookie_manager | Persistent cookies support for refresh-token cookie flows |
| hive | Local JSON cache with TTL and repeated lookups (offline support) |
| flutter_secure_storage | Secure storage for access token and sensitive data |
| go_router | Declarative routing with redirects and deep linking |
| google_sign_in | Social login provider (Google Sign-In) |
| recorder / image_picker | Media capture & uploads for chatbot file attachments |
Developer Onboarding (concise)
- Prereqs: Install Flutter stable and ensure Dart >=3.10.0.
- Clone repository and run
flutter pub get. - Open in VS Code / Android Studio — main entrypoint is
lib/main.dart. - DI: Inspect
lib/injection/injection_container.dartto understand service registration. - Router: check
lib/app_router.dartfor routes and redirect logic; inspectRouteNamesconstant helper. - Chatbot: See
lib/features/chatbotfor streaming & upload examples. - Run app:
flutter runorflutter run -d chromefor web; CI builds web via GitHub Actions.
Mirath demonstrates a sophisticated, production-ready Flutter application built with enterprise software engineering principles. The architecture prioritizes:
- Maintainability: Clear separation of concerns, modular features
- Scalability: Layered architecture, cache strategies, pagination
- Reliability: Error handling, offline support, retry mechanisms
- Security: Secure storage, JWT authentication, input validation
- Performance: Caching, lazy loading, efficient rebuilds
The codebase is well-positioned for scaling to thousands of active users while maintaining code quality and developer velocity.