Compare commits

...
Author SHA1 Message Date
hugo a66db23ace chore(release): publish packages
- wyatt_authentication_bloc@0.4.0+1
2022-11-16 17:20:57 -05:00
hugo 3d7af98ac9 fix(auth): add sort of mutex on onAuthChanges callback trigger 2022-11-16 17:16:41 -05:00
AN12345 b033b97917 feat(notification_bloc) add new package for push notification management 2022-11-16 16:58:19 -05:00
hugo 14b7f31b53 chore(form): use hard link for vscode folder 2022-11-16 16:32:05 -05:00
hugo 403bcafe93 chore(form): remove vscode symlink 2022-11-16 16:30:49 -05:00
hugo 21ab6f6021 chore(auth): use hardlink for vscode folder 2022-11-16 16:27:51 -05:00
hugo 198ecac5da chore(auth): remove vscode symlink to fix publish 2022-11-16 16:18:23 -05:00
hugo c945cd435e chore(release): publish packages
- wyatt_authentication_bloc@0.4.0
 - wyatt_form_bloc@0.2.0
2022-11-16 16:03:13 -05:00
hugo 852d29ef0a refactor(auth): update example with new input builders 2022-11-16 16:02:18 -05:00
hugo 08383ec03b refactor(form)!: input builders provides FormInput 2022-11-16 16:00:13 -05:00
hugo ee8ce76839 fix(form): disable status selection and rebuild SubmitBuilder on every state changes #28 2022-11-16 15:51:34 -05:00
hugo f05f2230e9 fix(auth): add initial null account event in stream in the mock authentication 2022-11-16 15:46:07 -05:00
hugo 54002beaab test(auth): add tests for the SignUpCubit update method 2022-11-16 15:45:11 -05:00
hugo d5698fbffc test(form): add formReplace test case #27 2022-11-16 15:43:52 -05:00
hugo 31ad460757 fix(auth)!: add signInWithEmailAndPassword and signInAnonymously methods in SignInCubit #26 2022-11-15 19:25:20 -05:00
hugo 4fd2a2a16c chore(auth): remove old example and rename actual 2022-11-15 18:52:10 -05:00
hugo 6eb40de72c feat(form): add metadata tag to bypass validation #29 2022-11-15 18:48:02 -05:00
hugo cecbc94a95 chore(auth): add global vscode folder symlink 2022-11-15 18:13:56 -05:00
hugo 3820fc4764 chore(auth): remove local vscode folder 2022-11-15 18:13:30 -05:00
hugo 1b893a5b84 test(form): add some tests 2022-11-15 18:10:50 -05:00
hugo 72661a343f fix(form): fix set operations behaviors 2022-11-15 18:10:30 -05:00
hugo 9ebe8e21dc fix(form): add nullable validator on boolean value 2022-11-15 18:09:39 -05:00
hugo 141f15b444 feat(form): add shorthands to access validator status 2022-11-15 18:09:13 -05:00
hugo 3a8a604a48 chore(form): add vscode folder symlink 2022-11-15 18:06:13 -05:00
hugo 41493207b3 chore(form): remove local vscode folder 2022-11-15 18:05:18 -05:00
hugo cbc4a2e149 chore: change vscode global settings 2022-11-15 18:03:30 -05:00
hugo 7345e415a7 chore(release): publish packages
- wyatt_crud_bloc@0.1.0
2022-11-13 20:29:19 -05:00
hugo ff680614aa typo(crud): fix line length 2022-11-13 20:21:20 -05:00
hugo 3e110e2eb5 fix(crud): update example 2022-11-13 20:19:21 -05:00
hugo 1ccb0a540e refactor(crud)!: migrate to wyatt architecture 2022-11-13 20:19:10 -05:00
hugo f9da719b91 refactor(crud): add vscode config 2022-11-13 20:18:10 -05:00
hugo 95f41025f4 chore(release): publish packages
- wyatt_authentication_bloc@0.3.0+1
2022-11-11 19:09:01 -05:00
hugo eceda684f0 fix(auth): fix some bugs to validate publishing 2022-11-11 19:08:28 -05:00
289 changed files with 6309 additions and 2436 deletions
+7 -2
View File
@@ -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,
}
}
+98
View File
@@ -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
+7 -2
View File
@@ -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
@@ -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,
),
),
],
);
});
});
}
+71
View File
@@ -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,
}
+8
View File
@@ -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/**"
+22 -2
View File
@@ -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

Some files were not shown because too many files have changed in this diff Show More