LexiGrow: a clinical-grade tracker for my kids' first words
When you have a baby, you start writing down the words. "Dada" got logged on a sticky note. "More" went into the Notes app. By the time you have a toddler you have words in four places, none of them queryable, none of them comparable to anything clinical.
LexiGrow is the app I built so my wife and I would stop losing track. Then I realized the proper version of this tool already existed in the speech-language pathology world — it's just not consumer-facing. So I rebuilt it from the spec.
MB-CDI as the foundation
The MacArthur-Bates Communicative Development Inventories are the standard parent-report instrument for early language acquisition. Two relevant forms:
- Words & Gestures — 8–18 months, 396 vocabulary items + a gestures inventory
- Words & Sentences — 16–30 months, expanded vocabulary + early grammar
LexiGrow ships with both vocabulary banks bundled as JSON (assets/data/mbcdi_vocabulary.json, assets/data/mbcdi_gestures.json). You don't track free-text words — you check off MB-CDI items as your child produces them. That means the data is directly comparable to clinical norms, not just a personal log.
The stack
Framework Flutter 3.27+ / Dart 3.6+
Local DB Isar (offline-first, fast, type-safe)
State / arch BLoC, feature-first
Theming Material 3 light + dark
Future backend Firestore for sync (Phase 8 of the modernization plan)
Compliance COPPA/GDPR services in core/compliance/
The project layout is feature-first so each module owns its own state:
lib/
├── main.dart
├── core/
│ ├── compliance/ COPPA/GDPR services
│ └── theme/ Material 3 light + dark
├── data/
│ ├── models/ Isar collections (VocabularyItem,
│ │ GestureItem,
│ │ ChildProfile)
│ └── repositories/ Isar-backed data access
└── features/ each feature owns its BLoC
├── analytics/
├── gestures/
├── home/
├── onboarding/
├── reports/
├── vocabulary/
└── quick_add/ voice + photo input
The data model is two tables and an inventory: VocabularyItem, GestureItem, and a ChildProfile. Everything else — frequency-of-use, age-at-first-production, comprehension-vs-production split — derives from those.
Bilingual children
This is where consumer apps fall over. If your kid says "dog" in English and "perro" in Spanish, those are not two vocabulary items — they're one concept. The MB-CDI norms only work if you count conceptual vocabulary, not surface form.
I baked that algorithm in. The doc is at design/bilingual_logic.md in the repo. The short version:
- Every MB-CDI item has a concept ID
- Each child profile has 1–N spoken languages
- A vocabulary entry is
(concept_id, language, age_at_first_production) - Comprehension and production are scored at the concept level, not the surface form
That single decision means LexiGrow can give a bilingual kid a fair number against the norms instead of penalizing them for speaking two languages.
What's hard about this
The hardest part is not over-medicalizing it. Parents don't want a clinical assessment tool that makes them anxious about every milestone. The MB-CDI is a percentile system; it's normal for a 14-month-old to be at the 30th percentile and a 22-month-old to be at the 90th. LexiGrow has to communicate that without either downplaying real concerns or generating false alarms.
The current design is:
- Show progress against age-band norms, never against a single rigid line
- Surface percentile ranges, not a single number
- Explicitly flag the "this is a wide range" framing in onboarding
- Provide a "share with your pediatrician" report — the only output styled like a clinical artifact
If a parent wants the clinical output, they can ask for it. If they want the encouraging output, that's the default.
What's next
Phase 1 of the modernization plan adds integration tests. Phase 5 renames voice/ to quick_add/ and consolidates the photo + voice inputs. Phase 8 adds Firestore sync for multi-device families. After that, the obvious next thing is a longitudinal report — a year-by-year report card you can keep across siblings.
This was the first Flutter project I'd shipped to a place where it actually had to feel like a native iOS app. Material 3 helps. The remaining iOS-feels-different work is mostly haptics, swipe-back, and the photo picker — and the photo picker is half of why God invented image_picker_ios.
If you're a parent who wants to log first words properly, or a clinician who's tired of recommending tools that don't use the MB-CDI, the repo lives on my GitHub.
