This repository is the source of truth for the AppForge Flutter workshop. It contains the finished Expense Tracker app, built from the student starter in many small, teachable Git commits.
Screen-share this repo during the workshop and walk the commits in order.
The student starter repo (
appforge-expense-tracker-student) is exactly this repo's first commit, minus this README andWORKSHOP_GUIDE.md.
# see the whole plan
git log --oneline
# jump to the start
git checkout checkpoint-01-project-setup
# step forward one teachable change at a time
git checkout HEAD@{1} # or check out the next commit hash
# show the room what changed in a commit
git show <commit> # diff + message
git diff <commitA> <commitB> # diff between any two pointsA comfortable rhythm per commit:
git show <commit>— read the diff together.- Explain why those lines were added (see
WORKSHOP_GUIDE.md). flutter run(hot-reload) so everyone sees the effect.- Students make the same change in their copy.
The Dart code is here; the platform folders are not. Generate them once:
flutter create .
flutter pub get
flutter run| Tag | Section |
|---|---|
checkpoint-01-project-setup |
Flutter project structure + app theme |
checkpoint-02-dart-model |
Dart fundamentals via the Expense model |
checkpoint-03-dashboard |
The dashboard / home screen |
checkpoint-04-expense-form |
The "Add Expense" form |
checkpoint-05-functionality |
Adding, deleting, live totals |
checkpoint-06-final-ui |
Empty states + visual polish |
Check one out with git checkout checkpoint-03-dashboard.
git log --oneline --reverse gives the authoritative list. Summary:
feat: add a reusable app theme— one file that holds the app's colours; wire it intoMaterialApp.
Concepts: lib/ layout, main.dart, runApp, MaterialApp, StatelessWidget,
the widget tree, ThemeData / ColorScheme.fromSeed.
feat: create the Expense model with a title and amountfeat: add a date to the Expense modelfeat: add an expense category enumfeat: give each category a label/... an iconfeat: add some sample expense datafeat: add currency and date formatting helpers
Concepts: classes, final fields, constructors, named parameters,
required, types (String, double, DateTime), enums, switch,
extensions, List, functions, string interpolation, null-aware ??.
refactor: turn the dashboard into a StatefulWidgetfeat: keep the list of expenses in dashboard statefeat: give the dashboard a scrollable layoutfeat: calculate total spendingfeat: add the TotalSpendingCard widgetfeat: add the ExpenseCard widgetfeat: show every expense with a for-loopfeat: add a "Recent expenses" headingfeat: show a simple category breakdown on the dashboardrefactor: move the category breakdown into its own widget
Concepts: StatefulWidget vs StatelessWidget, State, setState, getters,
for loops, accumulating a total, Map, Column/Row/ListView,
Container/BoxDecoration, extracting a widget, passing data via constructors.
refactor: turn the Add Expense screen into a StatefulWidgetfeat: add a description field to the formfeat: add an amount field to the formfeat: add a category dropdown to the formfeat: add a date picker to the formfeat: add a Save button to the formfeat: validate the form fields
Concepts: Form + GlobalKey<FormState>, TextEditingController, dispose,
TextFormField, keyboardType, DropdownButtonFormField, showDatePicker,
Future/async/await, validator functions, double.tryParse.
feat: return the new expense when the form is savedfeat: add the new expense to the listfeat: let the ExpenseCard report a delete tapfeat: delete an expense from the dashboard
Concepts: Navigator.push / pop with a return value, building an object from
form input, setState to add/remove from a List, callback parameters
(VoidCallback), why the totals recompute "for free" (getters + rebuild).
feat: show a message when there are no expensesfeat: hide the category section until there is spendingstyle: add icons and spacing to the category breakdownstyle: refine the expense card appearancetest: check the dashboard renders its sectionsdocs: note the finished app in the README
Concepts: conditional widgets (if/else in a child list), empty states,
SizedBox.shrink, where on an iterable, small visual refinements, a basic
widget test.
- Dashboard: total spending card, per-category breakdown, recent expenses list.
- Add Expense: description, amount, category, date, with validation.
- Add and delete expenses; all totals update live.
- Friendly empty state.
- In-memory data only — no backend, no database, no state-management packages.
See WORKSHOP_GUIDE.md for the per-commit teaching table.
🤖 Generated with Claude Code