├── .gitattributes └── ex01 ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ex01 │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── 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 │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── img │ ├── BoardGameGeek.png │ ├── Discord.jpg │ ├── Gamefound.jpg │ ├── Google.png │ ├── LinkedIn.png │ ├── Netflix.png │ ├── PayPal.png │ ├── Spotify.png │ ├── Trello.jpg │ ├── YouTube.png │ ├── amazon.png │ ├── nokotan.jpg │ └── steam.png ├── firebase.json ├── lib ├── firebase_options.dart ├── main.dart └── screens │ ├── auth_wrapper.dart │ ├── home_screen.dart │ ├── login_screen.dart │ └── register_screen.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── web ├── favicon.png ├── icons ├── Icon-192.png ├── Icon-512.png ├── Icon-maskable-192.png └── Icon-maskable-512.png ├── index.html └── manifest.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /ex01/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | 44 | # Android Studio will place build artifacts here 45 | /android/app/debug 46 | /android/app/profile 47 | /android/app/release 48 | -------------------------------------------------------------------------------- /ex01/.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. 5 | 6 | version: 7 | revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 17 | base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 18 | - platform: android 19 | create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 20 | base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 21 | - platform: web 22 | create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 23 | base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /ex01/README.md: -------------------------------------------------------------------------------- 1 | # ex01 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /ex01/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 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /ex01/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 | -------------------------------------------------------------------------------- /ex01/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | // START: FlutterFire Configuration 26 | apply plugin: 'com.google.gms.google-services' 27 | // END: FlutterFire Configuration 28 | apply plugin: 'kotlin-android' 29 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 30 | 31 | android { 32 | compileSdkVersion flutter.compileSdkVersion 33 | ndkVersion flutter.ndkVersion 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_1_8 37 | targetCompatibility JavaVersion.VERSION_1_8 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = '1.8' 42 | } 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | } 47 | 48 | defaultConfig { 49 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 50 | applicationId "com.example.ex01" 51 | // You can update the following values to match your application needs. 52 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 53 | minSdkVersion flutter.minSdkVersion 54 | targetSdkVersion flutter.targetSdkVersion 55 | versionCode flutterVersionCode.toInteger() 56 | versionName flutterVersionName 57 | } 58 | 59 | buildTypes { 60 | release { 61 | // TODO: Add your own signing config for the release build. 62 | // Signing with the debug keys for now, so `flutter run --release` works. 63 | signingConfig signingConfigs.debug 64 | } 65 | } 66 | } 67 | 68 | flutter { 69 | source '../..' 70 | } 71 | 72 | dependencies { 73 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 74 | } 75 | -------------------------------------------------------------------------------- /ex01/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "313775491917", 4 | "project_id": "fir-c61da", 5 | "storage_bucket": "fir-c61da.firebasestorage.app" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:313775491917:android:0d086f2379b04cdb8dc504", 11 | "android_client_info": { 12 | "package_name": "com.example.ex01" 13 | } 14 | }, 15 | "oauth_client": [], 16 | "api_key": [ 17 | { 18 | "current_key": "AIzaSyBEP2wyPkkPZd8z63-EiS2a44TcpAmSzgg" 19 | } 20 | ], 21 | "services": { 22 | "appinvite_service": { 23 | "other_platform_oauth_client": [] 24 | } 25 | } 26 | } 27 | ], 28 | "configuration_version": "1" 29 | } -------------------------------------------------------------------------------- /ex01/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ex01/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /ex01/android/app/src/main/kotlin/com/example/ex01/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.ex01 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /ex01/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /ex01/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ex01/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | // START: FlutterFire Configuration 10 | classpath 'com.google.gms:google-services:4.3.15' 11 | // END: FlutterFire Configuration 12 | classpath 'com.android.tools.build:gradle:7.1.2' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | rootProject.buildDir = '../build' 25 | subprojects { 26 | project.buildDir = "${rootProject.buildDir}/${project.name}" 27 | } 28 | subprojects { 29 | project.evaluationDependsOn(':app') 30 | } 31 | 32 | task clean(type: Delete) { 33 | delete rootProject.buildDir 34 | } 35 | -------------------------------------------------------------------------------- /ex01/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /ex01/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-7.4-all.zip 7 | -------------------------------------------------------------------------------- /ex01/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 | -------------------------------------------------------------------------------- /ex01/assets/img/BoardGameGeek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/BoardGameGeek.png -------------------------------------------------------------------------------- /ex01/assets/img/Discord.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/Discord.jpg -------------------------------------------------------------------------------- /ex01/assets/img/Gamefound.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/Gamefound.jpg -------------------------------------------------------------------------------- /ex01/assets/img/Google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/Google.png -------------------------------------------------------------------------------- /ex01/assets/img/LinkedIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/LinkedIn.png -------------------------------------------------------------------------------- /ex01/assets/img/Netflix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/Netflix.png -------------------------------------------------------------------------------- /ex01/assets/img/PayPal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/PayPal.png -------------------------------------------------------------------------------- /ex01/assets/img/Spotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/Spotify.png -------------------------------------------------------------------------------- /ex01/assets/img/Trello.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/Trello.jpg -------------------------------------------------------------------------------- /ex01/assets/img/YouTube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/YouTube.png -------------------------------------------------------------------------------- /ex01/assets/img/amazon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/amazon.png -------------------------------------------------------------------------------- /ex01/assets/img/nokotan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/nokotan.jpg -------------------------------------------------------------------------------- /ex01/assets/img/steam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/assets/img/steam.png -------------------------------------------------------------------------------- /ex01/firebase.json: -------------------------------------------------------------------------------- 1 | {"flutter":{"platforms":{"android":{"default":{"projectId":"fir-c61da","appId":"1:313775491917:android:0d086f2379b04cdb8dc504","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"fir-c61da","configurations":{"android":"1:313775491917:android:0d086f2379b04cdb8dc504","web":"1:313775491917:web:4101f66262a666c38dc504"}}}}}} -------------------------------------------------------------------------------- /ex01/lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: type=lint 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | return web; 21 | } 22 | switch (defaultTargetPlatform) { 23 | case TargetPlatform.android: 24 | return android; 25 | case TargetPlatform.iOS: 26 | throw UnsupportedError( 27 | 'DefaultFirebaseOptions have not been configured for ios - ' 28 | 'you can reconfigure this by running the FlutterFire CLI again.', 29 | ); 30 | case TargetPlatform.macOS: 31 | throw UnsupportedError( 32 | 'DefaultFirebaseOptions have not been configured for macos - ' 33 | 'you can reconfigure this by running the FlutterFire CLI again.', 34 | ); 35 | case TargetPlatform.windows: 36 | throw UnsupportedError( 37 | 'DefaultFirebaseOptions have not been configured for windows - ' 38 | 'you can reconfigure this by running the FlutterFire CLI again.', 39 | ); 40 | case TargetPlatform.linux: 41 | throw UnsupportedError( 42 | 'DefaultFirebaseOptions have not been configured for linux - ' 43 | 'you can reconfigure this by running the FlutterFire CLI again.', 44 | ); 45 | default: 46 | throw UnsupportedError( 47 | 'DefaultFirebaseOptions are not supported for this platform.', 48 | ); 49 | } 50 | } 51 | 52 | static const FirebaseOptions web = FirebaseOptions( 53 | apiKey: 'AIzaSyAre3TCaxhF9znn5qEFCVHd8hRuTAnHQuI', 54 | appId: '1:313775491917:web:4101f66262a666c38dc504', 55 | messagingSenderId: '313775491917', 56 | projectId: 'fir-c61da', 57 | authDomain: 'fir-c61da.firebaseapp.com', 58 | storageBucket: 'fir-c61da.firebasestorage.app', 59 | measurementId: 'G-PVZG8WXLKM', 60 | ); 61 | 62 | static const FirebaseOptions android = FirebaseOptions( 63 | apiKey: 'AIzaSyBEP2wyPkkPZd8z63-EiS2a44TcpAmSzgg', 64 | appId: '1:313775491917:android:0d086f2379b04cdb8dc504', 65 | messagingSenderId: '313775491917', 66 | projectId: 'fir-c61da', 67 | storageBucket: 'fir-c61da.firebasestorage.app', 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /ex01/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | import 'firebase_options.dart'; 4 | import 'screens/auth_wrapper.dart'; 5 | import 'screens/login_screen.dart'; 6 | import 'screens/register_screen.dart'; 7 | import 'screens/home_screen.dart'; 8 | // import 'constants/app_styles.dart'; // Bỏ comment nếu bạn tạo file styles 9 | 10 | void main() async { 11 | WidgetsFlutterBinding.ensureInitialized(); 12 | await Firebase.initializeApp( 13 | options: DefaultFirebaseOptions.currentPlatform, 14 | ); 15 | runApp(MyApp()); 16 | } 17 | 18 | class MyApp extends StatelessWidget { 19 | @override 20 | Widget build(BuildContext context) { 21 | return MaterialApp( 22 | title: 'Google Style Auth', 23 | theme: ThemeData( 24 | primarySwatch: Colors.blue, 25 | scaffoldBackgroundColor: Colors.white, // Nền trắng giống Google 26 | fontFamily: 'Roboto', // Font giống Google (cần thêm vào pubspec.yaml nếu muốn dùng) 27 | inputDecorationTheme: InputDecorationTheme( // Style chung cho TextField 28 | border: OutlineInputBorder( 29 | borderRadius: BorderRadius.circular(8.0), 30 | borderSide: BorderSide(color: Colors.grey.shade400), 31 | ), 32 | focusedBorder: OutlineInputBorder( 33 | borderRadius: BorderRadius.circular(8.0), 34 | borderSide: BorderSide(color: Colors.blue, width: 2.0), 35 | ), 36 | labelStyle: TextStyle(color: Colors.grey.shade600), 37 | floatingLabelBehavior: FloatingLabelBehavior.auto, 38 | ), 39 | elevatedButtonTheme: ElevatedButtonThemeData( // Style chung cho Button 40 | style: ElevatedButton.styleFrom( 41 | foregroundColor: Colors.white, backgroundColor: Colors.blue, // Text trắng, nền xanh 42 | padding: EdgeInsets.symmetric(vertical: 12, horizontal: 20), 43 | shape: RoundedRectangleBorder( 44 | borderRadius: BorderRadius.circular(8.0), 45 | ), 46 | textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), 47 | ), 48 | ), 49 | textButtonTheme: TextButtonThemeData( 50 | style: TextButton.styleFrom( 51 | foregroundColor: Colors.blue, // Màu text xanh 52 | textStyle: TextStyle(fontWeight: FontWeight.w500) 53 | ) 54 | ) 55 | ), 56 | home: AuthWrapper(), 57 | routes: { 58 | '/login': (context) => LoginScreen(), 59 | '/register': (context) => RegisterScreen(), 60 | '/home': (context) => HomeScreen(), 61 | }, 62 | ); 63 | } 64 | } -------------------------------------------------------------------------------- /ex01/lib/screens/auth_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'login_screen.dart'; 4 | import 'home_screen.dart'; 5 | 6 | class AuthWrapper extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return StreamBuilder( 10 | stream: FirebaseAuth.instance.authStateChanges(), 11 | builder: (context, snapshot) { 12 | if (snapshot.connectionState == ConnectionState.waiting) { 13 | return Scaffold(body: Center(child: CircularProgressIndicator())); 14 | } 15 | if (snapshot.hasData) { 16 | return HomeScreen(); // Đã đăng nhập -> Home 17 | } 18 | return LoginScreen(); // Chưa đăng nhập -> Login 19 | }, 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /ex01/lib/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | 4 | class HomeScreen extends StatelessWidget { 5 | final FirebaseAuth _auth = FirebaseAuth.instance; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | final User? user = _auth.currentUser; 10 | 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: Text('Trang chủ'), 14 | backgroundColor: Colors.white, 15 | foregroundColor: Colors.grey.shade800, // Màu chữ và icon trên AppBar 16 | elevation: 1, // Thêm đường viền nhẹ 17 | actions: [ 18 | IconButton( 19 | icon: Icon(Icons.logout), 20 | tooltip: 'Đăng xuất', 21 | onPressed: () async { 22 | await _auth.signOut(); 23 | // AuthWrapper sẽ tự động chuyển về LoginScreen 24 | }, 25 | ), 26 | ], 27 | ), 28 | body: Center( 29 | child: Column( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | Text( 33 | 'Chào mừng!', 34 | style: Theme.of(context).textTheme.headlineSmall, 35 | ), 36 | SizedBox(height: 10), 37 | if (user?.email != null) 38 | Text('Đăng nhập với email: ${user!.email}'), 39 | SizedBox(height: 5), 40 | if (user?.uid != null) 41 | Text('User ID: ${user!.uid}', style: TextStyle(fontSize: 12, color: Colors.grey)), 42 | ], 43 | ), 44 | ), 45 | ); 46 | } 47 | } -------------------------------------------------------------------------------- /ex01/lib/screens/login_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | 4 | class LoginScreen extends StatefulWidget { 5 | @override 6 | _LoginScreenState createState() => _LoginScreenState(); 7 | } 8 | 9 | class _LoginScreenState extends State { 10 | final FirebaseAuth _auth = FirebaseAuth.instance; 11 | final GlobalKey _formKey = GlobalKey(); 12 | final TextEditingController _emailController = TextEditingController(); 13 | final TextEditingController _passwordController = TextEditingController(); 14 | 15 | bool _isLoading = false; 16 | String? _errorMessage; 17 | bool _obscurePassword = true; // Để ẩn/hiện mật khẩu 18 | 19 | Future _signIn() async { 20 | if (!_formKey.currentState!.validate()) return; 21 | 22 | setState(() { _isLoading = true; _errorMessage = null; }); 23 | 24 | try { 25 | await _auth.signInWithEmailAndPassword( 26 | email: _emailController.text.trim(), 27 | password: _passwordController.text.trim(), 28 | ); 29 | // AuthWrapper sẽ xử lý điều hướng khi đăng nhập thành công 30 | } on FirebaseAuthException catch (e) { 31 | setState(() { 32 | _errorMessage = _mapFirebaseAuthException(e.code); 33 | }); 34 | } catch (e) { 35 | setState(() { 36 | _errorMessage = 'Đã xảy ra lỗi không mong đợi. Vui lòng thử lại.'; 37 | }); 38 | } finally { 39 | if (mounted) { 40 | setState(() { _isLoading = false; }); 41 | } 42 | } 43 | } 44 | 45 | // Hàm ánh xạ mã lỗi Firebase thành thông báo thân thiện 46 | String _mapFirebaseAuthException(String code) { 47 | switch (code) { 48 | case 'user-not-found': 49 | case 'wrong-password': // Gộp chung để tránh tiết lộ email nào tồn tại 50 | return 'Email hoặc mật khẩu không chính xác.'; 51 | case 'invalid-email': 52 | return 'Địa chỉ email không hợp lệ.'; 53 | case 'user-disabled': 54 | return 'Tài khoản này đã bị vô hiệu hóa.'; 55 | default: 56 | return 'Đăng nhập thất bại. Vui lòng thử lại.'; 57 | } 58 | } 59 | 60 | @override 61 | void dispose() { 62 | _emailController.dispose(); 63 | _passwordController.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return Scaffold( 70 | body: SafeArea( // Đảm bảo nội dung không bị che bởi tai thỏ, notch,... 71 | child: Center( 72 | child: SingleChildScrollView( // Cho phép cuộn khi bàn phím hiện lên 73 | padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 20.0), 74 | child: Form( 75 | key: _formKey, 76 | child: Column( 77 | mainAxisAlignment: MainAxisAlignment.center, 78 | crossAxisAlignment: CrossAxisAlignment.stretch, // Kéo dài các thành phần con 79 | children: [ 80 | // Có thể thêm logo ở đây: Image.asset('assets/google_logo.png', height: 40), 81 | Text( 82 | 'Đăng nhập', 83 | textAlign: TextAlign.center, 84 | style: Theme.of(context).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.w500), 85 | ), 86 | SizedBox(height: 10), 87 | Text( 88 | 'Sử dụng tài khoản của bạn', // Hoặc tương tự 89 | textAlign: TextAlign.center, 90 | style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.grey.shade600), 91 | ), 92 | SizedBox(height: 40), 93 | TextFormField( 94 | controller: _emailController, 95 | decoration: InputDecoration( 96 | labelText: 'Email', 97 | prefixIcon: Icon(Icons.email_outlined), 98 | ), 99 | keyboardType: TextInputType.emailAddress, 100 | validator: (value) { 101 | if (value == null || value.isEmpty || !value.contains('@')) { 102 | return 'Vui lòng nhập email hợp lệ'; 103 | } 104 | return null; 105 | }, 106 | ), 107 | SizedBox(height: 15), 108 | TextFormField( 109 | controller: _passwordController, 110 | decoration: InputDecoration( 111 | labelText: 'Mật khẩu', 112 | prefixIcon: Icon(Icons.lock_outline), 113 | suffixIcon: IconButton( // Nút ẩn/hiện mật khẩu 114 | icon: Icon(_obscurePassword ? Icons.visibility_off_outlined : Icons.visibility_outlined), 115 | onPressed: () { 116 | setState(() { 117 | _obscurePassword = !_obscurePassword; 118 | }); 119 | }, 120 | ), 121 | ), 122 | obscureText: _obscurePassword, 123 | validator: (value) { 124 | if (value == null || value.isEmpty) { 125 | return 'Vui lòng nhập mật khẩu'; 126 | } 127 | return null; 128 | }, 129 | ), 130 | SizedBox(height: 10), 131 | Align( // Đặt nút quên mật khẩu sang phải 132 | alignment: Alignment.centerRight, 133 | child: TextButton( 134 | onPressed: () { 135 | // TODO: Implement forgot password functionality 136 | ScaffoldMessenger.of(context).showSnackBar( 137 | SnackBar(content: Text('Chức năng quên mật khẩu chưa được cài đặt.')) 138 | ); 139 | }, 140 | child: Text('Quên mật khẩu?'), 141 | ), 142 | ), 143 | SizedBox(height: 20), 144 | if (_errorMessage != null) 145 | Padding( 146 | padding: const EdgeInsets.only(bottom: 15.0), 147 | child: Text( 148 | _errorMessage!, 149 | style: TextStyle(color: Colors.red.shade700), 150 | textAlign: TextAlign.center, 151 | ), 152 | ), 153 | _isLoading 154 | ? Center(child: CircularProgressIndicator()) 155 | : ElevatedButton( 156 | onPressed: _signIn, 157 | child: Text('Đăng nhập'), 158 | ), 159 | SizedBox(height: 25), 160 | Row( // Phân tách dòng "Hoặc" 161 | children: [ 162 | Expanded(child: Divider(thickness: 1, color: Colors.grey.shade300)), 163 | Padding( 164 | padding: const EdgeInsets.symmetric(horizontal: 10.0), 165 | child: Text('Hoặc', style: TextStyle(color: Colors.grey.shade600)), 166 | ), 167 | Expanded(child: Divider(thickness: 1, color: Colors.grey.shade300)), 168 | ], 169 | ), 170 | SizedBox(height: 25), 171 | OutlinedButton.icon( // Nút đăng nhập Google (chưa cài đặt logic) 172 | icon: Image.asset('img/Google.png', height: 18.0), // Cần có file logo google trong assets 173 | label: Text('Đăng nhập bằng Google', style: TextStyle(color: Colors.grey.shade800)), 174 | style: OutlinedButton.styleFrom( 175 | side: BorderSide(color: Colors.grey.shade400), 176 | padding: EdgeInsets.symmetric(vertical: 12), 177 | ), 178 | onPressed: () { 179 | // TODO: Implement Google Sign-In 180 | ScaffoldMessenger.of(context).showSnackBar( 181 | SnackBar(content: Text('Chức năng đăng nhập Google chưa được cài đặt.')) 182 | ); 183 | }, 184 | ), 185 | SizedBox(height: 30), 186 | TextButton( 187 | onPressed: () { 188 | Navigator.pushNamed(context, '/register'); 189 | }, 190 | child: Text('Tạo tài khoản mới'), 191 | ), 192 | ], 193 | ), 194 | ), 195 | ), 196 | ), 197 | ), 198 | ); 199 | } 200 | } -------------------------------------------------------------------------------- /ex01/lib/screens/register_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | 4 | class RegisterScreen extends StatefulWidget { 5 | @override 6 | _RegisterScreenState createState() => _RegisterScreenState(); 7 | } 8 | 9 | class _RegisterScreenState extends State { 10 | final FirebaseAuth _auth = FirebaseAuth.instance; 11 | final GlobalKey _formKey = GlobalKey(); 12 | final TextEditingController _emailController = TextEditingController(); 13 | final TextEditingController _passwordController = TextEditingController(); 14 | final TextEditingController _confirmPasswordController = TextEditingController(); 15 | 16 | bool _isLoading = false; 17 | String? _errorMessage; 18 | bool _obscurePassword = true; 19 | bool _obscureConfirmPassword = true; 20 | 21 | Future _signUp() async { 22 | if (!_formKey.currentState!.validate()) return; 23 | 24 | setState(() { _isLoading = true; _errorMessage = null; }); 25 | 26 | try { 27 | await _auth.createUserWithEmailAndPassword( 28 | email: _emailController.text.trim(), 29 | password: _passwordController.text.trim(), 30 | ); 31 | // Đăng ký thành công, AuthWrapper sẽ xử lý điều hướng 32 | // Có thể thêm bước gửi email xác thực ở đây nếu muốn: 33 | // User? user = _auth.currentUser; 34 | // await user?.sendEmailVerification(); 35 | // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Đã gửi email xác thực.'))); 36 | // Navigator.pop(context); // Quay lại Login sau khi đăng ký 37 | 38 | } on FirebaseAuthException catch (e) { 39 | setState(() { 40 | _errorMessage = _mapFirebaseAuthException(e.code); 41 | }); 42 | } catch (e) { 43 | setState(() { 44 | _errorMessage = 'Đã xảy ra lỗi không mong đợi. Vui lòng thử lại.'; 45 | }); 46 | } finally { 47 | if (mounted) { 48 | setState(() { _isLoading = false; }); 49 | } 50 | } 51 | } 52 | 53 | String _mapFirebaseAuthException(String code) { 54 | switch (code) { 55 | case 'weak-password': 56 | return 'Mật khẩu quá yếu. Vui lòng chọn mật khẩu mạnh hơn.'; 57 | case 'email-already-in-use': 58 | return 'Địa chỉ email này đã được sử dụng.'; 59 | case 'invalid-email': 60 | return 'Địa chỉ email không hợp lệ.'; 61 | default: 62 | return 'Đăng ký thất bại. Vui lòng thử lại.'; 63 | } 64 | } 65 | 66 | @override 67 | void dispose() { 68 | _emailController.dispose(); 69 | _passwordController.dispose(); 70 | _confirmPasswordController.dispose(); 71 | super.dispose(); 72 | } 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | return Scaffold( 77 | appBar: AppBar( // Thêm AppBar để có nút quay lại 78 | backgroundColor: Colors.white, 79 | elevation: 0, 80 | iconTheme: IconThemeData(color: Colors.grey.shade800), // Màu icon quay lại 81 | ), 82 | body: SafeArea( 83 | child: Center( 84 | child: SingleChildScrollView( 85 | padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 10.0), 86 | child: Form( 87 | key: _formKey, 88 | child: Column( 89 | mainAxisAlignment: MainAxisAlignment.center, 90 | crossAxisAlignment: CrossAxisAlignment.stretch, 91 | children: [ 92 | Text( 93 | 'Tạo tài khoản', 94 | textAlign: TextAlign.center, 95 | style: Theme.of(context).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.w500), 96 | ), 97 | SizedBox(height: 30), 98 | TextFormField( 99 | controller: _emailController, 100 | decoration: InputDecoration(labelText: 'Email', prefixIcon: Icon(Icons.email_outlined)), 101 | keyboardType: TextInputType.emailAddress, 102 | validator: (value) { 103 | if (value == null || value.isEmpty || !value.contains('@')) { 104 | return 'Vui lòng nhập email hợp lệ'; 105 | } 106 | return null; 107 | }, 108 | ), 109 | SizedBox(height: 15), 110 | TextFormField( 111 | controller: _passwordController, 112 | decoration: InputDecoration( 113 | labelText: 'Mật khẩu', 114 | prefixIcon: Icon(Icons.lock_outline), 115 | suffixIcon: IconButton( 116 | icon: Icon(_obscurePassword ? Icons.visibility_off_outlined : Icons.visibility_outlined), 117 | onPressed: () { 118 | setState(() { _obscurePassword = !_obscurePassword; }); 119 | }, 120 | ), 121 | ), 122 | obscureText: _obscurePassword, 123 | validator: (value) { 124 | if (value == null || value.isEmpty) { 125 | return 'Vui lòng nhập mật khẩu'; 126 | } 127 | if (value.length < 6) { 128 | return 'Mật khẩu phải có ít nhất 6 ký tự'; 129 | } 130 | return null; 131 | }, 132 | ), 133 | SizedBox(height: 15), 134 | TextFormField( 135 | controller: _confirmPasswordController, 136 | decoration: InputDecoration( 137 | labelText: 'Xác nhận mật khẩu', 138 | prefixIcon: Icon(Icons.lock_outline), 139 | suffixIcon: IconButton( 140 | icon: Icon(_obscureConfirmPassword ? Icons.visibility_off_outlined : Icons.visibility_outlined), 141 | onPressed: () { 142 | setState(() { _obscureConfirmPassword = !_obscureConfirmPassword; }); 143 | }, 144 | ), 145 | ), 146 | obscureText: _obscureConfirmPassword, 147 | validator: (value) { 148 | if (value == null || value.isEmpty) { 149 | return 'Vui lòng xác nhận mật khẩu'; 150 | } 151 | if (value != _passwordController.text) { 152 | return 'Mật khẩu không khớp'; 153 | } 154 | return null; 155 | }, 156 | ), 157 | SizedBox(height: 25), 158 | if (_errorMessage != null) 159 | Padding( 160 | padding: const EdgeInsets.only(bottom: 15.0), 161 | child: Text( 162 | _errorMessage!, 163 | style: TextStyle(color: Colors.red.shade700), 164 | textAlign: TextAlign.center, 165 | ), 166 | ), 167 | _isLoading 168 | ? Center(child: CircularProgressIndicator()) 169 | : ElevatedButton( 170 | onPressed: _signUp, 171 | child: Text('Tạo tài khoản'), 172 | ), 173 | SizedBox(height: 20), 174 | TextButton( 175 | onPressed: () { 176 | Navigator.pop(context); // Quay lại màn hình trước (Login) 177 | }, 178 | child: Text('Đã có tài khoản? Đăng nhập'), 179 | ), 180 | ], 181 | ), 182 | ), 183 | ), 184 | ), 185 | ), 186 | ); 187 | } 188 | } -------------------------------------------------------------------------------- /ex01/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _flutterfire_internals: 5 | dependency: transitive 6 | description: 7 | name: _flutterfire_internals 8 | sha256: de9ecbb3ddafd446095f7e833c853aff2fa1682b017921fe63a833f9d6f0e422 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.3.54" 12 | async: 13 | dependency: transitive 14 | description: 15 | name: async 16 | sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.12.0" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.2" 28 | characters: 29 | dependency: transitive 30 | description: 31 | name: characters 32 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.4.0" 36 | clock: 37 | dependency: transitive 38 | description: 39 | name: clock 40 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.1.2" 44 | collection: 45 | dependency: transitive 46 | description: 47 | name: collection 48 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.19.1" 52 | cupertino_icons: 53 | dependency: "direct main" 54 | description: 55 | name: cupertino_icons 56 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.0.8" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.2" 68 | firebase_auth: 69 | dependency: "direct main" 70 | description: 71 | name: firebase_auth 72 | sha256: "54c62b2d187709114dd09ce658a8803ee91f9119b0e0d3fc2245130ad9bff9ad" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "5.5.2" 76 | firebase_auth_platform_interface: 77 | dependency: transitive 78 | description: 79 | name: firebase_auth_platform_interface 80 | sha256: "5402d13f4bb7f29f2fb819f3b6b5a5a56c9f714aef2276546d397e25ac1b6b8e" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "7.6.2" 84 | firebase_auth_web: 85 | dependency: transitive 86 | description: 87 | name: firebase_auth_web 88 | sha256: "2be496911f0807895d5fe8067b70b7d758142dd7fb26485cbe23e525e2547764" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "5.14.2" 92 | firebase_core: 93 | dependency: "direct main" 94 | description: 95 | name: firebase_core 96 | sha256: "017d17d9915670e6117497e640b2859e0b868026ea36bf3a57feb28c3b97debe" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "3.13.0" 100 | firebase_core_platform_interface: 101 | dependency: transitive 102 | description: 103 | name: firebase_core_platform_interface 104 | sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "5.4.0" 108 | firebase_core_web: 109 | dependency: transitive 110 | description: 111 | name: firebase_core_web 112 | sha256: "129a34d1e0fb62e2b488d988a1fc26cc15636357e50944ffee2862efe8929b23" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "2.22.0" 116 | flutter: 117 | dependency: "direct main" 118 | description: flutter 119 | source: sdk 120 | version: "0.0.0" 121 | flutter_lints: 122 | dependency: "direct dev" 123 | description: 124 | name: flutter_lints 125 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 126 | url: "https://pub.dev" 127 | source: hosted 128 | version: "2.0.3" 129 | flutter_test: 130 | dependency: "direct dev" 131 | description: flutter 132 | source: sdk 133 | version: "0.0.0" 134 | flutter_web_plugins: 135 | dependency: transitive 136 | description: flutter 137 | source: sdk 138 | version: "0.0.0" 139 | http_parser: 140 | dependency: transitive 141 | description: 142 | name: http_parser 143 | sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" 144 | url: "https://pub.dev" 145 | source: hosted 146 | version: "4.1.2" 147 | leak_tracker: 148 | dependency: transitive 149 | description: 150 | name: leak_tracker 151 | sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "10.0.8" 155 | leak_tracker_flutter_testing: 156 | dependency: transitive 157 | description: 158 | name: leak_tracker_flutter_testing 159 | sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "3.0.9" 163 | leak_tracker_testing: 164 | dependency: transitive 165 | description: 166 | name: leak_tracker_testing 167 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "3.0.1" 171 | lints: 172 | dependency: transitive 173 | description: 174 | name: lints 175 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "2.1.1" 179 | matcher: 180 | dependency: transitive 181 | description: 182 | name: matcher 183 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "0.12.17" 187 | material_color_utilities: 188 | dependency: transitive 189 | description: 190 | name: material_color_utilities 191 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "0.11.1" 195 | meta: 196 | dependency: transitive 197 | description: 198 | name: meta 199 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "1.16.0" 203 | path: 204 | dependency: transitive 205 | description: 206 | name: path 207 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "1.9.1" 211 | plugin_platform_interface: 212 | dependency: transitive 213 | description: 214 | name: plugin_platform_interface 215 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "2.1.8" 219 | sky_engine: 220 | dependency: transitive 221 | description: flutter 222 | source: sdk 223 | version: "0.0.0" 224 | source_span: 225 | dependency: transitive 226 | description: 227 | name: source_span 228 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 229 | url: "https://pub.dev" 230 | source: hosted 231 | version: "1.10.1" 232 | stack_trace: 233 | dependency: transitive 234 | description: 235 | name: stack_trace 236 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 237 | url: "https://pub.dev" 238 | source: hosted 239 | version: "1.12.1" 240 | stream_channel: 241 | dependency: transitive 242 | description: 243 | name: stream_channel 244 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 245 | url: "https://pub.dev" 246 | source: hosted 247 | version: "2.1.4" 248 | string_scanner: 249 | dependency: transitive 250 | description: 251 | name: string_scanner 252 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 253 | url: "https://pub.dev" 254 | source: hosted 255 | version: "1.4.1" 256 | term_glyph: 257 | dependency: transitive 258 | description: 259 | name: term_glyph 260 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 261 | url: "https://pub.dev" 262 | source: hosted 263 | version: "1.2.2" 264 | test_api: 265 | dependency: transitive 266 | description: 267 | name: test_api 268 | sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 269 | url: "https://pub.dev" 270 | source: hosted 271 | version: "0.7.4" 272 | typed_data: 273 | dependency: transitive 274 | description: 275 | name: typed_data 276 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 277 | url: "https://pub.dev" 278 | source: hosted 279 | version: "1.4.0" 280 | vector_math: 281 | dependency: transitive 282 | description: 283 | name: vector_math 284 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 285 | url: "https://pub.dev" 286 | source: hosted 287 | version: "2.1.4" 288 | vm_service: 289 | dependency: transitive 290 | description: 291 | name: vm_service 292 | sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" 293 | url: "https://pub.dev" 294 | source: hosted 295 | version: "14.3.1" 296 | web: 297 | dependency: transitive 298 | description: 299 | name: web 300 | sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" 301 | url: "https://pub.dev" 302 | source: hosted 303 | version: "1.1.1" 304 | sdks: 305 | dart: ">=3.7.0-0 <4.0.0" 306 | flutter: ">=3.22.0" 307 | -------------------------------------------------------------------------------- /ex01/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: ex01 2 | description: A new Flutter project. 3 | publish_to: 'none' 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.17.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cupertino_icons: ^1.0.2 13 | firebase_core: ^3.13.0 14 | firebase_auth: ^5.5.2 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ^2.0.0 20 | 21 | flutter: 22 | assets: 23 | - assets/img/ 24 | uses-material-design: true 25 | -------------------------------------------------------------------------------- /ex01/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:ex01/ex01.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /ex01/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/web/favicon.png -------------------------------------------------------------------------------- /ex01/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/web/icons/Icon-192.png -------------------------------------------------------------------------------- /ex01/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/web/icons/Icon-512.png -------------------------------------------------------------------------------- /ex01/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /ex01/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TranHuuDat2004/DemoFirebase/8e86d18d05aecf747ad1f285b1f266c1b8b9e750/ex01/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /ex01/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ex01 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /ex01/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ex01", 3 | "short_name": "ex01", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | --------------------------------------------------------------------------------