This is the official repo for this course:
This will include a full-stack eCommerce app using Flutter & Firebase:
A Flutter web preview of the app can be found here:
To clone the repo for the first time and open it in VSCode, run this:
git clone https://github.com/bizz84/complete-flutter-course.git
cd complete-flutter-course
code .
This will checkout the main branch which contains the latest code.
But at various points in the course, I'll ask you to checkout a specific branch name, so you can follow along with the right code, at the right time.
And to prevent any conflicts, you may need to reset your local changes:
git reset --hard HEAD
git checkout <branch-name>
If the course project doesn't compile or run for you, there are a number of things to look out for.
I'm keeping a list of all the FAQs here:
- What you will learn in this course
 - Section overview
 - VSCode Shortcuts, Extensions & Settings for Flutter development
 - Join the Slack Channel
 - Course Project on GitHub
 - Download the Starter Project & 
pubspec.yamloverview - eCommerce app overview
 - Code walkthrough: project structure
 - Exploring the codebase with the Widget Inspector (DevTools)
 - UI Design Principles: Composition & Reusable Widget Classes
 - Useful Widgets for Responsive Design
 - App Localization
 
- Section Intro
 - Limitations of Navigator 1.0
 - GoRouter installation & initial setup with 
MaterialApp.router - Routes, sub-routes and navigation
 GoRouterHelperExtension andpageBuilder- Adding some additional routes
 - Routing by path vs routing by name
 - Routing with parameters
 - GoRouter error handling
 - Navigating with go vs push
 - Adding the remaining routes
 - How to pop a route with GoRouter
 - Nested Navigation
 - Bug fix & wrap up
 
- Section Intro
 - Popular App Architectures: MVC, MVP, MVVM, Clean Architecture, Bloc
 - Riverpod App Architecture with the Controller-Service-Repository Pattern
 - Project Structure: Feature-first vs Layer-first
 - The Repository Pattern and the Data Layer
 - Implementing the "fake" products repository as a singleton
 - Working with Future and Stream-based APIs
 - Wrap Up
 
- Section Intro
 - Introduction to Riverpod
 - Riverpod installation and setup
 - Creating our first provider
 - Reading providers with 
ConsumerWidgetandConsumer - Working with 
FutureProvider,StreamProvider, andAsyncValue - Testing 
AsyncValueby adding a delay - The 
familymodifier - The 
autoDisposemodifier + advanced data caching options - Creating a reusable 
AsyncValueWidgethelper - Wrap Up + Exercise
 
- Section intro
 - Implementing a fake authentication repository
 - Creating repositories using abstract classes (optional)
 - Intro: a reactive in-memory store with RxDart
 - Implementing the 
InMemoryStorewith RxDart - Using the 
InMemoryStorein theFakeAuthRepository - Accessing the 
FakeAuthRepositorywithref.read()in theAccountScreen - Creating our first controller using 
StateNotifier - Using the 
StateNotifierinside theAccountScreenwidget - Listening to provider state changes with 
ref.listen() - Bug-fix for 
Navigator.pop - The 
AsyncValue.guardmethod - Adding an 
AsyncValueextension method - Using the 
authStateChangesProviderinHomeAppBar - Intro to the email & password sign-in screen
 - How to generate immutable state classes in Dart
 - Using 
AsyncValueinsideEmailPasswordSignInState - Implementing the 
EmailPasswordSignInController - Using the 
EmailPasswordSignInControllerin the widget class - Bug fix + filtering state updates with 
select() - GoRouter redirects
 - GoRouter: the 
refreshListenableargument - Wrap Up
 
- Section Intro
 - Introduction to Automated Testing and the Testing Pyramid
 - Getting started with automated testing
 - Writing the first unit test + adding 
toString()and equality implementations - Test matchers and working with methods that throw exceptions
 - Fixing the 
getProduct()method and updating the unit tests - Working with groups and testing Futures and Streams
 - Adding an optional delay to the 
FakeProductsRepository - How to generate a Flutter test coverage report in VSCode
 - Testing the 
FakeAuthRepository(part 1) - Testing the 
FakeAuthRepository(part 2) + advanced stream matchers - Mocks vs Fakes + installing the mocktail package
 - Testing the 
AccountScreenController(part 1) +AsyncValuesubclasses - Testing the 
AccountScreenController(part 2) + working with mocks - Testing the 
AccountScreenController(part 3) + type matchers - Testing with Stream Matchers and Predicates
 - Testing lifecycle methods (
setUp,tearDown,setUpAll,tearDownAll) - Testing the 
EmailPasswordSignInControllerwith acceptance criteria - Testing the 
EmailPasswordSignInController(part 2) - Tip: setting custom test timeouts per-file
 - Adding a test workflow to automate testing with GitHub Actions
 - Wrap up
 
