Skip to content

Hugo-Dev1/noteaai

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Notea AI Logo

Notea AI β€” Hybrid AI Notes App

A senior-level Flutter portfolio application demonstrating Clean Architecture,
hybrid on-device/cloud AI routing, and cost-conscious engineering.

Flutter Dart Riverpod Gemini Tests License


Screenshots

Home Editor AI Actions
Insights β€” Mood Tracker Insights β€” Topic Distribution AI Highlights
Insights History


What makes this app portfolio-worthy?

Three engineering decisions separate NoteAI from a typical Flutter CRUD app:

1. Selective Intelligence β€” A routing engine (AIOrchestrator) that decides in real time whether a task should run on-device (free, private) or in the cloud (powerful, tracked). This is not a feature flag β€” it is a decision tree that evaluates prompt complexity, task type, and monthly budget before every AI call.

2. The Budget Guardian β€” A financial protection system baked into the data layer. Every cloud AI call is intercepted by UsageRepositoryImpl, costed using TokenCalculator, and recorded atomically in Isar. At $8.00 the user sees a warning chip. At $10.00 the app enforces local-only mode β€” automatically, without any user action required. The guard lives in AIOrchestrator.route(), not in UI button handlers, so it is impossible to bypass by adding a new screen.

3. Strict layer boundaries β€” Domain, Data, and Presentation layers have zero cross-layer imports. The domain layer has no Flutter or infrastructure dependencies. Every AI action is a standalone use case β€” adding a new AI feature means adding a new file, not modifying existing ones.


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     PRESENTATION LAYER                       β”‚
β”‚                                                              β”‚
β”‚  HomeScreen  EditorScreen  InsightsScreen  FoldersScreen     β”‚
β”‚  SettingsScreen  (GoRouter + fl_chart + flutter_animate)     β”‚
β”‚                                                              β”‚
β”‚  Riverpod Providers (@riverpod code generation)              β”‚
β”‚  notesNotifierProvider β†’ aiOrchestratorProvider              β”‚
β”‚  summarizeNotifier  insightsNotifier  settingsProvider       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚ ref.watch / ref.read
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      DOMAIN LAYER                            β”‚
β”‚            (zero Flutter / infrastructure imports)           β”‚
β”‚                                                              β”‚
β”‚  Entities: Note, AiUsage, InsightsData, AiResult             β”‚
β”‚  Use Cases: SummarizeNote, ExtractTasks, ChangeTone,         β”‚
β”‚             ExpandIdea, FixGrammar, Translate, FormatText,   β”‚
β”‚             GenerateInsights, GetNotes, SaveNote, DeleteNote  β”‚
β”‚                                                              β”‚
β”‚           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚           β”‚        AIOrchestrator         β”‚  The Brain       β”‚
β”‚           β”‚                               β”‚                  β”‚
β”‚           β”‚  route(prompt, task)          β”‚                  β”‚
β”‚           β”‚  1. canUseCloudAI? (budget)   β”‚                  β”‚
β”‚           β”‚  2. isEligibleForLocal?       β”‚                  β”‚
β”‚           β”‚     (task type + char length) β”‚                  β”‚
β”‚           β”‚  3. recordUsage after cloud   β”‚                  β”‚
β”‚           β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                  β”‚              β”‚                             β”‚
β”‚           Local? β”‚              β”‚ Cloud?                      β”‚
β”‚      (free, $0)  β”‚              β”‚ (tracked, budgeted)         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚              β”‚ delegates
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       DATA LAYER                               β”‚
β”‚                                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚   GemmaDataSourceImpl    β”‚  β”‚  GeminiRemoteDataSource    β”‚ β”‚
β”‚  β”‚                          β”‚  β”‚                            β”‚ β”‚
β”‚  β”‚  Primary: Gemma Nano     β”‚  β”‚  Routes via Vercel proxy   β”‚ β”‚
β”‚  β”‚  (Android AICore)        β”‚  β”‚  Gemini 1.5 Flash (simple) β”‚ β”‚
β”‚  β”‚  Fallback: extractive    β”‚  β”‚  Gemini 1.5 Pro  (complex) β”‚ β”‚
β”‚  β”‚  summarization (Dart)    β”‚  β”‚  Uses TokenCalculator for  β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  cost estimation           β”‚ β”‚
β”‚                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                             β”‚                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚            UsageRepositoryImpl (Budget Guardian)         β”‚  β”‚
β”‚  β”‚                                                          β”‚  β”‚
β”‚  β”‚  isBudgetExceeded()  β†’ reads Isar, compares $10 cap      β”‚  β”‚
β”‚  β”‚  recordApiCall()     β†’ Read-Modify-Write (atomic)        β”‚  β”‚
β”‚  β”‚  getCurrentMonthUsage() β†’ AiUsageModel.toEntity()        β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                 Isar Local Database                     β”‚   β”‚
β”‚  β”‚                                                         β”‚   β”‚
β”‚  β”‚  NoteModel @collection                                  β”‚   β”‚
β”‚  β”‚  β”œβ”€β”€ @Index() category  (O(1) filter by category)       β”‚   β”‚
β”‚  β”‚  └── @Index(type: value) updatedAt  (sorted queries)    β”‚   β”‚
β”‚  β”‚                                                         β”‚   β”‚
β”‚  β”‚  AiUsageModel @collection                               β”‚   β”‚
β”‚  β”‚  └── month + year fields (monthly budget tracking)      β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

