├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── Runner.entitlements │ ├── AppDelegate.swift │ ├── GoogleService-Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ ├── flutter_export_environment.sh │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile └── Podfile.lock ├── lib ├── services │ ├── services.dart │ ├── firestore.dart │ ├── models.dart │ ├── auth.dart │ └── models.g.dart ├── shared │ ├── shared.dart │ ├── error.dart │ ├── loading.dart │ ├── bottom_nav.dart │ └── progress_bar.dart ├── about │ └── about.dart ├── routes.dart ├── theme.dart ├── quiz │ ├── quiz_state.dart │ └── quiz.dart ├── home │ └── home.dart ├── main.dart ├── topics │ ├── topics.dart │ ├── topic_item.dart │ └── drawer.dart ├── profile │ └── profile.dart └── login │ └── login.dart ├── assets ├── logo.png ├── user.png ├── congrats.gif └── covers │ ├── js.png │ ├── rxjs.png │ ├── ts.png │ ├── angular.png │ ├── firebase.png │ ├── firestore.png │ ├── flutter.png │ ├── default-cover.png │ ├── flutter-layout.png │ └── cloud-functions.png ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── quizapp3 │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ └── google-services.json ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── .metadata ├── .gitignore ├── README.md ├── analysis_options.yaml ├── pubspec.yaml ├── .flutter-plugins-dependencies └── pubspec.lock /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/services/services.dart: -------------------------------------------------------------------------------- 1 | export 'auth.dart'; 2 | export 'firestore.dart'; 3 | export 'models.dart'; 4 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/logo.png -------------------------------------------------------------------------------- /assets/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/user.png -------------------------------------------------------------------------------- /assets/congrats.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/congrats.gif -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /assets/covers/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/js.png -------------------------------------------------------------------------------- /assets/covers/rxjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/rxjs.png -------------------------------------------------------------------------------- /assets/covers/ts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/ts.png -------------------------------------------------------------------------------- /assets/covers/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/angular.png -------------------------------------------------------------------------------- /assets/covers/firebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/firebase.png -------------------------------------------------------------------------------- /assets/covers/firestore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/firestore.png -------------------------------------------------------------------------------- /assets/covers/flutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/flutter.png -------------------------------------------------------------------------------- /lib/shared/shared.dart: -------------------------------------------------------------------------------- 1 | export 'bottom_nav.dart'; 2 | export 'error.dart'; 3 | export 'loading.dart'; 4 | export 'progress_bar.dart'; 5 | -------------------------------------------------------------------------------- /assets/covers/default-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/default-cover.png -------------------------------------------------------------------------------- /assets/covers/flutter-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/flutter-layout.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/covers/cloud-functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/assets/covers/cloud-functions.png -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/quizapp3/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.fireship.quizapp 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/flutter-firebase-quizapp-course/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.applesignin 6 | 7 | Default 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: ffb2ecea5223acdd139a5039be2f9c796962833d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /lib/shared/error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ErrorMessage extends StatelessWidget { 4 | final String message; 5 | 6 | const ErrorMessage({super.key, this.message = 'it broke'}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Center( 11 | child: Text(message), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/about/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AboutScreen extends StatelessWidget { 4 | const AboutScreen({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | appBar: AppBar(title: const Text('about'), backgroundColor: Colors.blue), 10 | body: const Center( 11 | child: Text('About this app...'), 12 | ), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:quizapp/about/about.dart'; 2 | import 'package:quizapp/profile/profile.dart'; 3 | import 'package:quizapp/login/login.dart'; 4 | import 'package:quizapp/topics/topics.dart'; 5 | import 'package:quizapp/home/home.dart'; 6 | 7 | var appRoutes = { 8 | '/': (context) => const HomeScreen(), 9 | '/login': (context) => const LoginScreen(), 10 | '/topics': (context) => const TopicsScreen(), 11 | '/profile': (context) => const ProfileScreen(), 12 | '/about': (context) => const AboutScreen(), 13 | }; 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=C:\src\flutter" 4 | export "FLUTTER_APPLICATION_PATH=D:\dev\flutter-firebase-quizapp-course" 5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true" 6 | export "FLUTTER_TARGET=lib\main.dart" 7 | export "FLUTTER_BUILD_DIR=build" 8 | export "FLUTTER_BUILD_NAME=1.0.0" 9 | export "FLUTTER_BUILD_NUMBER=1" 10 | export "DART_OBFUSCATION=false" 11 | export "TRACK_WIDGET_CREATION=false" 12 | export "TREE_SHAKE_ICONS=false" 13 | export "PACKAGE_CONFIG=.dart_tool/package_config.json" 14 | -------------------------------------------------------------------------------- /lib/shared/loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Loader extends StatelessWidget { 4 | const Loader({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const SizedBox( 9 | width: 250, 10 | height: 250, 11 | child: CircularProgressIndicator(), 12 | ); 13 | } 14 | } 15 | 16 | class LoadingScreen extends StatelessWidget { 17 | const LoadingScreen({super.key}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return const Scaffold( 22 | body: Center( 23 | child: Loader(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /lib/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | 4 | var appTheme = ThemeData( 5 | fontFamily: GoogleFonts.nunito().fontFamily, 6 | bottomAppBarTheme: const BottomAppBarTheme( 7 | color: Colors.black87, 8 | ), 9 | brightness: Brightness.dark, 10 | textTheme: const TextTheme( 11 | bodyText1: TextStyle(fontSize: 18), 12 | bodyText2: TextStyle(fontSize: 16), 13 | button: TextStyle( 14 | letterSpacing: 1.5, 15 | fontWeight: FontWeight.bold, 16 | ), 17 | headline1: TextStyle( 18 | fontWeight: FontWeight.bold, 19 | ), 20 | subtitle1: TextStyle( 21 | color: Colors.grey, 22 | ), 23 | ), 24 | buttonTheme: const ButtonThemeData(), 25 | ); 26 | -------------------------------------------------------------------------------- /lib/quiz/quiz_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:quizapp/services/models.dart'; 3 | 4 | class QuizState with ChangeNotifier { 5 | double _progress = 0; 6 | Option? _selected; 7 | 8 | final PageController controller = PageController(); 9 | 10 | double get progress => _progress; 11 | Option? get selected => _selected; 12 | 13 | set progress(double newValue) { 14 | _progress = newValue; 15 | notifyListeners(); 16 | } 17 | 18 | set selected(Option? newValue) { 19 | _selected = newValue; 20 | notifyListeners(); 21 | } 22 | 23 | void nextPage() async { 24 | await controller.nextPage( 25 | duration: const Duration(milliseconds: 500), 26 | curve: Curves.easeOut, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.8' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /lib/home/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:quizapp/login/login.dart'; 3 | import 'package:quizapp/services/auth.dart'; 4 | import 'package:quizapp/shared/shared.dart'; 5 | import 'package:quizapp/topics/topics.dart'; 6 | 7 | class HomeScreen extends StatelessWidget { 8 | const HomeScreen({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return StreamBuilder( 13 | stream: AuthService().userStream, 14 | builder: (context, snapshot) { 15 | if (snapshot.connectionState == ConnectionState.waiting) { 16 | return const LoadingScreen(); 17 | } else if (snapshot.hasError) { 18 | return const Center( 19 | child: ErrorMessage(), 20 | ); 21 | } else if (snapshot.hasData) { 22 | return const TopicsScreen(); 23 | } else { 24 | return const LoginScreen(); 25 | } 26 | }, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Firebase - The Full Course 3.0 2 | 3 | 🕊️🔥 Build a realtime Quiz App from scratch with Flutter 3 & Firebase. 4 | 5 | ## Learn it 6 | 7 | Enroll in the [Flutter Firebase Course](https://fireship.io/courses/flutter-firebase/) on Fireship. 8 | 9 | ## Try it 10 | 11 | - [Get it on The App Store](https://itunes.apple.com/us/app/fireship/id1462592372?mt=8) 12 | - [Get it on Google Play](https://play.google.com/store/apps/details?id=io.fireship.quizapp) 13 | 14 | ## Run it 15 | 16 | ``` 17 | git clone https://github.com/fireship-io/flutter-firebase-quizapp-course.git quizapp 18 | cd quizapp 19 | 20 | flutter run 21 | ``` 22 | 23 | ## Learning Goals 24 | 25 | - 👨‍🎤 Build a complete Flutter app with Firebase 26 | - 🐦 Master most common Flutter widgets 27 | - 🔏 User Authentication (Apple, Google, Anonymous) 28 | - ⚒️ State Management with Provider 29 | - 🔥 Model relational Firestore data 30 | - 🎭 Animation for realtime data streams 31 | - ⚓ Organize large Flutter projects 32 | - 🤖 Firestore -> Dart Classes with JSON Serializable 33 | - 🚀 Deploy to the Apple App Store and Google Play 34 | -------------------------------------------------------------------------------- /lib/shared/bottom_nav.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 3 | 4 | class BottomNavBar extends StatelessWidget { 5 | const BottomNavBar({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return BottomNavigationBar( 10 | items: const [ 11 | BottomNavigationBarItem( 12 | icon: Icon( 13 | FontAwesomeIcons.graduationCap, 14 | size: 20, 15 | ), 16 | label: 'Topics', 17 | ), 18 | BottomNavigationBarItem( 19 | icon: Icon( 20 | FontAwesomeIcons.bolt, 21 | size: 20, 22 | ), 23 | label: 'About', 24 | ), 25 | BottomNavigationBarItem( 26 | icon: Icon( 27 | FontAwesomeIcons.circleUser, 28 | size: 20, 29 | ), 30 | label: 'Profile', 31 | ), 32 | ], 33 | fixedColor: Colors.deepPurple[200], 34 | onTap: (int idx) { 35 | switch (idx) { 36 | case 0: 37 | // do nothing 38 | break; 39 | case 1: 40 | Navigator.pushNamed(context, '/about'); 41 | break; 42 | case 2: 43 | Navigator.pushNamed(context, '/profile'); 44 | break; 45 | } 46 | }, 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 758773997881-sk4tfalbk1oqh5f2vv4gbcjqdlm7lq5f.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.758773997881-sk4tfalbk1oqh5f2vv4gbcjqdlm7lq5f 9 | ANDROID_CLIENT_ID 10 | 758773997881-b4g2gerk1isv5ehq4h9s63hgnn433rjc.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyCHpFJY54w_YiXjCOfbVbsefn91FFUJ9PE 13 | GCM_SENDER_ID 14 | 758773997881 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | io.fireship.quizapp 19 | PROJECT_ID 20 | fireship-lessons 21 | STORAGE_BUCKET 22 | fireship-lessons.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:758773997881:ios:82656735bc074188d1c8e8 35 | DATABASE_URL 36 | https://fireship-lessons.firebaseio.com 37 | 38 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '10.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | - use_super_parameters 26 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 27 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 28 | 29 | # Additional information about this file can be found at 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | quizapp 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_core/firebase_core.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:quizapp/routes.dart'; 5 | import 'package:quizapp/services/services.dart'; 6 | import 'package:quizapp/shared/shared.dart'; 7 | import 'package:quizapp/theme.dart'; 8 | 9 | void main() { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | runApp(const App()); 12 | } 13 | 14 | class App extends StatefulWidget { 15 | const App({super.key}); 16 | 17 | @override 18 | State createState() => _AppState(); 19 | } 20 | 21 | class _AppState extends State { 22 | final Future _initialization = Firebase.initializeApp(); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return FutureBuilder( 27 | // Initialize FlutterFire: 28 | future: _initialization, 29 | builder: (context, snapshot) { 30 | // Check for errors 31 | if (snapshot.hasError) { 32 | // Error screen 33 | } 34 | 35 | // Once complete, show your application 36 | if (snapshot.connectionState == ConnectionState.done) { 37 | return StreamProvider( 38 | create: (_) => FirestoreService().streamReport(), 39 | catchError: (_, err) => Report(), 40 | initialData: Report(), 41 | child: MaterialApp( 42 | debugShowCheckedModeBanner: true, 43 | routes: appRoutes, 44 | theme: appTheme), 45 | ); 46 | } 47 | 48 | // Otherwise, show something whilst waiting for initialization to complete 49 | return const MaterialApp(home: LoadingScreen()); 50 | }, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/services/firestore.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:cloud_firestore/cloud_firestore.dart'; 3 | import 'package:rxdart/rxdart.dart'; 4 | import 'package:quizapp/services/auth.dart'; 5 | import 'package:quizapp/services/models.dart'; 6 | 7 | class FirestoreService { 8 | final FirebaseFirestore _db = FirebaseFirestore.instance; 9 | 10 | /// Reads all documments from the topics collection 11 | Future> getTopics() async { 12 | var ref = _db.collection('topics'); 13 | var snapshot = await ref.get(); 14 | var data = snapshot.docs.map((s) => s.data()); 15 | var topics = data.map((d) => Topic.fromJson(d)); 16 | return topics.toList(); 17 | } 18 | 19 | /// Retrieves a single quiz document 20 | Future getQuiz(String quizId) async { 21 | var ref = _db.collection('quizzes').doc(quizId); 22 | var snapshot = await ref.get(); 23 | return Quiz.fromJson(snapshot.data() ?? {}); 24 | } 25 | 26 | /// Listens to current user's report document in Firestore 27 | Stream streamReport() { 28 | return AuthService().userStream.switchMap((user) { 29 | if (user != null) { 30 | var ref = _db.collection('reports').doc(user.uid); 31 | return ref.snapshots().map((doc) => Report.fromJson(doc.data()!)); 32 | } else { 33 | return Stream.fromIterable([Report()]); 34 | } 35 | }); 36 | } 37 | 38 | /// Updates the current user's report document after completing quiz 39 | Future updateUserReport(Quiz quiz) { 40 | var user = AuthService().user!; 41 | var ref = _db.collection('reports').doc(user.uid); 42 | 43 | var data = { 44 | 'total': FieldValue.increment(1), 45 | 'topics': { 46 | quiz.topic: FieldValue.arrayUnion([quiz.id]) 47 | } 48 | }; 49 | 50 | return ref.set(data, SetOptions(merge: true)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/topics/topics.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 3 | import 'package:quizapp/services/services.dart'; 4 | import 'package:quizapp/shared/shared.dart'; 5 | import 'package:quizapp/topics/drawer.dart'; 6 | import 'package:quizapp/topics/topic_item.dart'; 7 | 8 | class TopicsScreen extends StatelessWidget { 9 | const TopicsScreen({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return FutureBuilder>( 14 | future: FirestoreService().getTopics(), 15 | builder: (context, snapshot) { 16 | if (snapshot.connectionState == ConnectionState.waiting) { 17 | return const LoadingScreen(); 18 | } else if (snapshot.hasError) { 19 | return Center( 20 | child: ErrorMessage(message: snapshot.error.toString()), 21 | ); 22 | } else if (snapshot.hasData) { 23 | var topics = snapshot.data!; 24 | 25 | return Scaffold( 26 | appBar: AppBar( 27 | backgroundColor: Colors.deepPurple, 28 | title: const Text('Topics'), 29 | actions: [ 30 | IconButton( 31 | icon: Icon( 32 | FontAwesomeIcons.circleUser, 33 | color: Colors.pink[200], 34 | ), 35 | onPressed: () => Navigator.pushNamed(context, '/profile'), 36 | ) 37 | ], 38 | ), 39 | drawer: TopicDrawer(topics: topics), 40 | body: GridView.count( 41 | primary: false, 42 | padding: const EdgeInsets.all(20.0), 43 | crossAxisSpacing: 10.0, 44 | crossAxisCount: 2, 45 | children: topics.map((topic) => TopicItem(topic: topic)).toList(), 46 | ), 47 | bottomNavigationBar: const BottomNavBar(), 48 | ); 49 | } else { 50 | return const Text('No topics found in Firestore. Check database'); 51 | } 52 | }, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/services/models.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | part 'models.g.dart'; 3 | 4 | @JsonSerializable() 5 | class Option { 6 | String value; 7 | String detail; 8 | bool correct; 9 | Option({this.value = '', this.detail = '', this.correct = false}); 10 | factory Option.fromJson(Map json) => _$OptionFromJson(json); 11 | Map toJson() => _$OptionToJson(this); 12 | } 13 | 14 | @JsonSerializable() 15 | class Question { 16 | String text; 17 | List