Flutter Foundations Course
(COMPLETE PACKAGE)
Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.
What's Included
→ All 13 modules, totaling 14 hours of video
→ Lifetime Access
→ Bonus Articles
→ Full Source Code
→ Premium Support
→ English Subtitles
→ Completion Certificate
→ 30 Day Money Back Guarantee
Course Overview
Getting started with Flutter is easy enough.
Once you know about the most important widgets, you can start building apps and add features.
But unless you have a robust architecture in place, you’ll soon run into problems and start questioning your decisions:
- This widget does way too much, but where should all this logic go?
- I have bugs! How do I fix them and prevent new ones from appearing?
- I know I should write tests for this code, but I don’t know how. Help!
- Maybe I should just delete everything and start again! But how can I get it right next time?
I know how you feel. I’ve been there myself.
Now, imagine...
- Feeling confident about how to structure your code
- Knowing how to write automated tests for almost any case you can think of
- Having a repeatable process for adding new features, reliably
Sounds good, right?
To help you get there, I’ll guide you through the process of building a real-world eCommerce app in Flutter. And I’ll explain my reasoning and decision-making all along.
You could waste hours or days trying to make sense of it all with some random tutorials.
Or you can learn with the techniques and best practices I learned through years of experience, all distilled into a well-structured curriculum that goes above and beyond what you can find on YouTube or Udemy.
Course Curriculum
- 1. What you will learn in this course (3:14)
- 2. Section overview (1:15)
- 3. VSCode Shortcuts, Extensions & Settings for Flutter development (1:44)
- 4. Join the Discord Server (0:48)
- 5. Course Project on GitHub (2:01)
- 6. Download the Starter Project & pubspec.yaml overview (5:26)
- 7. eCommerce app overview (3:38)
- 8. Project structure & code walkthrough (6:25)
- 9. Exploring the codebase with the Widget Inspector (DevTools) (3:38)
- 10. UI Design Principles: Composition & Reusable Widget Classes (5:02)
- 11. Useful Widgets for Responsive Design (8:24)
- 12. App Localization (3:45)
- Request for Feedback (optional)
- 1. Section Intro (2:51)
- 2. Limitations of Navigator 1.0 (6:54)
- 3. GoRouter installation & initial setup with MaterialApp.router (6:27)
- 4. Routes, sub-routes and navigation (7:43)
- 5. GoRouterHelper Extension and pageBuilder (3:54)
- 6. Adding some additional routes (6:18)
- 7. Routing by path vs routing by name (4:35)
- 8. Routing with parameters (6:30)
- 9. GoRouter Error Handling (3:45)
- 10. Navigating with go vs push (3:40)
- 11. Adding the remaining routes (6:13)
- 12. How to pop a route with GoRouter (4:22)
- 13. Nested Navigation (1:40)
- 14. Wrap up + Exercise (3:38)
- Request for feedback (optional)
- 1. Section Intro (1:56)
- 2. Popular App Architectures: MVC, MVP, MVVM, Clean Architecture, Bloc (4:15)
- 3. Riverpod App Architecture with the Controller-Service-Repository Pattern (6:17)
- 4. Project Structure: Feature-first vs Layer-first (6:23)
- 5. The Repository Pattern and the Data Layer (4:23)
- 6. Implementing the "fake" products repository as a singleton (5:34)
- 7. Working with Future and Stream-based APIs (6:45)
- 8. Wrap Up (2:27)
- Request for Feedback (optional)
- 1. Section Intro (3:49)
- 2. Introduction to Riverpod (4:50)
- 3. Riverpod installation and setup (3:35)
- 4. Creating our first provider (4:25)
- 5. Reading providers with ConsumerWidget and Consumer (7:11)
- 6. Working with FutureProvider, StreamProvider, and AsyncValue (6:34)
- 7. Testing AsyncValue by adding a delay (3:24)
- 8. The family modifier (5:41)
- 9. The autoDispose modifier + advanced data caching options with keepAlive() & Timer (9:33)
- 10. Creating a reusable AsyncValueWidget helper (8:17)
- 11. Wrap Up + Exercise (2:43)
- Request for Feedback (optional)
- 1. Section intro (2:25)
- 2. Implementing a fake authentication repository (4:36)
- 3. Creating repositories using abstract classes (optional) (3:58)
- 4. Intro: a reactive in-memory store with RxDart (3:13)
- 5. Implementing the InMemoryStore with RxDart (5:02)
- 6. Using the InMemoryStore in the FakeAuthRepository (4:53)
- 7. Accessing the FakeAuthRepository with ref.read() in the AccountScreen (3:54)
- 8. Creating our first controller using StateNotifier (7:46)
- 9. Using the StateNotifier inside the AccountScreen widget (3:48)
- 10. Listening to provider state changes with ref.listen() (6:48)
- 11. Bug-fix for Navigator.pop (2:37)
- 12. The AsyncValue.guard method (2:09)
- 13. Adding an AsyncValue extension method (3:28)
- 14. Using the authStateChangesProvider in HomeAppBar (5:29)
- 15. Intro to the email & password sign-in screen (4:52)
- 16. How to generate immutable state classes in Dart (EmailPasswordSignInState) (3:05)
- 17. Using AsyncValue inside EmailPasswordSignInState (2:20)
- 18. Implementing the EmailPasswordSignInController (6:31)
- 19. Using the EmailPasswordSignInController in the widget class (7:26)
- 20. Bug fix + filtering state updates with select() (5:48)
- 21. GoRouter redirects (5:58)
- 22. GoRouter: the refreshListenable argument (5:12)
- 23. Wrap Up + Exercise (2:00)
- Request for Feedback (optional)
- 1. Section Intro (1:59)
- 2. Introduction to Automated Testing and the Testing Pyramid (4:43)
- 3. Getting started with automated testing (2:28)
- 4. Writing the first unit test + adding toString() and equality implementations (4:09)
- 5. Test matchers and working with methods that throw exceptions (6:18)
- 6. Fixing the getProduct() method and updating the unit tests (3:39)
- 7. Working with groups and testing Futures and Streams (4:57)
- 8. Adding an optional delay to the FakeProductsRepository (4:14)
- 9. How to generate a Flutter test coverage report in VSCode (3:41)
- 10. Testing the FakeAuthRepository (part 1) (6:26)
- 11. Testing the FakeAuthRepository (part 2) + advanced stream matchers (7:00)
- 12. Mocks vs Fakes + installing the mocktail package (2:45)
- 13. Testing the AccountScreenController (part 1) + AsyncValue subclasses (4:41)
- 14. Testing the AccountScreenController (part 2) + working with mocks (7:35)
- 15. Testing the AccountScreenController (part 3) + type matchers (3:12)
- 16. Testing with Stream Matchers and Predicates (5:58)
- 17. Testing lifecycle methods (setUp, tearDown, setUpAll, tearDownAll) (4:48)
- 18. Testing the EmailPasswordSignInController with Acceptance Criteria (7:07)
- 19. Testing the EmailPasswordSignInController (part 2) (6:23)
- 20. Tip: setting custom test timeouts per-file (2:07)
- 21. Adding a test workflow to automate testing with GitHub Actions (3:26)
- 22. Wrap up (1:18)
- Request for Feedback (optional)
- 1. Section Intro (2:42)
- 2. Introduction to widget tests + starter project (2:27)
- 3. Writing our first widget test using pumpWidget() (4:09)
- 4. Working with WidgetTester and finder (4:38)
- 5. Robot testing (4:54)
- 6. How to find widgets by key (3:43)
- 7. Writing widget tests with mocks and provider overrides (7:54)
- 8. Writing widget tests with Future.delayed() and runAsync() (4:51)
- 9. Adding the email & password widget tests (6:08)
- 10. Adding the email & password widget tests (part 2) (4:55)
- 11. Test setup for the authentication flow + using pumpAndSettle() (7:03)
- 12. Fixing the RenderFlex overflow error (2:10)
- 13. Completing the authentication flow test (8:42)
- 14. Integration tests (5:30)
- 15. Golden image tests (7:14)
- 16. Running golden image tests with size variants (5:43)
- 17. How to deal with Golden Image tests failing on CI (1:42)
- 18. Wrap Up (2:49)
- Request for Feedback (optional)
- 1. Section Intro (2:29)
- 2. Overview of the shopping cart feature + technical requirements (6:29)
- 3. App Architecture for the Shopping Cart Feature (2:54)
- 4. Starter project + Overview of the data and domain layers (8:47)
- 5. Local Data Persistence with Sembast: Initial Setup (7:21)
- 6. How to persist the shopping cart data with the SembastCartRepository (5:26)
- 7. Implementing the CartService class (7:24)
- 8. Updating the CartService class to read dependencies using Ref (3:32)
- 9. Writing unit tests using ProviderContainer (4:31)
- 10. Writing the unit tests the CartService class (7:41)
- 11. Implementing the AddToCartController (9:07)
- 12. Updating the AddToCartWidget (7:42)
- 13. Bug Fix: Adding autoDispose to the AddToCartController (2:31)
- 14. Showing the cart items in the ShoppingCartScreen + AutoDispose vs AlwaysAlive error when combining providers (5:59)
- 15. Implementing the ShoppingCartItemController (5:18)
- 16. Updating the EditOrRemoveItemWidget and ShoppingCartScreen widgets (4:38)
- 17. Calculating and showing the cart items count (4:06)
- 18. Calculating and showing the cart total price (5:18)
- 19. Limiting the available quantity when adding items to the shopping cart (6:15)
- 20. Implementing the CartSyncService with a listener (5:36)
- 21. Registering the CartSyncService with ProviderContainer when the app starts (3:06)
- 22. Implementing the logic inside the CartSyncService (4:35)
- 23. Implementing the logic inside the CartSyncService (part 2 - optional) (5:22)
- 24. Unit tests for the CartSyncService (11:12)
- 25. Unit-testing providers with dependencies using ProviderContainer (4:04)
- 26. Updated widget and integration tests (3:41)
- 27. Wrap up + exercise (implement a wish list feature) (1:42)
- Request for Feedback (optional)
- 1. Section intro (2:04)
- 2. Starter project for the checkout flows (3:23)
- 3. Updating the CheckoutScreen with the PageController initialization logic (6:30)
- 4. Do we need a StateNotifier for the CheckoutScreen? (2:16)
- 5. Updating the PaymentPage (1:57)
- 6. Implementing the PaymentButtonController (7:06)
- 7. Wrap Up (1:11)
- Request for Feedback (optional)
- 1. Section Intro (3:09)
- 2. Errors vs exceptions (1:51)
- 3. Starter project overview + defining custom exceptions with enums and subclasses (5:14)
- 4. Using sealed classes to define exception types (2:59)
- 5. Using the AppException sealed class in the FakeAuthRepository (4:12)
- 6. Adding an AsyncErrorLogger using ProviderObserver (7:30)
- 7. Creating a reusable ErrorLogger to catch all exceptions (6:22)
- 8. Completing the error handling system (8:49)
- 9. Working with the Result type (Success and Error) (8:15)
- 10. Drawbacks of the Result type (and when not to use it) (2:16)
- 11. Wrap up (1:20)
- Request for Feedback (optional)
- 1. Section Intro (2:17)
- 2. Starter project overview (7:25)
- 3. Overview of the LeaveReviewScreen (3:01)
- 4. Implemeting a LeaveReviewController and submitting form data (5:21)
- 5. Testing the LeaveReviewForm and preventing an AssertionError (5:13)
- 6. Dismissing the LeaveReviewScreen programmatically on success using a callback (3:06)
- 7. How to prefill a form with data from a repository/backend (6:17)
- 8. Optimization: only submit the form if the data has changed (3:08)
- 9. Showing existing reviews in the ProductReviewsList (4:38)
- 10. Updating the LeaveReviewAction by reading read data from the userPurchaseProvider (5:17)
- 11. Calculating the average product ratings (4:24)
- 12. Updated tests & wrap up (3:32)
- Request for Feedback (optional)
- 1. Section Intro (2:15)
- 2. Client-side vs server-side search (2:23)
- 3. Adding a search method to the FakeProductsRepository (3:26)
- 4. Implementing client-side search with StateProvider and FutureProvider (7:18)
- 5. Riverpod caching with autoDispose and cacheTime (4:19)
- 6. Debouncing and cancelling network requests (3:40)
- Request for Feedback (optional)
- 1. Introduction to Riverpod 2.x (2:11)
- 2. Starter project and updated code walkthrough (2:12)
- 3. Installing the Riverpod Generator package (2:40)
- 4. Generating providers with the @riverpod syntax (5:08)
- 5. Migrating some more providers to Riverpod Generator + the keepAlive syntax (7:44)
- 6. Migrating the AccountScreenController from StateNotifier to AsyncNotifier (7:53)
- 7. Converting the AccountScreenController to use Riverpod Generator (4:17)
- 8. How to check if an AsyncNotifier is mounted (5:57)
- 9. How to write unit tests for AsyncNotifier subclasses (6:50)
- 10. Wrap Up (1:05)
- 11. Conclusion & Next Steps (1:30)
- Request for Feedback (optional)
I spent over 800 hours making this a high-quality course and I'm confident you'll get a lot of value out of it.
But the only way to find out if this course is right for you is on the inside, not on the outside. So you get on the inside and see if it’s true and valuable to you. Then, if it is, that’s when you decide to keep it.
But if you're not happy with it, for any reason, you can reach out within 30 days of purchase to get a full refund. All you have to do is go to [email protected] and tell me “gimme my money back” and you got it, and in short order. My response time is usually 24 hours.
Who is this course for?
This is an intermediate to advanced-level course, going above and beyond what you can find on YouTube or Udemy.
To make the most of it, you'll need to be already familiar with the Dart language (my Dart course can help with that) and how to build apps with Flutter.
The sections about state management and navigation will use the Riverpod and go_router packages, starting from the basics (it's ok if you haven't used them before).
Frequently Asked Questions
How long is the course?
The course will include over 10 hours of in-depth video, focusing on quality content and code.
Is the source code included?
Of course! You'll get the full source code for the entire course project, along with all the additional materials.
Will you cover any state management packages other than Riverpod?
I'll include a brief overview of alternative techniques so that I can highlight the differences with Riverpod.
But we'll develop the course project using Riverpod only.
Will you cover any navigation packages other than GoRouter?
No. There is a lot to cover already, so we'll focus on going from the Navigator 1.0 API to GoRouter, and explain why it's more convenient than Navigator 2.0.
What if I get stuck? Can I ask questions?
Yes. The course includes premium support on Slack and I aim to answer all questions within 24 hours.
What is my teaching style?
My courses are very practical, clear, and concise - just like all the tutorials on my YouTube channel. I always explain what we're going to build and why, and then how. You'll leave this course with a deeper understanding of how things work and will be able to apply this knowledge to your own apps.
What if I don't like the course?
The course comes with a 30 day money back guarantee. If you're not happy with the course, for any reason, you can reach out and I'll issue a full refund.
Will I get a completion certificate?
Sure do! Once you complete the course this will be issued to you automatically.
I have another question!
Sure - email me at [email protected] and I'll reply within 48 hours.
About Me
My name is Andrea, I'm a Flutter GDE and I've been writing code professionally for over 12 years. I've been a mobile app developer since 2012, working for startups, agencies, and big companies.
With my Flutter tutorials and courses, I've helped thousands of students become better developers.
I know what it takes to become a great Flutter developer. I can't wait to share this course with you and help you fast-track your learning.