AI Routing Decision Tree

User triggers AI action
         β”‚
         β–Ό
AIOrchestrator.route(prompt, task)
         β”‚
         β–Ό
usageRepository.isBudgetExceeded()?
         β”‚
  YES ($10 reached) ──────────────────────→ _routeToLocalAi()
         β”‚                                        β”‚
         β”‚ NO ($0–$9.99)                          β”‚ isAvailable()?
         β–Ό                                        β”‚
_isEligibleForLocalAi(task, prompt)?        NO ──→ Exception
         β”‚                                    (UI shows budget-exceeded msg)
    task ∈ {summarize, extractTasks}
    AND prompt.length < 500 chars
         β”‚
    YES  β”‚                    NO
         β–Ό                     β–Ό
 _routeToLocalAi()      _routeToCloudAi()
         β”‚                     β”‚
  isAvailable()?        task ∈ {generateInsights,
         β”‚                      changeTone}?
    NO   β”‚                     β”‚
         β”‚             YES     β”‚    NO (expand, fixGrammar,
         β–Ό              β–Ό           translate, formatText)
  throws Exception  Gemini Pro       β–Ό
         β”‚              β”‚        Gemini Flash
  catch in route()      β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                   β”‚
         β–Ό             TokenCalculator
 _routeToCloudAi()     .calculateCost()
  (graceful fallback)        β”‚
                       recordApiCall()
                       (Isar atomic write)
                             β”‚
                       AiResult{text, model,
                         tokensUsed, costUsd}

Tech Stack

Layer Technology Why
UI Framework Flutter 3.x + Material 3 Cross-platform, Dynamic Color (Monet on Android 12+)
State Management Riverpod 2.x (code gen @riverpod) Compile-safe providers, zero manual boilerplate
Local Database Isar 3.x Type-safe queries, native List<double> for embeddings, no migrations
Cloud AI Gemini 1.5 Flash + Pro via Vercel proxy Flash for cost, Pro for quality β€” routed by task type. Proxy keeps API key off the device
On-device AI Gemma Nano (flutter_gemma, pending) Zero cost, offline, private
Local AI Fallback Extractive Dart algorithm Zero dependencies, works on all platforms while Gemma matures
Charts fl_chart 0.68 Fully customizable LineChart + PieChart
Animations flutter_animate 4.x Declarative staggered animations, shimmer skeleton loading
Navigation GoRouter 14.x Declarative routing, deep-link ready
Theming dynamic_color Monet wallpaper-based color extraction
Preferences shared_preferences Persistent user settings (theme, budget cap, language)
Immutable data freezed + json_serializable Pattern-matching sealed classes, code-generated fromJson
Environment flutter_dotenv API keys in .env, never committed to source control

