Flutter Foundations Course

Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.

--

Completed

--

Lessons completed

to track your course
progress

Module 1

Intro and Project Overview

A general introduction to the course and overview of the eCommerce app we will build. As part of this module, I'll share a starter project and give you a walkthrough of the existing code.

01 - What you will learn in this course
3:14
02 - Section Overview
1:15
03 - VSCode Shortcuts, Extensions & Settings for Flutter development
1:44
04 - Join Code With Andrea on Discord
0:48
05 - Course Project on GitHub
2:01
06 - Starter project overview: pubspec.yaml file
5:26
07 - eCommerce app overview
3:38
08 - Starter project overview - project structure
6:25
09 - Exploring the codebase with the Widget Inspector in the DevTools
3:38
10 - UI Design Principles: Composition & Small Widget Classes
5:02
11 - Useful Widgets for Responsive Design
8:24
12 - Localization
3:45
13 - Request for Feedback (optional)

Module 2

Navigation with GoRouter

An introduction to declarative routing in Flutter. In this module, we will migrate the eCommerce app from Navigator 1.0 to the new GoRouter APIs.

01 - Navigation with GoRouter: Section Intro
2:51
02 - Limitations of Navigator 1.0
6:54
03 - GoRouter installation & initial setup with MaterialApp.router
6:27
04 - Routes, sub-routes and navigation
7:43
05 - GoRouterHelper extension and pageBuilder
3:54
06 - Adding some additional routes
3:54
07 - Routing by path vs routing by name
4:35
08 - Routing with parameters
6:30
09 - 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 - Bug fix & wrap up
3:37
15 - Request for Feedback (optional)

Module 3

Flutter App Architecture

This module introduces a scalable app architecture composed of four layers (data, domain, application, and presentation), and covers the project structure that we will use when adding features to our eCommerce app. We also implement our first repository and talk about Future vs Stream-based APIs.

01 - Section Intro
1:56
02 - Popular App Architectures: MVC, MVP, MVVM, Clean Architecture, Bloc
4:15
03 - Riverpod App Architecture with the Controller-Service-Repository Pattern
6:17
04 - Project Structure: Feature-first vs Layer-first
6:23
05 - The Repository Pattern and the Data Layer
4:23
06 - Implementing the 'fake' products repository as a singleton
5:34
07 - Working with Future and Stream-based APIs
6:45
08 - Wrap Up
2:27
09 - Request for Feedback (optional)

Module 4

Flutter Riverpod - Part 1 (Product Listings)

In this module, we get started with Riverpod by covering Providers, Consumers, and modifiers such as 'family' and 'autoDispose'. We also learn how to use Riverpod as a DI system for our project, and get practice working with FutureProvider and StreamProvider when reading data. We also explore couple of advanced features.

01 - Section Intro
3:49
02 - Introduction to Riverpod
4:50
03 - Riverpod installation and setup
3:35
04 - Creating our first provider
4:25
05 - Reading providers with ConsumerWidget and Consumer
7:11
06 - Working with FutureProvider, StreamProvider, and AsyncValue
6:34
07 - Testing AsyncValue by adding a delay
3:24
08 - The family modifier
5:41
09 - The autoDispose modifier + advanced data caching options
9:33
10 - Creating a reusable AsyncValueWidget helper
8:17
11 - Wrap Up + Exercise
2:43
12 - Request for Feedback (optional)

Module 5

Flutter Riverpod - Part 2 (Authentication Flows)

In this module we'll use Riverpod to implement the authentication flows in the app. As part of this, we'll learn about data mutations, how to use controllers to manage the widget state, how to listen to state changes, how to work with redirects with GoRouter, and much more.

01 - Section intro
2:25
02 - Implementing a fake authentication repository
4:36
03 - Creating repositories using abstract classes (optional)
3:58
04 - Intro: a reactive in-memory store with RxDart
3:13
05 - Implementing the InMemoryStore with RxDart
5:02
06 - Using the InMemoryStore in the FakeAuthRepository
4:53
07 - Accessing the FakeAuthRepository with ref.read() in the AccountScreen
3:54
08 - Creating our first controller using StateNotifier
7:46
09 - Using the StateNotifier inside the AccountScreen widget
3:47
10 - Listening to provider state changes with ref.listen()
6:47
11 - Bug-fix for Navigator.pop
2:36
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
3:06
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
2:00
24 - Request for Feedback (optional)

