Flutter & Firebase: Build a Complete App for iOS & Android
Learn Dart, Flutter & Firebase by Google, and build a real-world, production ready app
What you will learn?
- Build a complete, real-world app with Flutter & Firebase
- Write iOS & Android apps with a single codebase
- Write production-ready code following best practices and become a competent Flutter developer
- Fast-track your Flutter learning with practical tips and proven techniques
Your trainer
Andrea Bizzotto
Andrea Bizzotto has extensive experience in mobile app development, having built complex apps for various clients and as an independent developer. He is passionate about open source, teaching, and all things Flutter. In 2018, he started Code With Andrea. As part of this, he launched a YouTube channel and a blog, where he regularly shares new articles and videos. All his learning material has been very well received by new students and the Flutter community.
411 lessons
Easy to follow lectures and videos covering subject details.
21 hours
This course includes hours of video material. Watch on-demand, anytime, anywhere.
Certificate of Completion
You will earn a Certificate of Completion at the end of this course.
Course curriculum
- Course Introduction01:50
- Course Content02:01
- App Overview03:24
- Making the most of this course02:42
- Course Resources00:34
- What is Flutter03:13
- The Dart Language02:25
- Introduction to Dartpad00:48
- A simple program01:42
- Variable declaration and initialization01:43
- String interpolation03:03
- Type inference with var03:13
- Var and final01:54
- The dynamic keyword01:53
- Introduction to functions04:18
- Function return types02:05
- Optional parameters, nullability and default values02:31
- Named parameters01:58
- The arrow operator01:51
- Introduction to classes02:01
- Class constructors03:05
- Instance methods02:14
- Inheritance02:56
- The super constructor01:59
- The base Object class and the toString method01:48
- Overriding the toString method05:25
- Abstract classes03:21
- More on abstract classes02:33
- Using abstract classes with functions01:54
- Computed properties02:39
- Mixins04:37
- Introduction to lists03:43
- Introduction to maps03:31
- Generics and type annotations04:53
- If and else statements03:51
- The ternary operator02:25
- The while loop03:13
- The for loop01:59
- Closures and the fold method04:12
- Enumerations02:45
- Switch statements02:49
- Wrap-up01:11
- Useful Links & Resources00:03
- macOS setup and Flutter 1.x00:18
- Flutter setup on macOS02:39
- Setting the PATH variable04:26
- Flutter doctor00:50
- Xcode and iOS simulator setup02:17
- Installing Android Studio05:54
- Installing the Android emulator02:20
- Running Flutter from the command line05:21
- Flutter setup on Android Studio03:35
- Installing Visual Studio Code03:11
- Useful Links & Resources00:05
- Flutter setup on Windows01:27
- Updating the path variable02:09
- Flutter doctor00:54
- Installing Android Studio04:27
- Installing the Android emulator03:18
- Running Flutter from the command line02:57
- Flutter setup on Android Studio04:19
- Installing Visual Studio Code02:52
- Useful Links & Resources00:05
- Creating a Flutter project with Android Studio03:28
- ACTION REQUIRED: Flutter 2.0 and Null Safety00:48
- A tour of the project folders02:40
- Running the Android emulator and iOS simulator02:09
- Overview of the Flutter counter app01:08
- Hot reload and hot restart04:06
- Introduction to widgets02:22
- The MaterialApp widget01:51
- The Scaffold widget02:37
- The Flutter widget tree03:25
- Stateless and stateful widgets02:42
- Updating the counter with setState02:10
- Wrap up01:10
- Useful Links & Resources00:02
- Shortcuts for VS Code and Android Studio00:11
- Overview of the Time tracker app01:42
- Switching between apps00:54
- Writing the root widget of the app04:07
- Adding the MaterialApp02:50
- Adding some folders to our project02:19
- Adding a sign-in page03:35
- The ThemeData class02:12
- The AppBar widget02:38
- Preview of the SignInPage layout01:27
- Adding a Column layout03:17
- The CrossAxisAlignment property03:55
- Code formatting with dartfmt04:53
- Adding some boxes and extracting code into a method03:39
- Private methods03:25
- Adding some padding04:33
- The MainAxisAlignment property01:14
- Text, TextStyle and FontWeight03:12
- Introduction to buttons01:49
- Adding the first button03:29
- Button callbacks explained04:14
- Customising button colors03:09
- MaterialColor explained03:08
- Changing button shapes02:16
- Making code reusable01:24
- Creating a reusable custom RaisedButton06:39
- Creating a reusable SignInButton04:13
- Setting default values02:22
- Making the button height configurable03:55
- Adding the remaining buttons04:36
- Adding logos: introduction01:17
- Updating the pubspec.yaml file04:02
- Image variants01:53
- Adding an image inside a button03:33
- Arranging widgets horizontally in a Row04:27
- The Opacity widget02:39
- Creating a custom SocialSignInButton05:58
- The @required annotation05:03
- Using assertions for better widget API design09:02
- Useful Links & Resources00:02
- Local and remote authentication03:56
- Introduction to Firebase01:33
- Creating a Firebase project02:03
- Configuring Firebase for Android08:58
- How to fix: "'keytool' is not recognized as an internal or external command"00:18
- Configuring Firebase for iOS07:22
- Installing the firebase_core and firebase_auth packages03:22
- Initializing the Firebase App03:08
- Running on iOS and updating Cocoapods01:25
- Futures, async and await02:00
- Signing in anonymously with Firebase04:15
- The FirebaseAuth singleton and private constructors03:06
- Explaining the short-hand syntax for callbacks01:25
- Error handling with try/catch02:43
- Useful Links & Resources00:26
- Recommendations about choosing and updating packages00:15
- Preview of the sign-in and sign-out flow03:09
- Creating a landing page widget02:21
- Adding a Firebase User to the LandingPage01:43
- Adding a callback to the SignInPage02:54
- Hooking up the onSignIn callback03:05
- Creating the home page04:02
- Adding the sign-out functionality02:46
- Hooking up the onSignOut callback02:34
- Retrieving the current user when the app starts02:11
- Explaining global access and scoped access05:35
- Creating the Auth class02:38
- The abstract AuthBase class03:03
- Using the Auth class04:36
- Lifting state up and its drawbacks04:38
- State Management & App Architecture02:30
- Introduction to Streams02:43
- Streams in practice with DartPad04:01
- Handling errors and closing streams04:51
- The authStateChanges stream02:19
- Listening to the authStateChanges stream02:36
- Adding the StreamBuilder code04:33
- More on StreamBuilder03:08
- Refactoring the sign-in flows02:41
- Wrap-up on Streams and StreamBuilder02:57
- Useful Links & Resources00:01
- Overview of the Firebase sign-in methods01:59
- Enabling support for Google Sign In02:29
- Adding Google Sign-In to the Auth class05:14
- Hooking up Google Sign-In to our button01:34
- Configuring Google Sign-In on iOS03:48
- Google Sign-In flow explained02:50
- Supporting Google Sign Out03:01
- Testing Google Sign-In on Android02:56
- Checklist: Google Sign-In Flutter setup on Android00:58
- Viewing registered users on the Firebase console00:46
- Registering a Facebook App07:32
- Enabling Facebook Sign-In on Firebase02:28
- Installing the Facebook login package01:23
- Enabling MultiDex support on Android01:37
- Adding the Facebook Sign-In code04:35
- Testing Facebook Sign-In on Android03:35
- Facebook iOS setup in Xcode04:39
- Testing Facebook Sign-In on iOS02:10
- Accessing the user's data and privacy considerations02:56
- Useful Links & Resources00:05
- Preview of the email & password sign-in page02:20
- Creating the email & password sign-in page01:59
- Passing the BuildContext across methods01:48
- Introduction to navigation05:20
- Adding a Card widget02:03
- Adding the email and password text fields05:17
- Adding the submit buttons02:21
- Creating a FormSubmitButton widget03:31
- Adding a TextEditingController05:23
- Toggling the form type06:21
- Adding the email & password authentication code04:14
- Implementing the submit method02:59
- Testing email & password sign-in02:55
- Customising the email and password text fields03:32
- Using FocusNode and FocusScope04:47
- Disabling the submit button on empty email or password05:30
- Adding a StringValidator class02:28
- Adding an email and password validation mixin02:50
- Showing an error text when the email or password are invalid03:16
- Tweaking form submission04:45
- Simulating a slow network with a delay02:51
- Adding a loading state to our form03:35
- Updating the email focus logic02:14
- Fixing the vertical overflow on small screens03:36
- Wrap-up04:41
- Useful Links & Resources00:04
- Introduction to dialogs01:18
- Showing a dialog02:49
- Dismissing dialogs02:14
- Platform-aware widgets on iOS, Android & more04:02
- Adding a reusable showAlertDialog function04:47
- Adding a sign-out confirmation dialog04:20
- Dialog differences on Android and iOS02:00
- Useful Links & Resources00:01
- Introduction to InheritedWidget03:35
- Creating an AuthProvider05:41
- Accessing the Auth object via the AuthProvider06:22
- Adding the provider package01:41
- Using the Provider class04:55
- Wrap-up about scoped access04:08
- Useful Links & Resources00:02
- Module Introduction01:08
- Creating better user-facing errors with FirebaseAuthException02:39
- Creating a custom exception alert dialog03:02
- Showing error alerts in the SignInPage03:15
- Adding a loading state: overview01:11
- Adding a loading state to the SignInPage02:32
- Using the loading state in the SignInPage03:35
- The dispose method02:57
- Introduction to state management with BLoCs04:49
- The application layers01:40
- BLoCs, sinks, streams, and asynchronous code04:52
- Introduction to the SignInBloc02:50
- Implementing a simple BLoC03:26
- Adding a Bloc with Provider inside a static method02:37
- Adding the StreamBuilder code02:47
- Converting the SignInPage to a stateless widget02:56
- The difference between Provider.of and Consumer03:15
- Disposing BLoCs with Provider01:03
- Adding authentication code to the SignInBloc05:47
- Updating the SignInPage02:13
- Fixing the BLoC submit method02:55
- Summary on the BLoC basics01:15
- Introduction to the email sign-in flow with BLoC02:35
- Creating a model class for the EmailSignInForm02:48
- Creating the EmailSignInBloc with a StreamController02:31
- Updating the model06:38
- Adding the BLoC submit method05:19
- Setting up the EmailSignInFormBlocBased with Provider05:05
- Refactoring the EmailSignInFormBlocBased widget by removing the state variables07:17
- Moving the business logic to the BLoC class03:35
- Moving more business logic to the model class06:31
- The benefits of separation of concerns with BLoC02:59
- Using stateful widgets with TextEditingControllers03:35
- Considerations about performance02:05
- Blocs and Services in the widget tree03:37
- Recap on State Management02:23
- Introduction to ValueNotifier02:06
- Adding a ValueNotifier with ChangeNotifierProvider04:50
- Consumer and ChangeNotifierProvider explained03:23
- Differences between BLoC/streams and ValueNotifier/ChangeNotifierProvider02:53
- Introduction to ChangeNotifier01:25
- Adding the EmailSignInChangeModel class04:47
- Completing the EmailSignInChangeModel class02:42
- Implementing the email sign-in form with ChangeNotifier05:31
- Comparing ValueNotifier and ChangeNotifier03:22
- Wrap up on State Management03:45
- Wrap up on the Authentication Flows02:05
- Useful Links & Resources00:05
- Overview of the time tracker app02:37
- Database schema and SQL vs NoSQL04:27
- Introduction to Cloud Firestore02:26
- Documents and Collections02:00
- Getting started with Firestore03:19
- Designing a Database API with CRUD operations03:21
- Managing private user data with Cloud Firestore03:21
- Installing Cloud Firestore01:49
- Renaming the HomePage to JobsPage02:30
- Adding the Database class03:01
- Adding the Database Provider01:30
- Adding a FloatingActionButton02:17
- Writing data to Firestore06:50
- Defining a strongly-typed Job model class04:02
- Defining a common API path class02:50
- Adding a generic setData method02:42
- Adding security rules04:52
- Handling Firestore permissions errors02:45
- Reading data from Firestore04:56
- Reading and parsing Firestore data streams04:01
- Adding a StreamBuilder to show a list of jobs04:08
- Debugging the StreamBuilder code04:51
- Firestore as a realtime database02:07
- Adding a factory constructor to our model class02:49
- Adding a generic method to read Firestore streams03:00
- Adding a FirestoreService class04:26
- Wrap-up on Cloud Firestore03:14
- Useful Links & Resources00:09
- Introduction to Forms with Cloud Firestore01:50
- Adding a new job page04:23
- The Placeholder widget03:59
- Introduction to Form and TextFormField03:40
- Validating and saving Form data08:03
- Accessing the Database object with the correct BuildContext04:41
- Saving jobs with a unique document ID04:53
- Handling errors01:39
- Enforcing unique job names03:03
- Fixing the integer-parsing code01:26
- Editing existing jobs: overview01:15
- Adding a custom JobListTile03:50
- Repurposing the AddJobPage for editing jobs04:46
- Reading the documentID from Firestore02:49
- Completing the code for editing jobs04:17
- Wrap up on working with Forms02:46
- Useful Links & Resources00:02
- Intro and multiple states of UI01:52
- Adding an empty content widget03:30
- Adding a reusable list items builder04:45
- Using ListView.builder04:51
- Using ListView.separated02:49
- Deleting jobs from Firestore02:27
- Adding swipe to delete support06:29
- Working with entries: overview01:50
- Relational data & drawbacks of NoSQL databases04:18
- Getting ready to add new files01:20
- Adding the source files to the project03:02
- Connecting the new code and updating the Firestore rules02:13
- Fixing the EditJobPage navigation03:01
- Overview of the JobEntriesPage02:28
- Reading and writing entries with Firestore05:25
- The EntryListItem widget (using InkWell and Expanded)02:04
- Formatting dates and currencies with the Intl package02:30
- Dart as UI: Spreads and Collection-if02:53
- Using date pickers with stateful widgets05:27
- Date and time input with a custom UI and DateTimePicker04:06
- Updating the UI when a Job changes05:10
- Wrap up and CupertinoDatePicker01:24
- Useful Links & Resources00:05
- Introduction to bottom navigation02:00
- Multiple navigation stacks01:45
- Creating a HomePage with a selected tab03:56
- Adding a CupertinoTabScaffold06:37
- Testing the bottom navigation02:34
- Adding the widget builders04:36
- Replacing the FloatingActionButtons04:26
- Moving the logout button to the AccountPage02:08
- Presenting modal routes with the root navigator03:21
- The CupertinoPageRoute01:00
- Handling the Android back button with WillPopScope and navigator keys05:35
- Adding pop-to-root navigation01:43
- Wrap up on multiple navigators01:43
- Useful Links & Resources00:04
- Introduction to advanced stream operations02:52
- Introduction to RxDart02:24
- Observable.combineLatest and data transformations in the time tracker app03:21
- Adding the source code for the new entries page01:15
- Reviewing the UI code for the entries page04:02
- Using combineLatest in practice04:22
- Data manipulation in the EntriesBloc04:08
- Wrap up on Observables01:40
- Single subscription vs broadcast streams03:02
- PublishSubject, ReplaySubject, BehaviorSubject01:58
- Adding a BehaviorSubject to the EmailSignInBloc03:34
- Wrap up and notes about local and remote state management02:27
- Useful Links & Resources00:03
- Completing the time tracker app: overview00:48
- Accessing the User object in the AccountPage01:39
- Adding an Avatar image04:43
- Finishing the Avatar code03:17
- Wrapping up the time tracker app00:37
- Introduction to writing tests02:06
- Testing Flutter Apps02:31
- Writing the first unit test03:30
- Running tests01:31
- Checking and fixing errors in tests01:18
- Testing edge cases by writing and fixing failing tests02:53
- Grouping tests together03:05
- The setUp method and testing date formatting with locales03:42
- The test lifecycle methods01:46
- Completing the formatting tests03:25
- Testing model classes03:48
- hashCode and the == operator04:57
- Adding a toString() method, wrap up on unit tests04:54
- Introduction to widget tests03:37
- Finding widgets and matcher arguments03:06
- Testing widget callbacks02:34
- Working with Acceptance Criteria01:16
- Introduction to test mocks and mockito02:05
- Injecting mock objects with Provider04:28
- Verifying mock methods03:32
- Working with keys, entering text and the pump() method06:18
- Testing widget updates on state changes02:52
- Completing the email sign-in tests02:16
- Replacing Navigator.pop with a callback when the user signs in02:30
- Updating the tests to handle the form callback02:12
- Stubbing mock objects05:41
- Recap on the email sign in forms and stubbing mocks02:22
- Using widget tests with StreamBuilder02:55
- Using StreamController inside tests07:07
- Adding a Database builder to the Landing Page05:16
- Test setup for the SignInPage02:45
- Adding keys to custom widget classes02:39
- Testing navigation04:41
- The great thing about widget tests01:54
- Testing ValueNotifier models07:07
- Testing ChangeNotifier models03:36
- Testing BloCs03:26
- Comparing EmailSignInModel objects01:17
- Testing streams in Blocs08:00
- Wrap up on unit & widget tests01:24
- Useful Links & Resources00:08
- How to migrate this project to Null Safety00:20
- Conclusion and Next Steps04:15
- Further reading00:07
Online Courses
Learning Flutter doesn't have to be hard. Here is our curated list of recommended online courses that will guide you step-by-step in the learning process.
Learn moreBooks
Are you an avid book reader? Do you prefer paperback, or maybe Kindle version? Take a look at our curated list of Flutter related books and take your
YouTube videos
The number of high-quality and free Flutter video tutorials is growing fast. Check this curated list of recommended videos - there is no excuse to stop learning.
Learn more