Key Features

Smart Notes

  • Create, edit, and organize notes with categories (Work, Ideas, Personal, Creative)
  • Masonry grid layout with glassmorphic cards and staggered entrance animations
  • Inline markdown formatting toolbar in the editor

AI Actions (7 actions in Editor overlay)

Action Routes to Notes
Summarize Gemma Nano / Gemini Flash Free for notes < 500 chars (extractive fallback)
Extract Tasks Gemma Nano / Gemini Flash Matches -, β€’, βœ“, β–‘, numbered lists
Change Tone Gemini Pro Professional rewrite β€” always generative
Expand Idea Gemini Flash Turns a short note into full paragraphs
Fix Grammar Gemini Flash Spell-check and grammar correction
Translate Gemini Flash 10 languages: EN, ES, FR, DE, PT, IT, ZH, JA, AR, RU
Format Text Gemini Flash Cleans up and converts to Markdown

AI Insights Dashboard

  • Mood trend line chart over 7 days (fl_chart LineChart with FlSpot data)
  • Topic distribution donut chart (fl_chart PieChart with TopicSlice domain entities)
  • AI-generated highlight cards: growth patterns and burnout alerts
  • Goals detected in notes with status tracking (in-progress / completed / at-risk)
  • Recommendations and weekly summary generated by Gemini Pro
  • Real-time budget chip: AI Budget: $X.XXX / $10.00 with warning color at $8.00
  • Skeleton shimmer loading state (flutter_animate .shimmer()) while Gemini Pro responds

Insights History (FoldersScreen)

  • Every insight analysis is persisted as a JSON snapshot with timestamp
  • Chronological history lets users compare how their notes evolve over time

Settings

  • Theme mode (light / dark / system)
  • Configurable budget cap and warning threshold
  • Default translation language
  • Number of notes to include in insights analysis
  • Default note category
  • Confirm-before-delete toggle
  • Export notes and clear AI history

Budget Guardian

  • Hard cap: $10.00 USD/month on cloud AI (AppConstants.monthlyBudgetLimitUsd)
  • Warning threshold: $8.00 (AppConstants.budgetWarningThresholdUsd)
  • Automatic local-only mode when cap is reached β€” enforced in AIOrchestrator, not UI
  • Atomic Read-Modify-Write in UsageRepositoryImpl.recordApiCall() to prevent race conditions

Project Structure