Module 6

Automated Testing - Part 1

Learn how to write unit tests in Flutter. Including how to work with test matchers, futures & streams, how to generate test coverage data, how to mock depedencies, and automate your test workflows with GitHub Actions.

01 - Section intro
1:59
02 - Introduction to Automated Testing and the Testing Pyramid
4:43
03 - Getting started with automated testing
2:28
04 - Writing the first unit test + adding toString() and equality implementations
4:09
05 - Test matchers and working with methods that throw exceptions
6:18
06 - Fixing the getProduct() method and updating the unit tests
3:39
07 - Working with groups and testing Futures and Streams
4:57
08 - Adding an optional delay to the FakeProductsRepository
4:14
09 - 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
23 - Request for Feedback (optional)

Module 7

Automated Testing - Part 2

Learn how to write widget, integration, and golden image tests in Flutter. Also included: how to use finders and the WidgetTester APIs, how to make tests more readable with Robot Testing, how to override providers during testing, how to preload fonts, icons, and image tests, and much more.

01 - Section intro
2:42
02 - Introduction to widget tests + starter project
2:27
03 - Writing our first widget test using pumpWidget()
4:09
04 - Working with WidgetTester and finder
4:38
05 - Robot testing
4:54
06 - How to find widgets by key
3:43
07 - Writing widget tests with mocks and provider overrides
7:54
08 - Writing widget tests with Future.delayed() and runAsync()
4:51
09 - 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:02
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
19 - Request for Feedback (optional)

Module 8

Feature: Shopping Cart

In this module we build the most complex feature of the eCommerce app. After outlining the requirements, we analyze the tradeoffs of various solutions, and proceed to the implementation stage. Along the way, we explore the application layer and learn about some advanced Riverpod features when writing and testing providers.

01 - Section Intro
2:29
02 - Overview of the shopping cart feature + technical requirements
6:29
03 - App Architecture for the shopping cart feature
2:54
04 - Starter project + overview of the data and domain layers
8:47
05 - Local data persistence with Sembast: Initial setup
7:21
06 - How to persist the shopping cart data with the SembastCartRepository
5:26
07 - Implementing the CartService class
7:23
08 - Updating the CartService class to read dependencies using Ref
3:32
09 - Writing unit tests using ProviderContainer
4:31
10 - Writing the unit tests for 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
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
4:38
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
28 - Request for Feedback (optional)

Module 9

Feature: Checkout Flows

In this module we build the checkout flows (registration, payment) for the eCommerce app, implement the client-side logic for placing orders, and add the corresponding unit, widget, and integration tests.

01 - Section Intro
2:03
02 - Starter project for the checkout flows
3:23
03 - Updating the CheckoutScreen with the PageController initialization logic
6:30
04 - Do we need a StateNotifier for the CheckoutScreen?
2:16
05 - Updating the PaymentPage
1:57
06 - Implementing the PaymentButtonController
7:06
07 - Wrap Up
1:11
08 - Request for Feedback (optional)

Module 10

Error Handling

This module is about various strategies for handling errors in Flutter. Inside, we learn how to create exception types using sealed classes (new in Dart 3.0), how to create a robust error handling system, and how to log errors using ProviderObserver. Finally, we briefly cover the topic of functional error handling with Result types vs try/catch.

01 - Section Intro
3:09
02 - Errors vs exceptions
1:51
03 - Starter project overview + defining custom exceptions with enums
5:14
04 - Using sealed classes to define exception types
2:59
05 - Using the AppException sealed class in the FakeAuthRepository
4:12
06 - Adding an AsyncErrorLogger using ProviderObserver
7:30
07 - Creating a reusable ErrorLogger to catch all exceptions
6:22
08 - Completing the error handling system
8:49
09 - Working with the Result type (Success and Error)
8:15
10 - Drawbacks of the Result type (and when not to use it)
2:15
11 - Wrap Up
1:20
12 - Request for Feedback (optional)

