├── .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 |
--------------------------------------------------------------------------------