lib/
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ constants/
β”‚   β”‚   β”œβ”€β”€ app_constants.dart    # AppConstants + AiTask enum (budget limits, thresholds)
β”‚   β”‚   └── api_constants.dart    # Model IDs (gemini-1.5-flash, gemini-1.5-pro)
β”‚   β”œβ”€β”€ errors/                   # Base Failure + Exception sealed classes
β”‚   β”œβ”€β”€ extensions/               # StringExtensions, ContextExtensions
β”‚   └── utils/
β”‚       β”œβ”€β”€ token_calculator.dart # TokenCalculator (cost math, pure static)
β”‚       └── network_info.dart     # Connectivity checking
β”‚
β”œβ”€β”€ domain/                       # Pure Dart β€” zero framework dependencies
β”‚   β”œβ”€β”€ entities/
β”‚   β”‚   β”œβ”€β”€ note.dart             # Note entity (id, title, content, embeddings)
β”‚   β”‚   β”œβ”€β”€ ai_usage.dart         # AiUsage (monthly tokens, costUsd, isBudgetExceeded)
β”‚   β”‚   └── insights_data.dart    # InsightsData (moodHistory, topicDistribution, highlights,
β”‚   β”‚                             #               goals, recommendations, weeklySummary)
β”‚   β”œβ”€β”€ repositories/
β”‚   β”‚   β”œβ”€β”€ note_repository.dart  # Abstract NoteRepository interface
β”‚   β”‚   β”œβ”€β”€ ai_repository.dart    # Abstract AiRepository (processLocal, processCloud)
β”‚   β”‚   └── usage_repository.dart # Abstract UsageRepository (isBudgetExceeded, record)
β”‚   β”œβ”€β”€ failures/
β”‚   β”‚   └── failures.dart         # Sealed Failure hierarchy (DatabaseFailure, AIFailure…)
β”‚   └── usecases/
β”‚       β”œβ”€β”€ ai_orchestrator.dart  # AIOrchestrator β€” central routing engine
β”‚       β”œβ”€β”€ summarize_note_usecase.dart
β”‚       β”œβ”€β”€ extract_tasks_usecase.dart
β”‚       β”œβ”€β”€ change_tone_usecase.dart
β”‚       β”œβ”€β”€ expand_idea_usecase.dart
β”‚       β”œβ”€β”€ fix_grammar_usecase.dart
β”‚       β”œβ”€β”€ translate_usecase.dart
β”‚       β”œβ”€β”€ format_text_usecase.dart
β”‚       β”œβ”€β”€ generate_insights_usecase.dart
β”‚       β”œβ”€β”€ get_notes_usecase.dart
β”‚       β”œβ”€β”€ save_note_usecase.dart
β”‚       └── delete_note_usecase.dart
β”‚
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ datasources/
β”‚   β”‚   β”œβ”€β”€ local/
β”‚   β”‚   β”‚   β”œβ”€β”€ isar_datasource.dart    # IsarDataSource (CRUD + usage queries)
β”‚   β”‚   β”‚   └── gemma_datasource.dart   # GemmaDataSourceImpl (on-device + fallback)
β”‚   β”‚   └── remote/
β”‚   β”‚       └── gemini_datasource.dart  # GeminiRemoteDataSource (via Vercel proxy)
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ note_model.dart       # @collection NoteModel with fromEntity/toEntity
β”‚   β”‚   β”œβ”€β”€ note_model.g.dart     # Generated by isar_generator
β”‚   β”‚   β”œβ”€β”€ ai_usage_model.dart   # @collection AiUsageModel
β”‚   β”‚   └── ai_usage_model.g.dart
β”‚   └── repositories/
β”‚       β”œβ”€β”€ note_repository_impl.dart   # Concrete NoteRepository
β”‚       β”œβ”€β”€ ai_repository_impl.dart     # Concrete AiRepository (delegates to sources)
β”‚       └── usage_repository_impl.dart  # Concrete UsageRepository (Budget Guardian)
β”‚
└── presentation/
    β”œβ”€β”€ screens/
    β”‚   β”œβ”€β”€ home_screen.dart      # Masonry grid, CategoryFilterChip, AiMagicFab
    β”‚   β”œβ”€β”€ editor_screen.dart    # TextField, MarkdownToolbar, AiOverlayMenu (7 actions)
    β”‚   β”œβ”€β”€ insights_screen.dart  # Dashboard: MoodTrackerCard, TopicDonutChart, highlights
    β”‚   β”œβ”€β”€ folders_screen.dart   # Chronological history of insight snapshots
    β”‚   └── settings_screen.dart  # User preferences (theme, AI, notes, data, about)
    β”œβ”€β”€ widgets/
    β”‚   β”œβ”€β”€ note_card.dart
    β”‚   β”œβ”€β”€ category_filter_chip.dart
    β”‚   β”œβ”€β”€ ai_magic_fab.dart
    β”‚   β”œβ”€β”€ ai_overlay_menu.dart        # 7 AI actions glassmorphic overlay
    β”‚   β”œβ”€β”€ markdown_toolbar.dart
    β”‚   β”œβ”€β”€ mood_tracker_card.dart
    β”‚   β”œβ”€β”€ topic_donut_chart.dart
    β”‚   β”œβ”€β”€ ai_highlight_card.dart
    β”‚   └── stat_card.dart
    β”œβ”€β”€ providers/
    β”‚   β”œβ”€β”€ notes_provider.dart         # notesNotifierProvider, usageRepositoryProvider
    β”‚   β”œβ”€β”€ notes_provider.g.dart
    β”‚   β”œβ”€β”€ ai_provider.dart            # aiOrchestratorProvider, all action notifiers
    β”‚   β”œβ”€β”€ ai_provider.g.dart
    β”‚   β”œβ”€β”€ settings_provider.dart      # settingsProvider (SharedPreferences-backed)
    β”‚   └── settings_provider.g.dart
    └── theme/
        β”œβ”€β”€ app_theme.dart              # Material 3 ThemeData with dynamic_color
        └── app_colors.dart             # Seed color + semantic color tokens