Module 11

Feature: Product Reviews

In this module we build a product reviews feature for the eCommerce app. As part of this, we learn how to submit or edit form data, how to calculate the average review score, and discuss some tradeoffs when building reactive UIs.

01 - Section Intro
2:17
02 - Starter project overview
7:25
03 - Overview of the LeaveReviewScreen
3:00
04 - Implemeting a LeaveReviewController and submitting form data
3:09
05 - Testing the LeaveReviewForm and preventing an AssertionError
5:21
06 - Dismissing the LeaveReviewScreen programmatically on success using a callback
3:06
07 - How to prefill a form with data from a repository/backend
6:17
08 - Optimization: only submit the form if the data has changed
3:08
09 - Showing existing reviews in the ProductReviewsList
4:38
10 - Showing the LeaveReviewAction only if the user has purchased the product
5:17
11 - Calculating the average product ratings
4:24
12 - Updated tests & wrap up
3:32
13 - Request for Feedback (optional)

Module 12

Feature: Product Search

In this module we build a product search feature for the eCommerce app. We'll start with an overview of client vs server-side search, then we'll write a search service class, learn how to debounce queries, and discuss tradeoffs when using Futures vs Streams.

01 - Section Intro
2:15
02 - Client-side vs server-side search
2:23
03 - Adding a search method to the FakeProductsRepository
3:26
04 - Implementing client-side search with StateProvider and FutureProvider
7:18
05 - Riverpod caching with autoDispose, keepAlive() and Timer
4:19
06 - Debouncing and cancelling network requests
3:40
07 - Request for Feedback (optional)

Module 13

New Riverpod 2.x APIs & Riverpod Generator

In this module we explore the Riverpod Generator package and how to use it with the new @riverpod syntax. And we also learn about the AsyncNotifier class (introduced in Riverpod 2.0) and refactor our eCommerce app to use the new APIs.

01 - Introduction to Riverpod 2.x
2:10
02 - Starter project and updated code walkthrough
2:12
03 - Installing the Riverpod Generator package
2:40
04 - Generating providers with the @riverpod syntax
5:07
05 - Migrating some more providers to Riverpod Generator + the keepAlive syntax
7:44
06 - Migrating the AccountScreenController from StateNotifier to AsyncNotifier
7:52
07 - Converting the AccountScreenController to use Riverpod Generator
4:16
08 - How to check if an AsyncNotifier is mounted
5:57
09 - How to write unit tests for AsyncNotifier subclasses
6:50
10 - Wrap Up
1:05
11 - Conclusion & Next Steps
1:30

Appendix 1

Common Problems and Solutions

Solutions to common problems including: version conflicts, upgrading dependencies, Cocoapods and Gradle issues.

01 - List of Common Problems and Solutions
02 - Articles & Reference guides
03 - Error: ADB exited with exit code 1 - Requested internal only, but not enough space
04 - Android build.gradle issues
05 - Can't run build_runner due to BuildForInputLogger error
06 - CocoaPods did not set the base configuration of your project because your project already has a custom config set
07 - CocoaPods not installed. Skipping pod install
08 - Enable macOS/Linux desktop support
09 - Extra page shown when swiping back in Safari on iOS
10 - How to recreate the starter project from scratch 💣
11 - How to run CocoaPods on Apple Silicon (M1)
12 - How to deal with localization (multiple arb files) with the feature-first approach
13 - The sandbox is not in sync with the Podfile.lock
14 - Using the Bash or Zsh shell terminal on Windows
15 - The webOnlyWarmupEngine API is deprecated and will be removed in a future release
16 - Xcode sandbox error when building for iOS
17 - October 2022 Update
18 - November 2022 Update
19 - February 2023 Update
20 - May 2023 Update
21 - August 2023 Update
22 - November 2023 Update
23 - May 2024 Update
24 - November 2024 Update