- Section Intro
 - Introduction to widget tests + starter project
 - Writing our first widget test using 
pumpWidget() - Working with 
WidgetTesterand finder - Robot testing
 - How to find widgets by key
 - Writing widget tests with mocks and provider overrides
 - Writing widget tests with 
Future.delayed()andrunAsync() - Adding the email & password widget tests
 - Adding the email & password widget tests (part 2)
 - Test setup for the authentication flow + using 
pumpAndSettle() - Fixing the RenderFlex overflow error
 - Completing the authentication flow test
 - Integration tests
 - Golden image tests
 - Running golden image tests with size variants
 - How to deal with holden image tests failing on CI
 - Wrap Up
 
- Section Intro
 - Overview of the shopping cart feature + technical requirements
 - App Architecture for the shopping cart feature
 - Starter project + overview of the data and domain layers
 - Local data persistence with Sembast: Initial setup
 - How to persist the shopping cart data with the 
SembastCartRepository - Implementing the 
CartServiceclass - Updating the 
CartServiceclass to read dependencies usingRef - Writing unit tests using 
ProviderContainer - Writing the unit tests for the 
CartServiceclass - Implementing the 
AddToCartController - Updating the 
AddToCartWidget - Bug Fix: Adding 
autoDisposeto theAddToCartController - Showing the cart items in the 
ShoppingCartScreen+ AutoDispose vs AlwaysAlive error when combining providers - Implementing the 
ShoppingCartItemController - Updating the 
EditOrRemoveItemWidgetandShoppingCartScreenwidgets - Calculating and showing the cart items count
 - Calculating and showing the cart total price
 - Limiting the available quantity when adding items to the shopping cart
 - Implementing the 
CartSyncServicewith a listener - Registering the 
CartSyncServicewithProviderContainerwhen the app starts - Implementing the logic inside the 
CartSyncService - Implementing the logic inside the 
CartSyncService(part 2 - optional) - Unit tests for the 
CartSyncService - Unit-testing providers with dependencies using 
ProviderContainer - Updated widget and integration tests
 - Wrap up + exercise (implement a wish list feature)
 
- Section intro
 - Starter project for the checkout flows
 - Updating the 
CheckoutScreenwith thePageControllerinitialization logic - Do we need a 
StateNotifierfor theCheckoutScreen? - Updating the 
PaymentPage - Implementing the 
PaymentButtonController - Wrap Up
 
- Intro
 - Errors vs exceptions
 - Starter project overview + defining custom exceptions with enums
 - Using sealed classes to define exception types
 - Using the 
AppExceptionsealed class in theFakeAuthRepository - Adding an 
AsyncErrorLoggerusingProviderObserver - Creating a reusable 
ErrorLoggerto catch all exceptions - Completing the error handling system
 - Working with the 
Resulttype (SuccessandError) - Drawbacks of the 
Resulttype (and when not to use it) - Wrap Up
 
- Section Intro
 - Starter project overview
 - Overview of the 
LeaveReviewScreen - Implemeting a 
LeaveReviewControllerand submitting form data - Testing the 
LeaveReviewFormand preventing anAssertionError - Dismissing the 
LeaveReviewScreenprogrammatically on success using a callback - How to prefill a form with data from a repository/backend
 - Optimization: only submit the form if the data has changed
 - Showing existing reviews in the 
ProductReviewsList - Updating the 
LeaveReviewActionby reading read data from theuserPurchaseProvider - Calculating the average product ratings
 - Updated tests & wrap up
 
- Section Intro
 - Client-side vs server-side search
 - Adding a search method to the 
FakeProductsRepository - Implementing client-side search with 
StateProviderandFutureProvider - Riverpod caching with 
autoDispose,keepAlive()andTimer - Debouncing and cancelling network requests
 
- Introduction to Riverpod 2.x
 - Starter project and updated code walkthrough
 - Installing the Riverpod Generator package
 - Generating providers with the 
@riverpodsyntax - Migrating some more providers to Riverpod Generator + the 
keepAlivesyntax - Migrating the 
AccountScreenControllerfromStateNotifiertoAsyncNotifier - Converting the 
AccountScreenControllerto use Riverpod Generator - How to check if an 
AsyncNotifieris mounted - How to write unit tests for 
AsyncNotifiersubclasses - Wrap Up
 - Conclusion & Next Steps
 