Getting Started

Prerequisites

  • Flutter 3.x SDK (flutter --version)
  • Android Studio or VS Code with the Flutter extension
  • A Google AI Studio API key (get yours free)
  • Android API 24+ recommended (for future Gemma Nano via Android AICore)

The app calls Gemini through a Vercel proxy. The backend repo is kept private to avoid exposing API keys. To run locally, deploy your own proxy or point VERCEL_API_URL to a local server.

Setup

# 1. Clone the repository
git clone https://github.com/Hugo-Dev1/noteai.git
cd noteai/noteai

# 2. Configure environment variables
cp .env.example .env
# Edit .env:
#   GEMINI_API_KEY=your_key_here
#   VERCEL_API_URL=your_vercel_proxy_url
#   VERCEL_API_SECRET=your_proxy_secret

# 3. Install dependencies
flutter pub get

# 4. Generate code (Riverpod providers + Isar schemas)
dart run build_runner build --delete-conflicting-outputs

# 5. Run the app
flutter run

The .env file is loaded by flutter_dotenv and declared as an asset in pubspec.yaml. It is listed in .gitignore and must never be committed.


Cost Engineering

NoteAI is designed for near-zero monthly AI costs. Every cloud call is tracked in Isar and blocked when the $10 hard cap is reached.

Scenario Model Used Estimated Cost
Summarize a short note (< 500 chars) Extractive fallback (Dart) $0.00
Summarize a long note Gemini 1.5 Flash ~$0.00001
Extract tasks from a note Extractive fallback (Dart) $0.00
Change tone (professional) Gemini 1.5 Flash ~$0.00002
Expand / Fix Grammar / Translate / Format Gemini 1.5 Flash ~$0.00001–$0.00003
Generate insights (up to 20 notes) Gemini 1.5 Pro ~$0.001–$0.01

Pricing constants (sourced from TokenCalculator and AppConstants):

  • Gemini 1.5 Flash: $0.075 / 1M input tokens, $0.30 / 1M output tokens
  • Gemini 1.5 Pro: $1.25 / 1M input tokens, $5.00 / 1M output tokens
  • Token estimation: text.length / 4 (standard 4-chars-per-token heuristic for English)

Realistic monthly estimate for daily use: < $1.00


Testing

flutter test

200 tests β€” 200 passing β€” 0 failures across:

  • Domain layer: use cases, AIOrchestrator routing logic
  • Data layer: repository implementations, Budget Guardian
  • Presentation: widget tests, provider state tests

All mocks are written by hand β€” no Mockito dependency.

CI runs on every push via .github/workflows/ci.yml:

  • Security check (.env not committed)
  • flutter analyze --fatal-infos
  • flutter test

What I Would Add Next

  • Stable flutter_gemma integration when Android AICore reaches broad availability
  • MediaPipe TextEmbedder for fully on-device semantic search (using stored embeddings field in NoteModel)
  • Golden tests for all widgets
  • Note export (PDF, Markdown)
  • iPad / tablet responsive layout with adaptive navigation rail

About This Project

NoteAI was built as a portfolio demonstration of senior-level Flutter engineering. Every major technical decision is documented with its rationale in DECISIONS.md.

About

Flutter notes app with hybrid AI routing (on-device + Gemini 2.5 Flash), Clean Architecture, Budget Guardian, and 7 AI-powered writing actions.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages