This course was created with the
course builder. Create your online course today.
Start now
Create your course
with
Autoplay
Autocomplete
Previous Lesson
Complete and Continue
Flutter & Firebase: Build a Complete App for iOS & Android
Course Introduction
Course Introduction (1:50)
Course Content (2:01)
App Overview (3:24)
Making the most of this course (2:35)
Course Resources & Q/A
What is Flutter (3:04)
Introduction to Dart
The Dart Language (2:25)
Introduction to Dartpad (0:48)
A simple program (1:42)
Variable declaration and initialization (1:43)
String interpolation (3:03)
Type inference with var (3:13)
Var and final (1:54)
The dynamic keyword (1:53)
Introduction to functions (4:18)
Function return types (2:05)
Optional parameters, nullability and default values (2:31)
Named parameters (1:58)
The arrow operator (1:51)
Introduction to classes (2:01)
Class constructors (3:05)
Instance methods (2:14)
Inheritance (2:56)
The super constructor (1:59)
The base Object class and the toString method (1:48)
Overriding the toString method (5:25)
Abstract classes (3:21)
More on abstract classes (2:33)
Using abstract classes with functions (1:54)
Computed properties (2:39)
Mixins (4:37)
Introduction to lists (3:43)
Introduction to maps (3:31)
Generics and type annotations (4:53)
If and else statements (3:51)
The ternary operator (2:25)
The while loop (3:13)
The for loop (1:59)
Closures and the fold method (4:12)
Enumerations (2:45)
Switch statements (2:49)
Wrap-up (1:11)
Useful Links & Resources
Flutter setup on macOS
Flutter setup on macOS (2:39)
Setting the PATH variable (4:26)
Flutter doctor (0:50)
Xcode and iOS simulator setup (2:17)
Installing Android Studio (5:54)
Installing the Android emulator (2:20)
Running Flutter from the command line (5:21)
Flutter setup on Android Studio (3:35)
Installing Visual Studio Code (3:11)
Useful Links & Resources
Flutter setup on Windows
Flutter setup on Windows (1:27)
Updating the path variable (2:09)
Flutter doctor (0:54)
Installing Android Studio (4:27)
Installing the Android emulator (3:18)
Running Flutter from the command line (2:57)
Flutter setup on Android Studio (4:19)
Installing Visual Studio Code (2:52)
Useful Links & Resources
Introduction to Flutter
Creating a Flutter project with Android Studio (3:28)
ACTION REQUIRED: Flutter 2.0 and Null Safety
A tour of the project folders (2:40)
Running the Android emulator and iOS simulator (2:09)
Overview of the Flutter counter app (1:08)
Hot reload and hot restart (4:06)
Introduction to widgets (2:22)
The MaterialApp widget (1:51)
The Scaffold widget (2:37)
The Flutter widget tree (3:25)
Stateless and stateful widgets (2:42)
Updating the counter with setState (2:10)
Wrap up (1:10)
Useful Links & Resources
Shortcuts for VS Code and Android Studio
Building Layouts
Overview of the Time tracker app (1:42)
Switching between apps (0:54)
Writing the root widget of the app (4:07)
Adding the MaterialApp (2:50)
Adding some folders to our project (2:19)
Adding a sign-in page (3:35)
The ThemeData class (2:12)
The AppBar widget (2:38)
Preview of the SignInPage layout (1:27)
Adding a Column layout (3:17)
The CrossAxisAlignment property (3:55)
Code formatting with dartfmt (4:53)
Adding some boxes and extracting code into a method (3:39)
Private methods (3:25)
Adding some padding (4:33)
The MainAxisAlignment property (1:14)
Text, TextStyle and FontWeight (3:12)
Introduction to buttons (1:49)
Adding the first button (3:29)
Button callbacks explained (4:14)
Customising button colors (3:09)
MaterialColor explained (3:08)
Changing button shapes (2:16)
Making code reusable (1:24)
Creating a reusable custom RaisedButton (6:39)
Creating a reusable SignInButton (4:13)
Setting default values (2:22)
Making the button height configurable (3:55)
Adding the remaining buttons (4:36)
Adding logos: introduction (1:17)
Updating the pubspec.yaml file (4:02)
Image variants (1:53)
Adding an image inside a button (3:33)
Arranging widgets horizontally in a Row (4:27)
The Opacity widget (2:39)
Creating a custom SocialSignInButton (5:58)
The @required annotation (5:03)
Using assertions for better widget API design (9:02)
Useful Links & Resources
Firebase Authentication
Local and remote authentication (3:56)
Introduction to Firebase (1:33)
Creating a Firebase project (2:03)
Configuring Firebase for Android (8:58)
How to fix: "'keytool' is not recognized as an internal or external command"
Configuring Firebase for iOS (7:22)
Installing the firebase_core and firebase_auth packages (3:22)
Initializing the Firebase App (3:08)
Running on iOS and updating Cocoapods (1:25)
Futures, async and await (2:00)
Signing in anonymously with Firebase (4:15)
The FirebaseAuth singleton and private constructors (3:06)
Explaining the short-hand syntax for callbacks (1:25)
Error handling with try/catch (2:43)
Useful Links & Resources
Recommendations about choosing and updating packages
Full Authentication Flow, State Management & Dependency Injection
Preview of the sign-in and sign-out flow (3:09)
Creating a landing page widget (2:21)
Adding a Firebase User to the LandingPage (1:43)
Adding a callback to the SignInPage (2:54)
Hooking up the onSignIn callback (3:05)
Creating the home page (4:02)
Adding the sign-out functionality (2:46)
Hooking up the onSignOut callback (2:34)
Retrieving the current user when the app starts (2:11)
Explaining global access and scoped access (5:35)
Creating the Auth class (2:38)
The abstract AuthBase class (3:03)
Using the Auth class (4:36)
Lifting state up and its drawbacks (4:38)
State Management & App Architecture (2:30)
Streams and StreamBuilder
Introduction to Streams (2:43)
Streams in practice with DartPad (4:01)
Handling errors and closing streams (4:51)
The authStateChanges stream (2:19)
Listening to the authStateChanges stream (2:36)
Adding the StreamBuilder code (4:33)
More on StreamBuilder (3:08)
Refactoring the sign-in flows (2:41)
Wrap-up on Streams and StreamBuilder (2:57)
Useful Links & Resources
Google and Facebook sign-in
Overview of the Firebase sign-in methods (1:59)
Enabling support for Google Sign In (2:29)
Adding Google Sign-In to the Auth class (5:14)
Hooking up Google Sign-In to our button (1:34)
Configuring Google Sign-In on iOS (3:48)
Google Sign-In flow explained (2:50)
Supporting Google Sign Out (3:01)
Testing Google Sign-In on Android (2:56)
Checklist: Google Sign-In Flutter setup on Android
Viewing registered users on the Firebase console (0:46)
Registering a Facebook App (7:32)
Enabling Facebook Sign-In on Firebase (2:28)
Installing the Facebook login package (1:23)
Enabling MultiDex support on Android (1:37)
Adding the Facebook Sign-In code (4:35)
Testing Facebook Sign-In on Android (3:35)
Facebook iOS setup in Xcode (4:39)
Testing Facebook Sign-In on iOS (2:10)
Accessing the user's data and privacy considerations (2:56)
Useful Links & Resources
Email & Password Sign-In + Handling Text Input
Preview of the email & password sign-in page (2:20)
Creating the email & password sign-in page (1:59)
Passing the BuildContext across methods (1:48)
Introduction to navigation (5:20)
Adding a Card widget (2:03)
Adding the email and password text fields (5:17)
Adding the submit buttons (2:21)
Creating a FormSubmitButton widget (3:31)
Adding a TextEditingController (5:23)
Toggling the form type (6:21)
Adding the email & password authentication code (4:14)
Implementing the submit method (2:59)
Testing email & password sign-in (2:55)
Customising the email and password text fields (3:32)
Using FocusNode and FocusScope (4:47)
Disabling the submit button on empty email or password (5:30)
Adding a StringValidator class (2:28)
Adding an email and password validation mixin (2:50)
Showing an error text when the email or password are invalid (3:16)
Tweaking form submission (4:45)
Simulating a slow network with a delay (2:51)
Adding a loading state to our form (3:35)
Updating the email focus logic (2:14)
Fixing the vertical overflow on small screens (3:36)
Wrap-up (4:41)
Useful Links & Resources
Platform-aware widgets and dialogs
Introduction to dialogs (1:18)
Showing a dialog (2:49)
Dismissing dialogs (2:14)
Platform-aware widgets on iOS, Android & more (4:02)
Adding a reusable showAlertDialog function (4:47)
Adding a sign-out confirmation dialog (4:20)
Dialog differences on Android and iOS (2:00)
Useful Links & Resources
Scoped Access with InheritedWidget and Provider
Introduction to InheritedWidget (3:35)
Creating an AuthProvider (5:41)
Accessing the Auth object via the AuthProvider (6:22)
Adding the provider package (1:41)
Using the Provider class (4:55)
Wrap-up about scoped access and Provider (4:08)
Useful Links & Resources
Polishing the Authentication flows
Module Introduction (1:08)
Creating better user-facing errors with FirebaseAuthException (2:39)
Creating a custom exception alert dialog (3:02)
Showing error alerts in the SignInPage (3:15)
Adding a loading state: overview (1:11)
Adding a loading state to the SignInPage (2:32)
Using the loading state in the SignInPage (3:35)
The dispose method (2:57)
BLoCs
Introduction to state management with BLoCs (4:49)
The application layers (1:40)
BLoCs, sinks, streams, and asynchronous code (4:52)
Introduction to the SignInBloc (2:50)
Implementing a simple BLoC (3:26)
Adding a Bloc with Provider inside a static method (2:37)
Adding the StreamBuilder code (2:47)
Converting the SignInPage to a stateless widget (2:56)
The difference between Provider.of and Consumer (3:15)
Disposing BLoCs with Provider (1:03)
Adding authentication code to the SignInBloc (5:47)
Updating the SignInPage (2:13)
Fixing the BLoC submit method (2:55)
Summary on the BLoC basics (1:15)
Introduction to the email sign-in flow with BLoC (2:35)
Creating a model class for the EmailSignInForm (2:48)
Creating the EmailSignInBloc with a StreamController (2:31)
Updating the model (6:38)
Adding the BLoC submit method (5:19)
Setting up the EmailSignInFormBlocBased with Provider (5:05)
Refactoring the EmailSignInFormBlocBased widget by removing the state variables (7:17)
Moving the business logic to the BLoC class (3:35)
Moving more business logic to the model class (6:31)
The benefits of separation of concerns with BLoC (2:59)
Using stateful widgets with TextEditingControllers (3:35)
Considerations about performance (2:05)
Blocs and Services in the widget tree (3:37)
State Management with Provider
Recap on State Management (2:23)
Introduction to ValueNotifier (2:06)
Adding a ValueNotifier with ChangeNotifierProvider (4:50)
Consumer and ChangeNotifierProvider explained (3:23)
Differences between BLoC/streams and ValueNotifier/ChangeNotifierProvider (2:53)
Introduction to ChangeNotifier (1:25)
Adding the EmailSignInChangeModel class (4:47)
Completing the EmailSignInChangeModel class (2:42)
Implementing the email sign-in form with ChangeNotifier (5:31)
Comparing ValueNotifier and ChangeNotifier (3:22)
Wrap up on State Management (3:45)
Wrap up on the Authentication Flows (2:05)
Useful Links & Resources
Databases and Cloud Firestore
Overview of the time tracker app (2:37)
Database schema and SQL vs NoSQL (4:27)
Introduction to Cloud Firestore (2:26)
Documents and Collections (2:00)
Getting started with Firestore (3:19)
Designing a Database API with CRUD operations (3:21)
Managing private user data with Cloud Firestore (3:21)
Installing Cloud Firestore (1:49)
Renaming the HomePage to JobsPage (2:30)
Adding the Database class (3:01)
Adding the Database Provider (1:30)
Adding a FloatingActionButton (2:17)
Writing data to Firestore (6:50)
Defining a strongly-typed Job model class (4:02)
Defining a common API path class (2:50)
Adding a generic setData method (2:42)
Adding security rules (4:52)
Handling Firestore permissions errors (2:45)
Reading data from Firestore (4:56)
Reading and parsing Firestore data streams (4:01)
Adding a StreamBuilder to show a list of jobs (4:08)
Debugging the StreamBuilder code (4:51)
Firestore as a realtime database (2:07)
Adding a factory constructor to our model class (2:49)
Adding a generic method to read Firestore streams (3:00)
Adding a FirestoreService class (4:26)
Wrap-up on Cloud Firestore (3:14)
Useful Links & Resources
Working with Forms and Cloud Firestore
Introduction to Forms with Cloud Firestore (1:50)
Adding a new job page (4:23)
The Placeholder widget (3:59)
Introduction to Form and TextFormField (3:40)
Validating and saving Form data (8:03)
Accessing the Database object with the correct BuildContext (4:41)
Saving jobs with a unique document ID (4:53)
Handling errors (1:39)
Enforcing unique job names (3:03)
Fixing the integer-parsing code (1:26)
Editing existing jobs: overview (1:15)
Adding a custom JobListTile (3:50)
Repurposing the AddJobPage for editing jobs (4:46)
Reading the documentID from Firestore (2:49)
Completing the code for editing jobs (4:17)
Wrap up on working with Forms (2:46)
Useful Links & Resources
Working with ListViews and multiple UI states
Intro and multiple states of UI (1:52)
Adding an empty content widget (3:30)
Adding a reusable list items builder (4:45)
Using ListView.builder (4:51)
Using ListView.separated (2:49)
Deleting jobs from Firestore (2:27)
Adding swipe to delete support (6:29)
Working with Date & Time Pickers, more on Cloud Firestore
Working with entries: overview (1:50)
Relational data & drawbacks of NoSQL databases (4:18)
Getting ready to add new files (1:20)
Adding the source files to the project (3:02)
Connecting the new code and updating the Firestore rules (2:13)
Fixing the EditJobPage navigation (3:01)
Overview of the JobEntriesPage (2:28)
Reading and writing entries with Firestore (5:25)
The EntryListItem widget (using InkWell and Expanded) (2:04)
Formatting dates and currencies with the Intl package (2:30)
Dart as UI: Spreads and Collection-if (2:53)
Using date pickers with stateful widgets (5:27)
Date and time input with a custom UI and DateTimePicker (4:06)
Updating the UI when a Job changes (5:10)
Wrap up and CupertinoDatePicker (1:24)
Useful Links & Resources
Bottom Navigation with the Cupertino widgets
Introduction to bottom navigation (2:00)
Multiple navigation stacks (1:45)
Creating a HomePage with a selected tab (3:56)
Adding a CupertinoTabScaffold (6:37)
Testing the bottom navigation (2:34)
Adding the widget builders (4:36)
Replacing the FloatingActionButtons (4:26)
Moving the logout button to the AccountPage (2:08)
Presenting modal routes with the root navigator (3:21)
The CupertinoPageRoute (1:00)
Handling the Android back button with WillPopScope and navigator keys (5:35)
Adding pop-to-root navigation (1:43)
Wrap up on multiple navigators (1:43)
Useful Links & Resources
Advanced Stream Operations with RxDart
Introduction to advanced stream operations (2:52)
Introduction to RxDart (2:24)
Observable.combineLatest and data transformations in the time tracker app (3:21)
Adding the source code for the new entries page (1:15)
Reviewing the UI code for the entries page (4:02)
Using combineLatest in practice (4:22)
Data manipulation in the EntriesBloc (4:08)
Wrap up on Observables (1:40)
Single subscription vs broadcast streams (3:02)
PublishSubject, ReplaySubject, BehaviorSubject (1:58)
Adding a BehaviorSubject to the EmailSignInBloc (3:34)
Wrap up and notes about local and remote state management (2:27)
Useful Links & Resources
Completing the Time Tracker App
Completing the time tracker app: overview (0:48)
Accessing the User object in the AccountPage (1:39)
Adding an Avatar image (4:43)
Finishing the Avatar code (3:17)
Wrapping up the time tracker app (0:37)
Unit & Widget Tests with Mockito
Introduction to writing tests (2:06)
Testing Flutter Apps (2:31)
Writing the first unit test (3:30)
Running tests (1:31)
Checking and fixing errors in tests (1:18)
Testing edge cases by writing and fixing failing tests (2:53)
Grouping tests together (3:05)
The setUp method and testing date formatting with locales (3:42)
The test lifecycle methods (1:46)
Completing the formatting tests (3:25)
Testing model classes (3:48)
hashCode and the == operator (4:57)
Adding a toString() method, wrap up on unit tests (4:54)
Introduction to widget tests (3:37)
Finding widgets and matcher arguments (3:06)
Testing widget callbacks (2:34)
Working with Acceptance Criteria (1:16)
Introduction to test mocks and mockito (2:05)
Injecting mock objects with Provider (4:28)
Verifying mock methods (3:32)
Working with keys, entering text and the pump() method (6:18)
Testing widget updates on state changes (2:52)
Completing the email sign-in tests (2:16)
Replacing Navigator.pop with a callback when the user signs in (2:30)
Updating the tests to handle the form callback (2:12)
Stubbing mock objects (5:41)
Recap on the email sign in forms and stubbing mocks (2:22)
Using widget tests with StreamBuilder (2:55)
Using StreamController inside tests (7:07)
Adding a Database builder to the Landing Page (5:16)
Test setup for the SignInPage (2:45)
Adding keys to custom widget classes (2:39)
Testing navigation (4:41)
The great thing about widget tests (1:54)
Testing ValueNotifier models (7:07)
Testing ChangeNotifier models (3:36)
Testing BloCs (3:26)
Comparing EmailSignInModel objects (1:17)
Testing streams in Blocs (8:00)
Wrap up on unit & widget tests (1:24)
Useful Links & Resources
Conclusion and Next Steps
How to migrate this project to Null Safety
Conclusion and Next Steps (4:14)
Further Reading
Tweaking form submission
Lesson content locked
If you're already enrolled,
you'll need to login
.
Enroll in Course to Unlock