Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a66db23ace
|
||
|
|
3d7af98ac9
|
||
|
|
b033b97917 | ||
|
|
14b7f31b53
|
||
|
|
403bcafe93
|
||
|
|
21ab6f6021
|
||
|
|
198ecac5da
|
||
|
|
c945cd435e
|
||
|
|
852d29ef0a
|
||
|
|
08383ec03b
|
||
|
|
ee8ce76839
|
||
|
|
f05f2230e9
|
||
|
|
54002beaab
|
||
|
|
d5698fbffc
|
||
|
|
31ad460757
|
||
|
|
4fd2a2a16c
|
||
|
|
6eb40de72c
|
||
|
|
cecbc94a95
|
||
|
|
3820fc4764
|
||
|
|
1b893a5b84
|
||
|
|
72661a343f
|
||
|
|
9ebe8e21dc
|
||
|
|
141f15b444
|
||
|
|
3a8a604a48
|
||
|
|
41493207b3
|
||
|
|
cbc4a2e149
|
||
|
|
7345e415a7
|
||
|
|
ff680614aa
|
||
|
|
3e110e2eb5
|
||
|
|
1ccb0a540e
|
||
|
|
f9da719b91
|
||
|
|
95f41025f4
|
||
|
|
eceda684f0
|
@@ -1,4 +1,10 @@
|
||||
{
|
||||
// Dart
|
||||
"dart.runPubGetOnPubspecChanges": "never",
|
||||
// Gitea
|
||||
"gitea.owner": "Wyatt-FOSS",
|
||||
"gitea.repo": "wyatt-packages",
|
||||
// Header
|
||||
"psi-header.changes-tracking": {
|
||||
"isActive": true
|
||||
},
|
||||
@@ -67,5 +73,4 @@
|
||||
],
|
||||
}
|
||||
],
|
||||
"dart.runPubGetOnPubspecChanges": false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,104 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## 2022-11-16
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`wyatt_authentication_bloc` - `v0.4.0+1`](#wyatt_authentication_bloc---v0401)
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_authentication_bloc` - `v0.4.0+1`
|
||||
|
||||
- **FIX**: add sort of mutex on onAuthChanges callback trigger.
|
||||
|
||||
|
||||
## 2022-11-16
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- [`wyatt_authentication_bloc` - `v0.4.0`](#wyatt_authentication_bloc---v040)
|
||||
- [`wyatt_form_bloc` - `v0.2.0`](#wyatt_form_bloc---v020)
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- There are no other changes in this release.
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_authentication_bloc` - `v0.4.0`
|
||||
|
||||
- **REFACTOR**: update example with new input builders.
|
||||
- **FIX**: add initial null account event in stream in the mock authentication.
|
||||
- **BREAKING** **FIX**: add signInWithEmailAndPassword and signInAnonymously methods in SignInCubit #26.
|
||||
|
||||
#### `wyatt_form_bloc` - `v0.2.0`
|
||||
|
||||
- **FIX**: disable status selection and rebuild SubmitBuilder on every state changes #28.
|
||||
- **FIX**: fix set operations behaviors.
|
||||
- **FIX**: add nullable validator on boolean value.
|
||||
- **FEAT**: add metadata tag to bypass validation #29.
|
||||
- **FEAT**: add shorthands to access validator status.
|
||||
- **BREAKING** **REFACTOR**: input builders provides FormInput.
|
||||
|
||||
|
||||
## 2022-11-13
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- [`wyatt_crud_bloc` - `v0.1.0`](#wyatt_crud_bloc---v010)
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- There are no other changes in this release.
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_crud_bloc` - `v0.1.0`
|
||||
|
||||
- **REFACTOR**: add vscode config.
|
||||
- **FIX**: update example.
|
||||
- **BREAKING** **REFACTOR**: migrate to wyatt architecture.
|
||||
|
||||
|
||||
## 2022-11-11
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`wyatt_authentication_bloc` - `v0.3.0+1`](#wyatt_authentication_bloc---v0301)
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_authentication_bloc` - `v0.3.0+1`
|
||||
|
||||
- **FIX**: fix some bugs to validate publishing.
|
||||
|
||||
|
||||
## 2022-11-11
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
firebase_options.dart
|
||||
.vscode
|
||||
@@ -1,4 +1,10 @@
|
||||
{
|
||||
// Dart
|
||||
"dart.runPubGetOnPubspecChanges": "never",
|
||||
// Gitea
|
||||
"gitea.owner": "Wyatt-FOSS",
|
||||
"gitea.repo": "wyatt-packages",
|
||||
// Header
|
||||
"psi-header.changes-tracking": {
|
||||
"isActive": true
|
||||
},
|
||||
@@ -67,5 +73,4 @@
|
||||
],
|
||||
}
|
||||
],
|
||||
"dart.runPubGetOnPubspecChanges": false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
## 0.4.0+1
|
||||
|
||||
- **FIX**: add sort of mutex on onAuthChanges callback trigger.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
> Note: This release has breaking changes.
|
||||
|
||||
- **REFACTOR**: update example with new input builders.
|
||||
- **FIX**: add initial null account event in stream in the mock authentication.
|
||||
- **BREAKING** **FIX**: add signInWithEmailAndPassword and signInAnonymously methods in SignInCubit #26.
|
||||
|
||||
## 0.3.0+1
|
||||
|
||||
- **FIX**: fix some bugs to validate publishing.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
> Note: This release has breaking changes.
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||
|
||||
analyzer:
|
||||
exclude: "!example/**"
|
||||
@@ -8,6 +8,7 @@
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
@@ -45,4 +46,4 @@ app.*.map.json
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
lib/firebase_options.dart
|
||||
firebase_options.dart
|
||||
@@ -1,10 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: 5464c5bac742001448fe4fc0597be939379f88ea
|
||||
revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
|
||||
base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
|
||||
- platform: ios
|
||||
create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
|
||||
base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
## 1.0.1
|
||||
|
||||
- Update a dependency to the latest release.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# authentication_bloc_example
|
||||
# example_router
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
@@ -8,9 +8,9 @@ This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
||||
@@ -30,6 +30,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
@@ -46,7 +47,9 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.authentication_bloc_example"
|
||||
applicationId "com.example.example_router"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "136771801992",
|
||||
"firebase_url": "https://tchat-beta.firebaseio.com",
|
||||
"project_id": "tchat-beta",
|
||||
"storage_bucket": "tchat-beta.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:136771801992:android:4ff419f07afdad6097203d",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.authentication_bloc_example"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "136771801992-nps7ic22shstvgdv0c2ojvbkj3dkntum.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.example.authentication_bloc_example",
|
||||
"certificate_hash": "5d8790309e13b68c35e5d4d8437c35c6d15e6131"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "136771801992-ncuib3rbu7p4ro4eo5su4vaudn2u4qrv.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyAYS14uXupkS158Q5QAFP1864UrUN_yDSk"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "136771801992-ncuib3rbu7p4ro4eo5su4vaudn2u4qrv.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
},
|
||||
{
|
||||
"client_id": "136771801992-e585bm1n9b3lv89t4phrl9u0glsg52ua.apps.googleusercontent.com",
|
||||
"client_type": 2,
|
||||
"ios_info": {
|
||||
"bundle_id": "com.example.example"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:136771801992:android:8482c9b90bc29de697203d",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.crud_bloc_example"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "136771801992-ncuib3rbu7p4ro4eo5su4vaudn2u4qrv.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyAYS14uXupkS158Q5QAFP1864UrUN_yDSk"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "136771801992-ncuib3rbu7p4ro4eo5su4vaudn2u4qrv.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
},
|
||||
{
|
||||
"client_id": "136771801992-e585bm1n9b3lv89t4phrl9u0glsg52ua.apps.googleusercontent.com",
|
||||
"client_type": 2,
|
||||
"ios_info": {
|
||||
"bundle_id": "com.example.example"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.authentication_bloc_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
package="com.example.example_router">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.authentication_bloc_example">
|
||||
package="com.example.example_router">
|
||||
<application
|
||||
android:label="authentication_bloc_example"
|
||||
android:label="example_router"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.authentication_bloc_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
package="com.example.example_router">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
@@ -6,7 +6,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
// START: FlutterFire Configuration
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
// END: FlutterFire Configuration
|
||||
|
||||
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 564 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
@@ -1,182 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:authentication_bloc_example/constants.dart';
|
||||
import 'package:authentication_bloc_example/home/home_page.dart';
|
||||
import 'package:authentication_bloc_example/login/login_page.dart';
|
||||
import 'package:authentication_bloc_example/model.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
class App extends StatelessWidget {
|
||||
const App({Key? key}) : super(key: key);
|
||||
|
||||
static FormData getNormalFormData() {
|
||||
return const FormData([
|
||||
FormInput(formFieldName, Name.pure()),
|
||||
FormInput(formFieldPhone, Phone.pure()),
|
||||
FormInput(formFieldPro, Boolean.pure()),
|
||||
FormInput(
|
||||
formFieldConfirmedPassword,
|
||||
ConfirmedPassword.pure(),
|
||||
metadata: FormInputMetadata(export: false),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
static FormData getProFormData() {
|
||||
return const FormData([
|
||||
FormInput(formFieldName, Name.pure()),
|
||||
FormInput(formFieldPhone, Phone.pure()),
|
||||
FormInput(formFieldPro, Boolean.pure()),
|
||||
FormInput(formFieldSiren, Siren.pure()),
|
||||
FormInput(formFieldIban, Iban.pure()),
|
||||
FormInput(
|
||||
formFieldConfirmedPassword,
|
||||
ConfirmedPassword.pure(),
|
||||
metadata: FormInputMetadata(export: false),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
// On Authentication Success. (Authenticated or Anonymous)
|
||||
// User callback.
|
||||
Future<Map<String, dynamic>> onAuthSuccess(UserInterface user) async {
|
||||
if (user.isNotEmpty && !user.isAnonymous) {
|
||||
// Check if user is register in Firesore.
|
||||
DocumentSnapshot firestoreUser = await FirebaseFirestore.instance
|
||||
.collection(firestoreCollectionUsers)
|
||||
.doc(user.uid)
|
||||
.get();
|
||||
|
||||
if (!firestoreUser.exists) {
|
||||
// Register user in Firestore when sign in with social account.
|
||||
final uid = user.uid;
|
||||
final u = {'uid': uid, 'email': user.email};
|
||||
await FirebaseFirestore.instance
|
||||
.collection(firestoreCollectionUsers)
|
||||
.doc(uid)
|
||||
.set(u);
|
||||
return {
|
||||
'user': UserFirestore(
|
||||
uid: uid,
|
||||
email: user.email ?? '',
|
||||
name: user.displayName ?? '',
|
||||
phone: user.phoneNumber ?? ''),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'user': UserFirestore.fromMap(
|
||||
firestoreUser.data() as Map<String, dynamic>),
|
||||
...firestoreUser.data() as Map<String, dynamic>? ?? {}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// On Sign Up Success.
|
||||
Future<void> onSignUpSuccess(SignUpState state, String? uid) async {
|
||||
if (uid != null) {
|
||||
final data = state.data.toMap();
|
||||
final user = {'uid': uid, 'email': state.email.value, ...data};
|
||||
log('onSignUpSuccess: $user');
|
||||
await FirebaseFirestore.instance
|
||||
.collection(firestoreCollectionUsers)
|
||||
.doc(uid)
|
||||
.set(user);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AuthenticationRepositoryInterface authenticationRepository =
|
||||
AuthenticationRepositoryFirebase();
|
||||
|
||||
AuthenticationCubit authenticationCubit = AuthenticationCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
onAuthSuccess: onAuthSuccess,
|
||||
);
|
||||
|
||||
SignUpCubit signUpCubit = SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
authenticationCubit: authenticationCubit,
|
||||
entries: getNormalFormData(),
|
||||
onSignUpSuccess: onSignUpSuccess,
|
||||
);
|
||||
|
||||
SignInCubit signInCubit = SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
authenticationCubit: authenticationCubit,
|
||||
);
|
||||
|
||||
return MultiRepositoryProvider(
|
||||
providers: [
|
||||
RepositoryProvider<AuthenticationRepositoryInterface>(
|
||||
create: (context) => authenticationRepository,
|
||||
),
|
||||
],
|
||||
child: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<AuthenticationCubit>(
|
||||
create: (context) => authenticationCubit..init(),
|
||||
),
|
||||
BlocProvider<SignUpCubit>(
|
||||
create: (context) => signUpCubit,
|
||||
),
|
||||
BlocProvider<SignInCubit>(
|
||||
create: (context) => signInCubit,
|
||||
),
|
||||
],
|
||||
child: const AppView(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppView extends StatelessWidget {
|
||||
const AppView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
buttonTheme: const ButtonThemeData(
|
||||
buttonColor: Colors.blue,
|
||||
textTheme: ButtonTextTheme.primary,
|
||||
),
|
||||
),
|
||||
home: AuthenticationBuilder(
|
||||
unknown: (context) {
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
},
|
||||
authenticated: (context, user, userData) => const HomePage(),
|
||||
unauthenticated: (context) => const LoginPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// -----
|
||||
// File: bootstrap.dart
|
||||
// Created Date: 19/08/2022 15:05:17
|
||||
// Last Modified: Wed Nov 09 2022
|
||||
// Last Modified: Fri Nov 11 2022
|
||||
// -----
|
||||
// Copyright (c) 2022
|
||||
|
||||
@@ -11,8 +11,6 @@ import 'dart:async';
|
||||
|
||||
import 'package:example_router/core/dependency_injection/get_it.dart';
|
||||
import 'package:example_router/core/utils/app_bloc_observer.dart';
|
||||
import 'package:example_router/firebase_options.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -25,11 +23,6 @@ Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
|
||||
FlutterError.onError = (details) {
|
||||
debugPrint(details.toString());
|
||||
};
|
||||
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
|
||||
await GetItInitializer.init();
|
||||
|
||||
runApp(await builder());
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:authentication_bloc_example/forgot_password/widgets/forgot_password_form.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class ForgotPasswordPage extends StatelessWidget {
|
||||
const ForgotPasswordPage({Key? key}) : super(key: key);
|
||||
|
||||
static Route route() {
|
||||
return MaterialPageRoute<void>(builder: (_) => const ForgotPasswordPage());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Forgot Password')),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: BlocProvider(
|
||||
create: (_) => PasswordResetCubit(
|
||||
context.read<AuthenticationRepositoryInterface>(),
|
||||
),
|
||||
child: const ForgotPasswordForm(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
class _EmailInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<PasswordResetCubit, PasswordResetState>(
|
||||
buildWhen: (previous, current) => previous.email != current.email,
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (email) =>
|
||||
context.read<PasswordResetCubit>().emailChanged(email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'email',
|
||||
helperText: '',
|
||||
errorText: state.email.invalid ? 'invalid email' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ResetButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<PasswordResetCubit, PasswordResetState>(
|
||||
buildWhen: (previous, current) => previous.status != current.status,
|
||||
builder: (context, state) {
|
||||
return state.status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: state.status.isValidated
|
||||
? () => context
|
||||
.read<PasswordResetCubit>()
|
||||
.sendPasswordResetEmail()
|
||||
: null,
|
||||
child: const Text('SEND EMAIL'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ForgotPasswordForm extends StatelessWidget {
|
||||
const ForgotPasswordForm({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<PasswordResetCubit, PasswordResetState>(
|
||||
listener: (context, state) {
|
||||
if (state.status.isSubmissionSuccess) {
|
||||
Navigator.of(context).pop();
|
||||
} else if (state.status.isSubmissionFailure) {
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage ?? 'Password reset Failure'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [_EmailInput(), const SizedBox(height: 8), _ResetButton()],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:authentication_bloc_example/home/widgets/infos.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:authentication_bloc_example/home/widgets/email_verification.dart';
|
||||
|
||||
class HomePage extends StatelessWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
|
||||
static Route route() {
|
||||
return MaterialPageRoute<void>(builder: (_) => const HomePage());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = context.select((AuthenticationCubit cubit) => cubit.state.user);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Home'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: const Icon(Icons.exit_to_app),
|
||||
onPressed: () => context
|
||||
.read<AuthenticationCubit>()
|
||||
.logOut(),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: BlocProvider(
|
||||
create: (_) => EmailVerificationCubit(
|
||||
context.read<AuthenticationRepositoryInterface>(),
|
||||
)..checkEmailVerification(),
|
||||
child: BlocBuilder<EmailVerificationCubit, EmailVerificationState>(
|
||||
builder: (context, state) {
|
||||
if (state.isVerified || user!.isAnonymous) {
|
||||
return const UserInfo();
|
||||
} else {
|
||||
return const EmailVerification();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class EmailVerification extends StatelessWidget {
|
||||
const EmailVerification({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = context.select((AuthenticationCubit cubit) => cubit.state.user);
|
||||
final userData = context.select((AuthenticationCubit cubit) => cubit.state.userData);
|
||||
return Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text("Hello ${userData!['name'] ?? 'null'}!"),
|
||||
const SizedBox(height: 4),
|
||||
Text("Email '${user?.email ?? 'null'}' is not verified"),
|
||||
const SizedBox(height: 4),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<EmailVerificationCubit>().sendEmailVerification();
|
||||
},
|
||||
child: const Text('(Re)send email'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<EmailVerificationCubit>().checkEmailVerification();
|
||||
},
|
||||
child: const Text('Refresh'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:authentication_bloc_example/home/widgets/avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:authentication_bloc_example/constants.dart';
|
||||
|
||||
class UserInfo extends StatelessWidget {
|
||||
const UserInfo({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final user = context.select((AuthenticationCubit cubit) => cubit.state.user);
|
||||
final userData = context.select((AuthenticationCubit cubit) => cubit.state.userData);
|
||||
return Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Avatar(photo: user?.photoURL),
|
||||
const SizedBox(height: 4),
|
||||
Text("Email: ${user?.email ?? 'null'}", style: textTheme.headline6),
|
||||
const SizedBox(height: 4),
|
||||
Text("Name: ${userData![formFieldName] ?? 'null'}",
|
||||
style: textTheme.headline6),
|
||||
const SizedBox(height: 4),
|
||||
Text("Phone: ${userData[formFieldPhone] ?? 'null'}",
|
||||
style: textTheme.headline6),
|
||||
const SizedBox(height: 4),
|
||||
Text("IsPro: ${userData[formFieldPro] ?? 'null'}",
|
||||
style: textTheme.headline6),
|
||||
const SizedBox(height: 4),
|
||||
Text("IsAnonymous: ${user?.isAnonymous.toString() ?? 'null'}",
|
||||
style: textTheme.headline6),
|
||||
const SizedBox(height: 4),
|
||||
Text("IsEmailVerified: ${user?.emailVerified.toString() ?? 'null'}",
|
||||
style: textTheme.headline6),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:authentication_bloc_example/app/app.dart';
|
||||
import 'package:authentication_bloc_example/constants.dart';
|
||||
import 'package:authentication_bloc_example/forgot_password/forgot_password_page.dart';
|
||||
import 'package:authentication_bloc_example/sign_up/sign_up_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
class _EmailInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInCubit, SignInState>(
|
||||
buildWhen: (previous, current) => previous.email != current.email,
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (email) => context.read<SignInCubit>().emailChanged(email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'email',
|
||||
helperText: '',
|
||||
errorText: state.email.invalid ? 'invalid email' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PasswordInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInCubit, SignInState>(
|
||||
buildWhen: (previous, current) => previous.password != current.password,
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (password) =>
|
||||
context.read<SignInCubit>().passwordChanged(password),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'password',
|
||||
helperText: '',
|
||||
errorText: state.password.invalid ? 'invalid password' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginAnonButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInCubit, SignInState>(
|
||||
buildWhen: (previous, current) => previous.status != current.status,
|
||||
builder: (context, state) {
|
||||
return state.status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: () =>
|
||||
context.read<SignInCubit>().signInAnonymously(),
|
||||
child: const Text('LOGIN ANONYMOUSLY'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginWithPasswordButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInCubit, SignInState>(
|
||||
buildWhen: (previous, current) => previous.status != current.status,
|
||||
builder: (context, state) {
|
||||
return state.status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: state.status.isValidated
|
||||
? () =>
|
||||
context.read<SignInCubit>().signInWithEmailAndPassword()
|
||||
: null,
|
||||
child: const Text('LOGIN WITH PASSWORD'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginWithGoogleButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInCubit, SignInState>(
|
||||
buildWhen: (previous, current) => previous.status != current.status,
|
||||
builder: (context, state) {
|
||||
return state.status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: () =>
|
||||
context.read<SignInCubit>().signInWithGoogle(),
|
||||
child: const Text('LOGIN GOOGLE'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignUpButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return TextButton(
|
||||
onPressed: () {
|
||||
context.read<SignUpCubit>().updateFormData(App.getNormalFormData());
|
||||
Navigator.of(context).push<void>(SignUpPage.route());
|
||||
},
|
||||
child: Text(
|
||||
'CREATE ACCOUNT',
|
||||
style: TextStyle(color: theme.primaryColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignUpAsProButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return TextButton(
|
||||
onPressed: () {
|
||||
context.read<SignUpCubit>().updateFormData(App.getProFormData());
|
||||
context.read<SignUpCubit>().dataChanged(
|
||||
formFieldPro,
|
||||
const Boolean.dirty(value: true),
|
||||
);
|
||||
Navigator.of(context).push<void>(SignUpPage.route());
|
||||
},
|
||||
child: Text(
|
||||
'CREATE PRO ACCOUNT',
|
||||
style: TextStyle(color: theme.primaryColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoginForm extends StatelessWidget {
|
||||
const LoginForm({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<SignInCubit, SignInState>(
|
||||
listener: (context, state) {
|
||||
if (state.status.isSubmissionFailure) {
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage ?? 'Authentication Failure'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 120),
|
||||
_EmailInput(),
|
||||
const SizedBox(height: 8),
|
||||
_PasswordInput(),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(ForgotPasswordPage.route());
|
||||
},
|
||||
child: Text(
|
||||
'Forgot password ?',
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_LoginWithPasswordButton(),
|
||||
const SizedBox(height: 8),
|
||||
_LoginAnonButton(),
|
||||
const SizedBox(height: 8),
|
||||
_LoginWithGoogleButton(),
|
||||
const SizedBox(height: 8),
|
||||
_SignUpButton(),
|
||||
const SizedBox(height: 8),
|
||||
_SignUpAsProButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,6 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import 'package:example_router/bootstrap.dart';
|
||||
import 'package:example_router/presentation/features/app/app.dart';
|
||||
|
||||
import 'package:authentication_bloc_example/app/app.dart';
|
||||
import 'package:authentication_bloc_example/app/bloc_observer.dart';
|
||||
import 'package:authentication_bloc_example/firebase_options.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
BlocOverrides.runZoned(
|
||||
() => runApp(
|
||||
const App(),
|
||||
),
|
||||
blocObserver: AppBlocObserver(),
|
||||
);
|
||||
void main() {
|
||||
bootstrap(App.new);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// -----
|
||||
// File: sign_in_form.dart
|
||||
// Created Date: 19/08/2022 15:24:37
|
||||
// Last Modified: Fri Nov 11 2022
|
||||
// Last Modified: Wed Nov 16 2022
|
||||
// -----
|
||||
// Copyright (c) 2022
|
||||
|
||||
@@ -16,14 +16,14 @@ class _EmailInput extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InputBuilder<SignInCubit<int>>(
|
||||
field: AuthFormField.email,
|
||||
builder: ((context, cubit, state, field, inputValid) {
|
||||
builder: ((context, cubit, state, field, input) {
|
||||
return TextField(
|
||||
onChanged: (email) => cubit.emailChanged(email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email',
|
||||
helperText: '',
|
||||
errorText: !inputValid ? 'Invalid email' : null,
|
||||
errorText: input.validator.invalid ? 'Invalid email' : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -36,14 +36,14 @@ class _PasswordInput extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InputBuilder<SignInCubit<int>>(
|
||||
field: AuthFormField.password,
|
||||
builder: ((context, cubit, state, field, inputValid) {
|
||||
builder: ((context, cubit, state, field, input) {
|
||||
return TextField(
|
||||
onChanged: (pwd) => cubit.passwordChanged(pwd),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
helperText: '',
|
||||
errorText: !inputValid ? 'Invalid password' : null,
|
||||
errorText: input.validator.invalid ? 'Invalid password' : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -59,8 +59,24 @@ class _SignInButton extends StatelessWidget {
|
||||
return status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: status.isValidated ? () => cubit.submit() : null,
|
||||
child: const Text('Sign in'),
|
||||
onPressed: status.isValidated ? () => cubit.signInWithEmailAndPassword() : null,
|
||||
child: const Text('Sign in with credentials'),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignInAnonymouslyButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SubmitBuilder<SignInCubit<int>>(
|
||||
builder: ((context, cubit, status) {
|
||||
return status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: () => cubit.signInAnonymously(),
|
||||
child: const Text('Sign in anonymously'),
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -80,12 +96,15 @@ class SignInForm extends StatelessWidget {
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_EmailInput(),
|
||||
const SizedBox(height: 8),
|
||||
_PasswordInput(),
|
||||
const SizedBox(height: 16),
|
||||
_SignInButton(),
|
||||
const SizedBox(height: 16),
|
||||
_SignInAnonymouslyButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -3,7 +3,7 @@
|
||||
// -----
|
||||
// File: sign_up_form.dart
|
||||
// Created Date: 19/08/2022 14:41:08
|
||||
// Last Modified: Fri Nov 11 2022
|
||||
// Last Modified: Wed Nov 16 2022
|
||||
// -----
|
||||
// Copyright (c) 2022
|
||||
|
||||
@@ -17,14 +17,14 @@ class _EmailInput extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InputBuilder<SignUpCubit<int>>(
|
||||
field: AuthFormField.email,
|
||||
builder: ((context, cubit, state, field, inputValid) {
|
||||
builder: ((context, cubit, state, field, input) {
|
||||
return TextField(
|
||||
onChanged: (email) => cubit.emailChanged(email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email',
|
||||
helperText: '',
|
||||
errorText: !inputValid ? 'Invalid email' : null,
|
||||
errorText: input.validator.invalid ? 'Invalid email' : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -37,7 +37,7 @@ class _PasswordInput extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InputBuilder<SignUpCubit<int>>(
|
||||
field: AuthFormField.password,
|
||||
builder: ((context, cubit, state, field, inputValid) {
|
||||
builder: ((context, cubit, state, field, input) {
|
||||
return TextField(
|
||||
onChanged: (pwd) {
|
||||
cubit.passwordChanged(pwd);
|
||||
@@ -52,7 +52,7 @@ class _PasswordInput extends StatelessWidget {
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
helperText: '',
|
||||
errorText: !inputValid ? 'Invalid password' : null,
|
||||
errorText: input.validator.invalid ? 'Invalid password' : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -65,7 +65,7 @@ class _ConfirmPasswordInput extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InputBuilder<SignUpCubit<int>>(
|
||||
field: AppFormField.confirmedPassword,
|
||||
builder: ((context, cubit, state, field, inputValid) {
|
||||
builder: ((context, cubit, state, field, input) {
|
||||
return TextField(
|
||||
onChanged: (pwd) {
|
||||
cubit.dataChanged(
|
||||
@@ -80,7 +80,8 @@ class _ConfirmPasswordInput extends StatelessWidget {
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Confirm password',
|
||||
helperText: '',
|
||||
errorText: !inputValid ? 'Passwords do not match' : null,
|
||||
errorText:
|
||||
input.validator.invalid ? 'Passwords do not match' : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -1,325 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:authentication_bloc_example/app/app.dart';
|
||||
import 'package:authentication_bloc_example/constants.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
class _NameInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (name) => context
|
||||
.read<SignUpCubit>()
|
||||
.dataChanged(formFieldName, Name.dirty(name)),
|
||||
keyboardType: TextInputType.name,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'name',
|
||||
helperText: '',
|
||||
errorText:
|
||||
state.data.isNotValid(formFieldName) ? 'invalid name' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PhoneInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (phone) => context
|
||||
.read<SignUpCubit>()
|
||||
.dataChanged(formFieldPhone, Phone.dirty(phone)),
|
||||
keyboardType: TextInputType.phone,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'phone',
|
||||
helperText: '',
|
||||
errorText: state.data.isNotValid(formFieldPhone)
|
||||
? 'invalid phone'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SirenInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (siren) => context
|
||||
.read<SignUpCubit>()
|
||||
.dataChanged(formFieldSiren, Siren.dirty(siren)),
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'siren',
|
||||
helperText: '',
|
||||
errorText: state.data.isNotValid(formFieldSiren)
|
||||
? 'invalid SIREN'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IbanInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (iban) => context
|
||||
.read<SignUpCubit>()
|
||||
.dataChanged(formFieldIban, Iban.dirty(iban)),
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'iban',
|
||||
helperText: '',
|
||||
errorText:
|
||||
state.data.isNotValid(formFieldIban) ? 'invalid IBAN' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmailInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
buildWhen: (previous, current) => previous.email != current.email,
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (email) => context.read<SignUpCubit>().emailChanged(email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'email',
|
||||
helperText: '',
|
||||
errorText: state.email.invalid ? 'invalid email' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PasswordInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (password) {
|
||||
context.read<SignUpCubit>().passwordChanged(password);
|
||||
context.read<SignUpCubit>().dataChanged(
|
||||
formFieldConfirmedPassword,
|
||||
ConfirmedPassword.dirty(
|
||||
password: password,
|
||||
value: context
|
||||
.read<SignUpCubit>()
|
||||
.state
|
||||
.data
|
||||
.valueOf<String>(formFieldConfirmedPassword),
|
||||
),
|
||||
);
|
||||
},
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'password',
|
||||
helperText: '',
|
||||
errorText: state.password.invalid ? 'invalid password' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConfirmPasswordInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (confirmPassword) => context
|
||||
.read<SignUpCubit>()
|
||||
.dataChanged(
|
||||
formFieldConfirmedPassword,
|
||||
ConfirmedPassword.dirty(
|
||||
password: context.read<SignUpCubit>().state.password.value,
|
||||
value: confirmPassword,
|
||||
),
|
||||
),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'confirm password',
|
||||
helperText: '',
|
||||
errorText: state.data.isNotValid(formFieldConfirmedPassword)
|
||||
? 'passwords do not match'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CheckIsProInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: const Text('Are you a pro?'),
|
||||
trailing: BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return Checkbox(
|
||||
value: state.data.valueOf<bool>(formFieldPro),
|
||||
onChanged: (isPro) {
|
||||
final value =
|
||||
isPro!; // tristate is false, so value can't be null
|
||||
|
||||
context.read<SignUpCubit>().dataChanged(
|
||||
formFieldPro,
|
||||
Boolean.dirty(value: value),
|
||||
);
|
||||
|
||||
if (value) {
|
||||
context.read<SignUpCubit>().updateFormData(
|
||||
App.getProFormData(),
|
||||
operation: SetOperation.union);
|
||||
} else {
|
||||
context.read<SignUpCubit>().updateFormData(
|
||||
App.getNormalFormData(),
|
||||
operation: SetOperation.intersection);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignUpButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
buildWhen: (previous, current) => previous.status != current.status,
|
||||
builder: (context, state) {
|
||||
return state.status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: state.status.isValidated
|
||||
? () => context.read<SignUpCubit>().signUpFormSubmitted()
|
||||
: null,
|
||||
child: const Text('SIGN UP'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DebugButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
// log(state.toString());
|
||||
log(state.data.toMap().toString());
|
||||
},
|
||||
child: const Text('DEBUG'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignUpForm extends StatelessWidget {
|
||||
const SignUpForm({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<SignUpCubit, SignUpState>(
|
||||
listener: (context, state) {
|
||||
if (state.status.isSubmissionSuccess) {
|
||||
Navigator.of(context).pop();
|
||||
} else if (state.status.isSubmissionFailure) {
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
SnackBar(content: Text(state.errorMessage ?? 'Sign Up Failure')),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_NameInput(),
|
||||
const SizedBox(height: 8),
|
||||
_PhoneInput(),
|
||||
const SizedBox(height: 8),
|
||||
_CheckIsProInput(),
|
||||
const SizedBox(height: 8),
|
||||
BlocBuilder<SignUpCubit, SignUpState>(
|
||||
builder: (context, state) {
|
||||
if (state.data.valueOf<bool>(formFieldPro)) {
|
||||
return Column(children: [
|
||||
_SirenInput(),
|
||||
const SizedBox(height: 8),
|
||||
_IbanInput(),
|
||||
const SizedBox(height: 8),
|
||||
]);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
_EmailInput(),
|
||||
const SizedBox(height: 8),
|
||||
_PasswordInput(),
|
||||
const SizedBox(height: 8),
|
||||
_ConfirmPasswordInput(),
|
||||
const SizedBox(height: 8),
|
||||
_SignUpButton(),
|
||||
const SizedBox(height: 8),
|
||||
_DebugButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: authentication_bloc_example
|
||||
name: example_router
|
||||
description: A new Flutter project.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
@@ -15,10 +15,10 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.0.1
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.16.2 <3.0.0"
|
||||
sdk: ">=2.17.6 <3.0.0"
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
@@ -29,22 +29,30 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
go_router: ^5.1.5
|
||||
firebase_core: ^2.1.1
|
||||
flutter_bloc: ^8.1.1
|
||||
get_it: ^7.2.0
|
||||
|
||||
firebase_core: ^1.14.1
|
||||
flutter_bloc: ^8.0.1
|
||||
wyatt_authentication_bloc:
|
||||
path: "../"
|
||||
|
||||
wyatt_form_bloc:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_form_bloc-v0.1.0+1
|
||||
path: packages/wyatt_form_bloc
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.2.0
|
||||
|
||||
wyatt_architecture:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.0.2
|
||||
|
||||
wyatt_type_utils:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.0.3+1
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.4
|
||||
cloud_firestore: ^3.1.12
|
||||
cupertino_icons: ^1.0.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -60,7 +68,7 @@ dev_dependencies:
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
@@ -74,7 +82,7 @@ flutter:
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// File generated by FlutterFire CLI.
|
||||
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
|
||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||
import 'package:flutter/foundation.dart'
|
||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||
|
||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// import 'firebase_options.dart';
|
||||
/// // ...
|
||||
/// await Firebase.initializeApp(
|
||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
||||
/// );
|
||||
/// ```
|
||||
class DefaultFirebaseOptions {
|
||||
static FirebaseOptions get currentPlatform {
|
||||
if (kIsWeb) {
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for web - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for android - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.iOS:
|
||||
return ios;
|
||||
case TargetPlatform.macOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for macos - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.windows:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for windows - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.linux:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for linux - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions are not supported for this platform.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static const FirebaseOptions ios = FirebaseOptions(
|
||||
apiKey: 'AIzaSyCDbbhjbFrQwLXuIANdJzjkDk8uOETnn7w',
|
||||
appId: '1:136771801992:ios:bcdca68d2b7d227097203d',
|
||||
messagingSenderId: '136771801992',
|
||||
projectId: 'tchat-beta',
|
||||
databaseURL: 'https://tchat-beta.firebaseio.com',
|
||||
storageBucket: 'tchat-beta.appspot.com',
|
||||
androidClientId: '136771801992-n2pq8oqutvrqj58e05hbavvc7n1jdfjb.apps.googleusercontent.com',
|
||||
iosClientId: '136771801992-p629tpo9bk3hcm2955s5ahivdla57ln9.apps.googleusercontent.com',
|
||||
iosBundleId: 'com.example.exampleRouter',
|
||||
);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import 'package:example_router/bootstrap.dart';
|
||||
import 'package:example_router/presentation/features/app/app.dart';
|
||||
|
||||
void main() {
|
||||
bootstrap(App.new);
|
||||
}
|
||||
@@ -23,7 +23,8 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
class AuthenticationMockDataSourceImpl extends AuthenticationRemoteDataSource {
|
||||
Pair<Account, String>? _connectedMock;
|
||||
Pair<Account, String>? _registeredMock;
|
||||
final StreamController<Account?> _streamAccount = StreamController();
|
||||
final StreamController<Account?> _streamAccount = StreamController()
|
||||
..add(null);
|
||||
|
||||
final List<Pair<Account, String>>? registeredAccounts;
|
||||
final String idToken;
|
||||
|
||||
@@ -44,6 +44,8 @@ class AuthenticationRepositoryImpl<T extends Object>
|
||||
|
||||
final OnAuthChange<T>? _onAccountChanges;
|
||||
|
||||
bool _pause = false; // Semaphore
|
||||
|
||||
AuthenticationRepositoryImpl({
|
||||
required AuthenticationCacheDataSource<T> authenticationCacheDataSource,
|
||||
required AuthenticationRemoteDataSource authenticationRemoteDataSource,
|
||||
@@ -120,6 +122,7 @@ class AuthenticationRepositoryImpl<T extends Object>
|
||||
}) =>
|
||||
Result.tryCatchAsync<Account, AppException, AppException>(
|
||||
() async {
|
||||
_pause = true;
|
||||
final account = await _authenticationRemoteDataSource.signUp(
|
||||
email: email,
|
||||
password: password,
|
||||
@@ -135,9 +138,13 @@ class AuthenticationRepositoryImpl<T extends Object>
|
||||
(error) async => error,
|
||||
);
|
||||
}
|
||||
_pause = false;
|
||||
return account;
|
||||
},
|
||||
(error) => error,
|
||||
(error) {
|
||||
_pause = false;
|
||||
return error;
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -200,7 +207,7 @@ class AuthenticationRepositoryImpl<T extends Object>
|
||||
@override
|
||||
Stream<FutureResult<AccountWrapper<T>>> streamAccount() =>
|
||||
_authenticationRemoteDataSource.streamAccount().map((account) async {
|
||||
if (_onAccountChanges.isNotNull) {
|
||||
if (_onAccountChanges.isNotNull && !_pause) {
|
||||
final dataResult = await _onAccountChanges!.call(account);
|
||||
return dataResult.map((data) => AccountWrapperModel(account, data));
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_authentication_bloc/src/core/constants/form_field.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/core/constants/form_name.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
@@ -82,6 +80,41 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
|
||||
|
||||
@override
|
||||
FutureOr<void> submit() async {
|
||||
throw UnimplementedError('`submit()` is not implemented for SignInCubit, '
|
||||
'please use `signInWithEmailAndPassword()` or `signInAnonymously()`');
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> update(
|
||||
WyattForm form, {
|
||||
SetOperation operation = SetOperation.replace,
|
||||
}) {
|
||||
final WyattForm current = _formRepository.accessForm(formName).clone();
|
||||
final WyattForm newForm = operation.operation.call(current, form);
|
||||
_formRepository.updateForm(newForm);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
form: newForm,
|
||||
status: newForm.validate(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> validate() {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: _formRepository.accessForm(formName).validate(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> signInWithEmailAndPassword() async {
|
||||
if (state.status.isSubmissionInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.status.isValidated) {
|
||||
return;
|
||||
}
|
||||
@@ -117,28 +150,22 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> update(
|
||||
WyattForm form, {
|
||||
SetOperation operation = SetOperation.replace,
|
||||
}) {
|
||||
final WyattForm current = _formRepository.accessForm(formName).clone();
|
||||
final WyattForm newForm = operation.operation.call(current, form);
|
||||
_formRepository.updateForm(newForm);
|
||||
FutureOr<void> signInAnonymously() async {
|
||||
if (state.status.isSubmissionInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||
|
||||
final uid = await _authenticationRepository.signInAnonymously();
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
form: newForm,
|
||||
status: newForm.validate(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> validate() {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: _formRepository.accessForm(formName).validate(),
|
||||
uid.fold(
|
||||
(value) => state.copyWith(status: FormStatus.submissionSuccess),
|
||||
(error) => state.copyWith(
|
||||
errorMessage: error.message,
|
||||
status: FormStatus.submissionFailure,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: wyatt_authentication_bloc
|
||||
description: Authentication BLoC for Flutter
|
||||
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_authentication_bloc
|
||||
version: 0.3.0
|
||||
version: 0.4.0+1
|
||||
|
||||
publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
|
||||
|
||||
@@ -24,15 +24,15 @@ dependencies:
|
||||
|
||||
wyatt_form_bloc:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.1.0+1
|
||||
version: ^0.2.0
|
||||
|
||||
wyatt_architecture:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.0.2
|
||||
version: ^0.0.2
|
||||
|
||||
wyatt_type_utils:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.0.3+1
|
||||
version: ^0.0.3+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -65,6 +65,10 @@ void main() {
|
||||
),
|
||||
).thenAnswer((_) async => Ok(account));
|
||||
|
||||
when(
|
||||
() => authenticationRepository.signInAnonymously(),
|
||||
).thenAnswer((_) async => Ok(account));
|
||||
|
||||
when(
|
||||
() => authenticationRepository.formRepository,
|
||||
).thenAnswer((_) => formRepository);
|
||||
@@ -258,13 +262,38 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
group('submit', () {
|
||||
group('signInWithEmailAndPassword()', () {
|
||||
blocTest<SignInCubit<int>, SignInState>(
|
||||
'does nothing when status is not validated',
|
||||
build: () => SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
act: (cubit) => cubit.submit(),
|
||||
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||
expect: () => const <SignInState>[],
|
||||
);
|
||||
|
||||
blocTest<SignInCubit<int>, SignInState>(
|
||||
'does nothing when status is submitInProgress',
|
||||
build: () => SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.submissionInProgress,
|
||||
),
|
||||
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||
expect: () => const <SignInState>[],
|
||||
);
|
||||
|
||||
@@ -308,7 +337,7 @@ void main() {
|
||||
),
|
||||
status: FormStatus.valid,
|
||||
),
|
||||
act: (cubit) => cubit.submit(),
|
||||
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||
verify: (_) {
|
||||
verify(
|
||||
() => authenticationRepository.signInWithEmailAndPassword(
|
||||
@@ -360,7 +389,7 @@ void main() {
|
||||
),
|
||||
status: FormStatus.valid,
|
||||
),
|
||||
act: (cubit) => cubit.submit(),
|
||||
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||
expect: () => <SignInState>[
|
||||
SignInState(
|
||||
form: WyattFormImpl(
|
||||
@@ -444,7 +473,170 @@ void main() {
|
||||
),
|
||||
status: FormStatus.valid,
|
||||
),
|
||||
act: (cubit) => cubit.submit(),
|
||||
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||
expect: () => <SignInState>[
|
||||
SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.submissionInProgress,
|
||||
),
|
||||
SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.submissionFailure,
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('signInAnonymously()', () {
|
||||
blocTest<SignInCubit<int>, SignInState>(
|
||||
'does nothing when status is submitInProgress',
|
||||
build: () => SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.submissionInProgress,
|
||||
),
|
||||
act: (cubit) => cubit.signInAnonymously(),
|
||||
expect: () => const <SignInState>[],
|
||||
);
|
||||
|
||||
blocTest<SignInCubit<int>, SignInState>(
|
||||
'calls signInAnonymously',
|
||||
build: () => SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
act: (cubit) => cubit.signInAnonymously(),
|
||||
verify: (_) {
|
||||
verify(
|
||||
() => authenticationRepository.signInAnonymously(),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
blocTest<SignInCubit<int>, SignInState>(
|
||||
'emits [submissionInProgress, submissionSuccess] '
|
||||
'when signInAnonymously succeeds',
|
||||
build: () => SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.valid,
|
||||
),
|
||||
act: (cubit) => cubit.signInAnonymously(),
|
||||
expect: () => <SignInState>[
|
||||
SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.submissionInProgress,
|
||||
),
|
||||
SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.submissionSuccess,
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<SignInCubit<int>, SignInState>(
|
||||
'emits [submissionInProgress, submissionFailure] '
|
||||
'when signInAnonymously fails',
|
||||
setUp: () {
|
||||
when(
|
||||
() => authenticationRepository.signInAnonymously(),
|
||||
).thenAnswer((_) async => Err(ServerException()));
|
||||
},
|
||||
build: () => SignInCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignInState(
|
||||
form: WyattFormImpl(
|
||||
[
|
||||
FormInput(
|
||||
AuthFormField.email,
|
||||
const Email.dirty(validEmailString),
|
||||
),
|
||||
FormInput(
|
||||
AuthFormField.password,
|
||||
const Password.dirty(validPasswordString),
|
||||
)
|
||||
],
|
||||
name: AuthFormName.signInForm,
|
||||
),
|
||||
status: FormStatus.valid,
|
||||
),
|
||||
act: (cubit) => cubit.signInAnonymously(),
|
||||
expect: () => <SignInState>[
|
||||
SignInState(
|
||||
form: WyattFormImpl(
|
||||
|
||||
@@ -481,5 +481,178 @@ void main() {
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('update', () {
|
||||
final inputsA = <
|
||||
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
|
||||
dynamic>>[
|
||||
FormInput(AuthFormField.email, const Email.pure()),
|
||||
FormInput(AuthFormField.password, const Password.pure())
|
||||
];
|
||||
|
||||
final inputsB = <
|
||||
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
|
||||
dynamic>>[
|
||||
FormInput('name', const Name.pure()),
|
||||
FormInput('phone', const Phone.pure()),
|
||||
];
|
||||
|
||||
final inputsC = <
|
||||
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
|
||||
dynamic>>[
|
||||
FormInput(AuthFormField.email, const Email.pure()),
|
||||
FormInput(AuthFormField.password, const Password.pure()),
|
||||
FormInput('name', const Name.pure()),
|
||||
FormInput('phone', const Phone.pure()),
|
||||
];
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits no state when apply intersection with the same form',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsA, name: 'otherSignUpForm'),
|
||||
operation: SetOperation.intersection,
|
||||
),
|
||||
expect: () => <SignUpState>[],
|
||||
);
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits no state when apply intersection with a form that '
|
||||
'contains every inputs of actual form',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsC, name: 'otherSignUpForm'),
|
||||
operation: SetOperation.intersection,
|
||||
),
|
||||
expect: () => <SignUpState>[],
|
||||
);
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits the form intersection when apply '
|
||||
'intersection with a smaller form',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsC,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsA, name: 'otherSignUpForm'),
|
||||
operation: SetOperation.intersection,
|
||||
),
|
||||
expect: () => <SignUpState>[
|
||||
SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits no state when apply union with the same form',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsA, name: 'otherSignUpForm'),
|
||||
operation: SetOperation.union,
|
||||
),
|
||||
expect: () => <SignUpState>[],
|
||||
);
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits the form union when apply '
|
||||
'union with an another form',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsB, name: 'otherSignUpForm'),
|
||||
operation: SetOperation.union,
|
||||
),
|
||||
expect: () => <SignUpState>[
|
||||
SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsC,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits no state when apply replace with the same form',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsA, name: AuthFormName.signUpForm),
|
||||
),
|
||||
expect: () => <SignUpState>[],
|
||||
);
|
||||
|
||||
blocTest<SignUpCubit<int>, SignUpState>(
|
||||
'emits a state with a new form after the replacement',
|
||||
build: () => SignUpCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsA,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
act: (cubit) => cubit.update(
|
||||
WyattFormImpl(inputsC, name: AuthFormName.signUpForm),
|
||||
),
|
||||
expect: () => <SignUpState>[
|
||||
SignUpState(
|
||||
form: WyattFormImpl(
|
||||
inputsC,
|
||||
name: AuthFormName.signUpForm,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"psi-header.changes-tracking": {
|
||||
"isActive": true
|
||||
},
|
||||
"psi-header.config": {
|
||||
"blankLinesAfter": 1,
|
||||
"forceToTop": true
|
||||
},
|
||||
"psi-header.lang-config": [
|
||||
{
|
||||
"beforeHeader": [
|
||||
"# -*- coding:utf-8 -*-",
|
||||
"#!/usr/bin/env python3"
|
||||
],
|
||||
"begin": "###",
|
||||
"end": "###",
|
||||
"language": "python",
|
||||
"prefix": "# "
|
||||
},
|
||||
{
|
||||
"beforeHeader": [
|
||||
"#!/usr/bin/env sh",
|
||||
""
|
||||
],
|
||||
"language": "shellscript",
|
||||
"begin": "",
|
||||
"end": "",
|
||||
"prefix": "# "
|
||||
},
|
||||
{
|
||||
"begin": "",
|
||||
"end": "",
|
||||
"language": "dart",
|
||||
"prefix": "// "
|
||||
},
|
||||
{
|
||||
"begin": "",
|
||||
"end": "",
|
||||
"language": "yaml",
|
||||
"prefix": "# "
|
||||
},
|
||||
{
|
||||
"begin": "<!--",
|
||||
"end": "-->",
|
||||
"language": "markdown",
|
||||
},
|
||||
],
|
||||
"psi-header.templates": [
|
||||
{
|
||||
"language": "*",
|
||||
"template": [
|
||||
"Copyright (C) <<year>> WYATT GROUP",
|
||||
"Please see the AUTHORS file for details.",
|
||||
"",
|
||||
"This program is free software: you can redistribute it and/or modify",
|
||||
"it under the terms of the GNU General Public License as published by",
|
||||
"the Free Software Foundation, either version 3 of the License, or",
|
||||
"any later version.",
|
||||
"",
|
||||
"This program is distributed in the hope that it will be useful,",
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of",
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
|
||||
"GNU General Public License for more details.",
|
||||
"",
|
||||
"You should have received a copy of the GNU General Public License",
|
||||
"along with this program. If not, see <https://www.gnu.org/licenses/>."
|
||||
],
|
||||
}
|
||||
],
|
||||
"dart.runPubGetOnPubspecChanges": false,
|
||||
}
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.1.0
|
||||
|
||||
> Note: This release has breaking changes.
|
||||
|
||||
- **REFACTOR**: add vscode config.
|
||||
- **FIX**: update example.
|
||||
- **BREAKING** **REFACTOR**: migrate to wyatt architecture.
|
||||
|
||||
## 0.0.2
|
||||
|
||||
- Migrate crud_package in monorepo
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||
|
||||
analyzer:
|
||||
exclude: "!example/**"
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: 5464c5bac742001448fe4fc0597be939379f88ea
|
||||
revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||
- platform: ios
|
||||
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
**/dgph
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
*.perspectivev3
|
||||
**/*sync/
|
||||
.sconsign.dblite
|
||||
.tags*
|
||||
**/.vagrant/
|
||||
**/DerivedData/
|
||||
Icon?
|
||||
**/Pods/
|
||||
**/.symlinks/
|
||||
profile
|
||||
xcuserdata
|
||||
**/.generated/
|
||||
Flutter/App.framework
|
||||
Flutter/Flutter.framework
|
||||
Flutter/Flutter.podspec
|
||||
Flutter/Generated.xcconfig
|
||||
Flutter/ephemeral/
|
||||
Flutter/app.flx
|
||||
Flutter/app.zip
|
||||
Flutter/flutter_assets/
|
||||
Flutter/flutter_export_environment.sh
|
||||
ServiceDefinitions.json
|
||||
Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
||||