Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c787b065ad | ||
|
|
12f9cf6aa5 | ||
|
|
74db784973 | ||
|
|
fe5fa692f7 | ||
|
|
5ea5c510cb | ||
|
|
bb264b7362 | ||
|
|
17ae855ffb | ||
|
|
b38fea130f | ||
|
|
e3776259df | ||
|
|
371cc33e3a | ||
|
|
3cbc268dea | ||
|
|
99b6a0a335 | ||
|
|
88719576b7 |
@@ -3,6 +3,85 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## 2022-05-02
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`wyatt_form_bloc` - `v0.0.2`](#wyatt_form_bloc---v002)
|
||||
- [`wyatt_authentication_bloc` - `v0.2.0+1`](#wyatt_authentication_bloc---v0201)
|
||||
|
||||
Packages with dependency updates only:
|
||||
|
||||
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||
|
||||
- `wyatt_authentication_bloc` - `v0.2.0+1`
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_form_bloc` - `v0.0.2`
|
||||
|
||||
- **FEAT**: add list option validator.
|
||||
|
||||
|
||||
## 2022-04-20
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`wyatt_crud_bloc` - `v0.0.2`](#wyatt_crud_bloc---v002)
|
||||
- [`wyatt_analysis` - `v2.0.1`](#wyatt_analysis---v201)
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_crud_bloc` - `v0.0.2`
|
||||
|
||||
- Migrate crud_package in monorepo
|
||||
|
||||
- **DOCS**: add example app.
|
||||
|
||||
#### `wyatt_analysis` - `v2.0.1`
|
||||
|
||||
- **DOCS**: Update readme with new version.
|
||||
|
||||
|
||||
## 2022-04-20
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`wyatt_authentication_bloc` - `v0.2.0`](#wyatt_authentication_bloc---v020)
|
||||
|
||||
Packages graduated to a stable release (see pre-releases prior to the stable version for changelog entries):
|
||||
|
||||
- `wyatt_authentication_bloc` - `v0.2.0`
|
||||
|
||||
---
|
||||
|
||||
#### `wyatt_authentication_bloc` - `v0.2.0`
|
||||
|
||||
|
||||
## 2022-04-20
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -63,22 +63,55 @@ dart pub global activate melos
|
||||
|
||||
Then bootstrap with `melos bs`.
|
||||
|
||||
---
|
||||
### Create a new package
|
||||
|
||||
## Usage
|
||||
Create a new package in `packages/` folder.
|
||||
|
||||
You can add any package of the `packages/` sub directory in your project.
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
wyatt_analysis:
|
||||
git:
|
||||
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-packages.git
|
||||
ref: wyatt_analysis-v2.0.0
|
||||
path: packages/wyatt_analysis
|
||||
```shell
|
||||
dart create -t package-simple wyatt_<name>
|
||||
```
|
||||
|
||||
Here you can change `package name` and `package version`.
|
||||
Remove any `example/` subfolder. Then create a new sample project.
|
||||
|
||||
```shell
|
||||
flutter create --platforms android --project-name <name>_example example
|
||||
```
|
||||
|
||||
Then bootstrap project with `melos bs` command.
|
||||
|
||||
### Convention
|
||||
|
||||
#### Naming
|
||||
|
||||
In the previous instructions `<name>` variable is important.
|
||||
It have to be clear and intelligible.
|
||||
|
||||
You **MUST** use underscores.
|
||||
|
||||
You **MUST** use `wyatt` prefix for package.
|
||||
|
||||
You **MUST** name example with specific name.
|
||||
|
||||
For example, if name is CRUD BLOC
|
||||
- name will be crud_bloc
|
||||
- so the package will be: `wyatt_crud_bloc`
|
||||
- and the example will be: `crud_bloc_example`
|
||||
|
||||
#### Commits
|
||||
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
tl;dr : `type(scope): description` where type can be **feat, fix, docs, chores, ci, perf, refactor, style, test**, and scope is package name.
|
||||
|
||||
examples:
|
||||
|
||||
- `feat(auth): add AWS support.` = add a feature in authentication_bloc package.
|
||||
- `docs: update readme.` = update **this** readme file.
|
||||
- `fix(crud)!: fix bug in awesome() function.` = fix a bug, `!` is important and indicate `BREAKING CHANGES`.
|
||||
|
||||
Conventional commits are important for `melos version` command !
|
||||
|
||||
#### Badging
|
||||
|
||||
In the package `readme.md` file, please specify the supported SDK:
|
||||
|
||||
@@ -97,6 +130,23 @@ or
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
You can add any package of the `packages/` sub directory in your project.
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
wyatt_analysis:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_analysis-v2.0.0
|
||||
path: packages/wyatt_analysis
|
||||
```
|
||||
|
||||
Here you can change `package name` and `package version`.
|
||||
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||

|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 2.0.1
|
||||
|
||||
- **DOCS**: Update readme with new version.
|
||||
|
||||
## 2.0.0
|
||||
|
||||
> Note: This release has breaking changes.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: wyatt_analysis
|
||||
description: Lint rules for Dart and Flutter used internally at Wyatt and Wyatt Studio.
|
||||
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis
|
||||
version: 2.0.0
|
||||
version: 2.0.1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.2.0+1
|
||||
|
||||
- Update a dependency to the latest release.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
|
||||
|
||||
## 0.2.0-dev.0
|
||||
|
||||
> Note: This release has breaking changes.
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
include: package:wyatt_analysis/analysis_options.flutter.1.1.1.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||
@@ -44,3 +44,5 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
lib/firebase_options.dart
|
||||
@@ -47,7 +47,7 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.authentication_bloc_example"
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
@@ -42,6 +42,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:136771801992:android:d20e0361057e815197203d",
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
// 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';
|
||||
@@ -55,14 +57,32 @@ class App extends StatelessWidget {
|
||||
if (user.isNotEmpty && !user.isAnonymous) {
|
||||
// Check if user is register in Firesore.
|
||||
DocumentSnapshot firestoreUser = await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.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.fromMap(firestoreUser.data() as Map<String, dynamic>),
|
||||
'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 {};
|
||||
}
|
||||
@@ -73,7 +93,11 @@ class App extends StatelessWidget {
|
||||
if (uid != null) {
|
||||
final data = state.data.toMap();
|
||||
final user = {'uid': uid, 'email': state.email.value, ...data};
|
||||
await FirebaseFirestore.instance.collection('users').doc(uid).set(user);
|
||||
log('onSignUpSuccess: $user');
|
||||
await FirebaseFirestore.instance
|
||||
.collection(firestoreCollectionUsers)
|
||||
.doc(uid)
|
||||
.set(user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,3 +20,5 @@ const String formFieldPro = 'isPro';
|
||||
const String formFieldConfirmedPassword = 'confirmedPassword';
|
||||
const String formFieldSiren = 'siren';
|
||||
const String formFieldIban = 'iban';
|
||||
|
||||
const String firestoreCollectionUsers = 'authentication_bloc_users';
|
||||
@@ -1,53 +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:
|
||||
return android;
|
||||
case TargetPlatform.iOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for ios - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.macOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for macos - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions are not supported for this platform.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static const FirebaseOptions android = FirebaseOptions(
|
||||
apiKey: 'AIzaSyAYS14uXupkS158Q5QAFP1864UrUN_yDSk',
|
||||
appId: '1:136771801992:android:4ff419f07afdad6097203d',
|
||||
messagingSenderId: '136771801992',
|
||||
projectId: 'tchat-beta',
|
||||
databaseURL: 'https://tchat-beta.firebaseio.com',
|
||||
storageBucket: 'tchat-beta.appspot.com',
|
||||
);
|
||||
}
|
||||
@@ -102,6 +102,24 @@ class _LoginWithPasswordButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -182,6 +200,8 @@ class LoginForm extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
_LoginAnonButton(),
|
||||
const SizedBox(height: 8),
|
||||
_LoginWithGoogleButton(),
|
||||
const SizedBox(height: 8),
|
||||
_SignUpButton(),
|
||||
const SizedBox(height: 8),
|
||||
_SignUpAsProButton(),
|
||||
|
||||
@@ -255,7 +255,8 @@ class _DebugButton extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
log(state.toString());
|
||||
// log(state.toString());
|
||||
log(state.data.toMap().toString());
|
||||
},
|
||||
child: const Text('DEBUG'),
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ dependencies:
|
||||
wyatt_form_bloc:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_form_bloc-v0.0.1-dev.0
|
||||
ref: wyatt_form_bloc-v0.0.2
|
||||
path: packages/wyatt_form_bloc
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
||||
+37
-3
@@ -106,10 +106,12 @@ class SignInAnonymouslyFailureFirebase
|
||||
}
|
||||
}
|
||||
|
||||
class SignInWithGoogleFailureFirebase extends SignInWithGoogleFailureInterface {
|
||||
SignInWithGoogleFailureFirebase([String? code, String? message])
|
||||
class SignInWithCredentialFailureFirebase
|
||||
extends SignInWithCredentialFailureInterface {
|
||||
SignInWithCredentialFailureFirebase([String? code, String? message])
|
||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||
SignInWithGoogleFailureFirebase.fromCode(String code) : super.fromCode(code) {
|
||||
SignInWithCredentialFailureFirebase.fromCode(String code)
|
||||
: super.fromCode(code) {
|
||||
switch (code) {
|
||||
case 'account-exists-with-different-credential':
|
||||
message = 'Account exists with different credentials.';
|
||||
@@ -143,6 +145,38 @@ class SignInWithGoogleFailureFirebase extends SignInWithGoogleFailureInterface {
|
||||
}
|
||||
}
|
||||
|
||||
class SignInWithGoogleFailureFirebase
|
||||
extends SignInWithCredentialFailureFirebase
|
||||
implements SignInWithGoogleFailureInterface {
|
||||
SignInWithGoogleFailureFirebase([String? code, String? message])
|
||||
: super(code, message);
|
||||
SignInWithGoogleFailureFirebase.fromCode(String code) : super.fromCode(code);
|
||||
}
|
||||
|
||||
class SignInWithFacebookFailureFirebase
|
||||
extends SignInWithCredentialFailureFirebase
|
||||
implements SignInWithFacebookFailureInterface {
|
||||
SignInWithFacebookFailureFirebase([String? code, String? message])
|
||||
: super(code, message);
|
||||
SignInWithFacebookFailureFirebase.fromCode(String code)
|
||||
: super.fromCode(code);
|
||||
}
|
||||
|
||||
class SignInWithAppleFailureFirebase extends SignInWithCredentialFailureFirebase
|
||||
implements SignInWithAppleFailureInterface {
|
||||
SignInWithAppleFailureFirebase([String? code, String? message])
|
||||
: super(code, message);
|
||||
SignInWithAppleFailureFirebase.fromCode(String code) : super.fromCode(code);
|
||||
}
|
||||
|
||||
class SignInWithTwitterFailureFirebase extends SignInWithCredentialFailureFirebase
|
||||
implements SignInWithAppleFailureInterface {
|
||||
SignInWithTwitterFailureFirebase([String? code, String? message])
|
||||
: super(code, message);
|
||||
SignInWithTwitterFailureFirebase.fromCode(String code) : super.fromCode(code);
|
||||
}
|
||||
|
||||
|
||||
class SignInWithEmailLinkFailureFirebase
|
||||
extends SignInWithEmailLinkFailureInterface {
|
||||
SignInWithEmailLinkFailureFirebase([String? code, String? message])
|
||||
|
||||
+55
@@ -64,6 +64,20 @@ abstract class FetchSignInMethodsForEmailFailureInterface
|
||||
: super.fromCode(code);
|
||||
}
|
||||
|
||||
/// {@template sign_in_with_credential_failure}
|
||||
/// Thrown during the sign in process if a failure occurs.
|
||||
/// {@endtemplate}
|
||||
abstract class SignInWithCredentialFailureInterface
|
||||
extends AuthenticationFailureInterface {
|
||||
/// {@macro sign_in_with_credential_failure}
|
||||
SignInWithCredentialFailureInterface(String code, String message)
|
||||
: super(code, message);
|
||||
|
||||
/// {@macro sign_in_with_credential_failure}
|
||||
SignInWithCredentialFailureInterface.fromCode(String code)
|
||||
: super.fromCode(code);
|
||||
}
|
||||
|
||||
/// {@template sign_in_anonymously_failure}
|
||||
/// Thrown during the sign in process if a failure occurs.
|
||||
/// {@endtemplate}
|
||||
@@ -91,6 +105,47 @@ abstract class SignInWithGoogleFailureInterface
|
||||
SignInWithGoogleFailureInterface.fromCode(String code) : super.fromCode(code);
|
||||
}
|
||||
|
||||
/// {@template sign_in_with_facebook_failure}
|
||||
/// Thrown during the sign in process if a failure occurs.
|
||||
/// {@endtemplate}
|
||||
abstract class SignInWithFacebookFailureInterface
|
||||
extends AuthenticationFailureInterface {
|
||||
/// {@macro sign_in_with_facebook_failure}
|
||||
SignInWithFacebookFailureInterface(String code, String message)
|
||||
: super(code, message);
|
||||
|
||||
/// {@macro sign_in_with_facebook_failure}
|
||||
SignInWithFacebookFailureInterface.fromCode(String code)
|
||||
: super.fromCode(code);
|
||||
}
|
||||
|
||||
/// {@template sign_in_with_apple_failure}
|
||||
/// Thrown during the sign in process if a failure occurs.
|
||||
/// {@endtemplate}
|
||||
abstract class SignInWithAppleFailureInterface
|
||||
extends AuthenticationFailureInterface {
|
||||
/// {@macro sign_in_with_apple_failure}
|
||||
SignInWithAppleFailureInterface(String code, String message)
|
||||
: super(code, message);
|
||||
|
||||
/// {@macro sign_in_with_apple_failure}
|
||||
SignInWithAppleFailureInterface.fromCode(String code) : super.fromCode(code);
|
||||
}
|
||||
|
||||
/// {@template sign_in_with_twitter_failure}
|
||||
/// Thrown during the sign in process if a failure occurs.
|
||||
/// {@endtemplate}
|
||||
abstract class SignInWithTwitterFailureInterface
|
||||
extends AuthenticationFailureInterface {
|
||||
/// {@macro sign_in_with_twitter_failure}
|
||||
SignInWithTwitterFailureInterface(String code, String message)
|
||||
: super(code, message);
|
||||
|
||||
/// {@macro sign_in_with_twitter_failure}
|
||||
SignInWithTwitterFailureInterface.fromCode(String code)
|
||||
: super.fromCode(code);
|
||||
}
|
||||
|
||||
/// {@template sign_in_with_email_link_failure}
|
||||
/// Thrown during the sign in process if a failure occurs.
|
||||
/// {@endtemplate}
|
||||
|
||||
@@ -64,6 +64,9 @@ class UserFirebase implements UserInterface {
|
||||
@override
|
||||
String get uid => _user?.uid ?? '';
|
||||
|
||||
@override
|
||||
String? get providerId => _user?.providerData.first.providerId;
|
||||
|
||||
@override
|
||||
bool? get isNewUser {
|
||||
if (_user?.metadata.lastSignInTime == null ||
|
||||
|
||||
@@ -71,6 +71,9 @@ abstract class UserInterface {
|
||||
/// The user's unique ID.
|
||||
String get uid;
|
||||
|
||||
/// The provider ID for the user.
|
||||
String? get providerId;
|
||||
|
||||
/// Whether the user account has been recently created.
|
||||
bool? get isNewUser;
|
||||
|
||||
|
||||
+111
-5
@@ -15,19 +15,28 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||
import 'package:twitter_login/twitter_login.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_firebase.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/models/user/user_firebase.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/utils/cryptography.dart';
|
||||
|
||||
class AuthenticationRepositoryFirebase
|
||||
implements AuthenticationRepositoryInterface {
|
||||
final FirebaseAuth _firebaseAuth;
|
||||
final TwitterLogin? _twitterLogin;
|
||||
|
||||
UserFirebase _userCache = const UserFirebase.empty();
|
||||
|
||||
AuthenticationRepositoryFirebase({FirebaseAuth? firebaseAuth})
|
||||
: _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance;
|
||||
AuthenticationRepositoryFirebase({
|
||||
FirebaseAuth? firebaseAuth,
|
||||
TwitterLogin? twitterLogin,
|
||||
}) : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
|
||||
_twitterLogin = twitterLogin;
|
||||
|
||||
@override
|
||||
Stream<UserInterface> get user {
|
||||
@@ -99,9 +108,106 @@ class AuthenticationRepositoryFirebase
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> signInWithGoogle() {
|
||||
// TODO(hpcl): implement signInWithGoogle
|
||||
throw UnimplementedError();
|
||||
Future<void> signInWithGoogle() async {
|
||||
// Trigger the authentication flow
|
||||
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
|
||||
|
||||
// Obtain the auth details from the request
|
||||
final GoogleSignInAuthentication? googleAuth =
|
||||
await googleUser?.authentication;
|
||||
|
||||
// Create a new credential
|
||||
final credential = GoogleAuthProvider.credential(
|
||||
accessToken: googleAuth?.accessToken,
|
||||
idToken: googleAuth?.idToken,
|
||||
);
|
||||
|
||||
try {
|
||||
await _firebaseAuth.signInWithCredential(credential);
|
||||
} on FirebaseAuthException catch (e) {
|
||||
throw SignInWithGoogleFailureFirebase.fromCode(e.code);
|
||||
} catch (_) {
|
||||
throw SignInWithGoogleFailureFirebase();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> signInWithFacebook() async {
|
||||
// Trigger the sign-in flow
|
||||
final LoginResult loginResult = await FacebookAuth.instance.login();
|
||||
|
||||
// Create a credential from the access token
|
||||
final OAuthCredential credential =
|
||||
FacebookAuthProvider.credential(loginResult.accessToken?.token ?? '');
|
||||
|
||||
try {
|
||||
await _firebaseAuth.signInWithCredential(credential);
|
||||
} on FirebaseAuthException catch (e) {
|
||||
throw SignInWithFacebookFailureFirebase.fromCode(e.code);
|
||||
} catch (_) {
|
||||
throw SignInWithFacebookFailureFirebase();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> signInWithApple() async {
|
||||
// To prevent replay attacks with the credential returned from Apple, we
|
||||
// include a nonce in the credential request. When signing in with
|
||||
// Firebase, the nonce in the id token returned by Apple, is expected to
|
||||
// match the sha256 hash of `rawNonce`.
|
||||
final rawNonce = Cryptography.generateNonce();
|
||||
final nonce = Cryptography.sha256ofString(rawNonce);
|
||||
|
||||
// Request credential for the currently signed in Apple account.
|
||||
final appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final credential = OAuthProvider('apple.com').credential(
|
||||
idToken: appleCredential.identityToken,
|
||||
rawNonce: rawNonce,
|
||||
);
|
||||
|
||||
// Sign in the user with Firebase. If the nonce we generated earlier does
|
||||
// not match the nonce in `appleCredential.identityToken`,
|
||||
// sign in will fail.
|
||||
try {
|
||||
await _firebaseAuth.signInWithCredential(credential);
|
||||
} on FirebaseAuthException catch (e) {
|
||||
throw SignInWithAppleFailureFirebase.fromCode(e.code);
|
||||
} catch (_) {
|
||||
throw SignInWithAppleFailureFirebase();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> signInWithTwitter() async {
|
||||
final twitterLogin = _twitterLogin;
|
||||
if (twitterLogin == null) {
|
||||
throw SignInWithTwitterFailureFirebase();
|
||||
}
|
||||
|
||||
// Trigger the sign-in flow
|
||||
final authResult = await twitterLogin.login();
|
||||
|
||||
// Create a credential from the access token
|
||||
final credential = TwitterAuthProvider.credential(
|
||||
accessToken: authResult.authToken!,
|
||||
secret: authResult.authTokenSecret!,
|
||||
);
|
||||
|
||||
try {
|
||||
await _firebaseAuth.signInWithCredential(credential);
|
||||
} on FirebaseAuthException catch (e) {
|
||||
throw SignInWithCredentialFailureFirebase.fromCode(e.code);
|
||||
} catch (_) {
|
||||
throw SignInWithCredentialFailureFirebase();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
+15
@@ -61,6 +61,21 @@ abstract class AuthenticationRepositoryInterface {
|
||||
/// Throws a [SignInWithGoogleFailureInterface] if an exception occurs.
|
||||
Future<void> signInWithGoogle();
|
||||
|
||||
/// Starts the Sign In with Facebook Flow.
|
||||
///
|
||||
/// Throws a [SignInWithFacebookFailureInterface] if an exception occurs.
|
||||
Future<void> signInWithFacebook();
|
||||
|
||||
/// Starts the Sign In with Apple Flow.
|
||||
///
|
||||
/// Throws a [SignInWithAppleFailureInterface] if an exception occurs.
|
||||
Future<void> signInWithApple();
|
||||
|
||||
/// Starts the Sign In with Twitter Flow.
|
||||
///
|
||||
/// Throws a [SignInWithTwitterFailureInterface] if an exception occurs.
|
||||
Future<void> signInWithTwitter();
|
||||
|
||||
/// Signs in using an email address and email sign-in link.
|
||||
///
|
||||
/// Throws a [SignInWithEmailLinkFailureInterface] if an exception occurs.
|
||||
|
||||
@@ -76,6 +76,28 @@ class SignInCubit extends Cubit<SignInState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> signInWithGoogle() async {
|
||||
if (state.status.isSubmissionInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||
try {
|
||||
await _authenticationRepository.signInWithGoogle();
|
||||
_authenticationCubit.start();
|
||||
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
||||
} on SignInWithGoogleFailureInterface catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
errorMessage: e.message,
|
||||
status: FormStatus.submissionFailure,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
emit(state.copyWith(status: FormStatus.submissionFailure));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> signInWithEmailAndPassword() async {
|
||||
if (!state.status.isValidated) return;
|
||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||
|
||||
@@ -45,4 +45,14 @@ class SignInState extends Equatable {
|
||||
|
||||
@override
|
||||
List<Object> get props => [email, password, status];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
email: $email,
|
||||
password: $password,
|
||||
status: $status,
|
||||
errorMessage: $errorMessage,
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
class Cryptography {
|
||||
/// Generates a cryptographically secure random nonce, to be included in a
|
||||
/// credential request.
|
||||
static String generateNonce([int length = 32]) {
|
||||
const charset =
|
||||
'0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
|
||||
final random = Random.secure();
|
||||
return List.generate(length, (_) => charset[random.nextInt(charset.length)])
|
||||
.join();
|
||||
}
|
||||
|
||||
/// Returns the sha256 hash of [input] in hex notation.
|
||||
static String sha256ofString(String input) {
|
||||
final bytes = utf8.encode(input);
|
||||
final digest = sha256.convert(bytes);
|
||||
return digest.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +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.2.0-dev.0
|
||||
|
||||
publish_to: none
|
||||
version: 0.2.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.15.1 <3.0.0"
|
||||
@@ -13,14 +11,19 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
crypto: ^3.0.2
|
||||
flutter_bloc: ^8.0.1
|
||||
equatable: ^2.0.3
|
||||
firebase_auth: ^3.3.14
|
||||
firebase_auth: ^3.3.17
|
||||
google_sign_in: ^5.3.0
|
||||
flutter_facebook_auth: ^4.3.0
|
||||
sign_in_with_apple: ^3.3.0
|
||||
twitter_login: ^4.2.3
|
||||
|
||||
wyatt_form_bloc:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_form_bloc-v0.0.1-dev.0
|
||||
ref: wyatt_form_bloc-v0.0.2
|
||||
path: packages/wyatt_form_bloc
|
||||
|
||||
|
||||
@@ -32,5 +35,5 @@ dev_dependencies:
|
||||
wyatt_analysis:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_analysis-v2.0.0
|
||||
ref: wyatt_analysis-v2.0.1
|
||||
path: packages/wyatt_analysis
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Files and directories created by pub.
|
||||
.dart_tool/
|
||||
.packages
|
||||
|
||||
# Conventional directory for build outputs.
|
||||
build/
|
||||
|
||||
# Omit committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
||||
@@ -0,0 +1,9 @@
|
||||
## 0.0.2
|
||||
|
||||
- Migrate crud_package in monorepo
|
||||
|
||||
- **DOCS**: add example app.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial version.
|
||||
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
@@ -0,0 +1 @@
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||
@@ -0,0 +1,48 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
/lib/firebase_options.dart
|
||||
@@ -0,0 +1,10 @@
|
||||
# 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.
|
||||
|
||||
version:
|
||||
revision: 5464c5bac742001448fe4fc0597be939379f88ea
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
@@ -0,0 +1,16 @@
|
||||
# crud_bloc_example
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
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)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@@ -0,0 +1,29 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,13 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
@@ -0,0 +1,71 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
// START: FlutterFire Configuration
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
// END: FlutterFire Configuration
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.crud_bloc_example"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"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-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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:136771801992:android:d20e0361057e815197203d",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.example"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "136771801992-n2pq8oqutvrqj58e05hbavvc7n1jdfjb.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.example.example",
|
||||
"certificate_hash": "5d8790309e13b68c35e5d4d8437c35c6d15e6131"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "136771801992-pj9s88ao7d99pnp4ace75nj1abtmoq7e.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.example.example",
|
||||
"certificate_hash": "bc8faf31c0b0085dc358b2319684e10c93f42719"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.crud_bloc_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,34 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.crud_bloc_example">
|
||||
<application
|
||||
android:label="crud_bloc_example"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.example.crud_bloc_example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- 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 -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- 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 -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.crud_bloc_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,34 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
// START: FlutterFire Configuration
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
// END: FlutterFire Configuration
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#Fri Jun 23 08:50:38 CEST 2017
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
include ':app'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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:math';
|
||||
|
||||
import 'package:crud_bloc_example/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _userRepository = CrudRepositoryFirestore<UserFirestore>(
|
||||
'users_crud',
|
||||
UserFirestore.parser(),
|
||||
);
|
||||
|
||||
final _userCubit = CrudCubit<UserFirestore>(_userRepository);
|
||||
|
||||
return RepositoryProvider<CrudRepositoryFirestore<UserFirestore>>(
|
||||
create: (context) => _userRepository,
|
||||
child: BlocProvider<CrudCubit<UserFirestore>>(
|
||||
create: (context) => _userCubit,
|
||||
child: MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: const MyHomePage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatelessWidget {
|
||||
const MyHomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Flutter Demo Home Page'),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
const Text("Data:"),
|
||||
CrudStreamBuilder<UserFirestore>(
|
||||
onError: (context, state) => const SizedBox.shrink(),
|
||||
onLoading: (context, state) => const Text("Loading..."),
|
||||
onStream: (context, data) {
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: data.length,
|
||||
itemBuilder: (context, index) {
|
||||
final user = data.elementAt(index);
|
||||
return ListTile(
|
||||
title: Text(user?.name ?? 'Error'),
|
||||
subtitle: Text(user?.id ?? 'Error'),
|
||||
onTap: () {
|
||||
context.read<CrudCubit<UserFirestore>>().get(
|
||||
(user?.id)!,
|
||||
);
|
||||
},
|
||||
onLongPress: () {
|
||||
context.read<CrudCubit<UserFirestore>>().delete(
|
||||
(user?.id)!,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
BlocBuilder<CrudCubit<UserFirestore>, CrudState<UserFirestore>>(
|
||||
builder: (context, state) {
|
||||
return Center(
|
||||
child: Text(
|
||||
state.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
final r = Random().nextInt(1000);
|
||||
context.read<CrudCubit<UserFirestore>>().create(
|
||||
UserFirestore(
|
||||
name: 'Wyatt $r',
|
||||
email: '$r@wyattapp.io',
|
||||
phone: '06$r'),
|
||||
);
|
||||
},
|
||||
child: const Text("Create"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<CrudCubit<UserFirestore>>().deleteAll();
|
||||
},
|
||||
child: const Text("DeleteAll"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<CrudCubit<UserFirestore>>().getAll();
|
||||
},
|
||||
child: const Text("GetAll"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context
|
||||
.read<CrudCubit<UserFirestore>>()
|
||||
.query([LimitQueryFirestore(1)]);
|
||||
},
|
||||
child: const Text("Query"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<CrudCubit<UserFirestore>>().streamOf();
|
||||
},
|
||||
child: const Text("StreamOf"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context
|
||||
.read<CrudCubit<UserFirestore>>()
|
||||
.updateAll({'updated': DateTime.now()});
|
||||
},
|
||||
child: const Text("UpdateAll"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<CrudCubit<UserFirestore>>().reset();
|
||||
},
|
||||
child: const Text("Reset"),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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:crud_bloc_example/app.dart';
|
||||
import 'package:crud_bloc_example/firebase_options.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
|
||||
|
||||
class UserFirestore extends Model<DocumentSnapshot, UserFirestore> {
|
||||
@override
|
||||
String? id;
|
||||
|
||||
String? name;
|
||||
String? email;
|
||||
String? phone;
|
||||
|
||||
UserFirestore({
|
||||
required this.name,
|
||||
required this.email,
|
||||
required this.phone,
|
||||
this.id,
|
||||
});
|
||||
UserFirestore._();
|
||||
|
||||
factory UserFirestore.parser() {
|
||||
return UserFirestore._();
|
||||
}
|
||||
|
||||
@override
|
||||
UserFirestore? from(DocumentSnapshot? object) {
|
||||
if (object == null) return null;
|
||||
if (object.exists) {
|
||||
return UserFirestore(
|
||||
id: object.id,
|
||||
name: (object.data() as Map<String, dynamic>?)!['name'] as String,
|
||||
email: (object.data() as Map<String, dynamic>?)!['email'] as String,
|
||||
phone: (object.data() as Map<String, dynamic>?)!['phone'] as String,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object> toMap() {
|
||||
return {
|
||||
'name': name ?? '',
|
||||
'email': email ?? '',
|
||||
'phone': phone ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'UserFirestore(id: $id, name: $name, email: $email, phone: $phone)';
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
name: crud_bloc_example
|
||||
description: A new Flutter project.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# 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.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.16.2 <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
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
cloud_firestore: ^3.1.12
|
||||
firebase_core: ^1.14.1
|
||||
flutter_bloc: ^8.0.1
|
||||
wyatt_crud_bloc:
|
||||
path: "../"
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^1.0.0
|
||||
|
||||
# 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.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - 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.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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/>.
|
||||
|
||||
export 'crud_builder.dart';
|
||||
export 'crud_stream_builder.dart';
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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_crud_bloc/src/crud/cubit/crud_cubit.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/model.dart';
|
||||
|
||||
class CrudBuilder<T extends Model<Object?, T>> extends StatelessWidget {
|
||||
const CrudBuilder({
|
||||
Key? key,
|
||||
this.onIdle,
|
||||
required this.onLoading,
|
||||
required this.onError,
|
||||
required this.onSuccess,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget Function(BuildContext, CrudState<T>)? onIdle;
|
||||
final Widget Function(BuildContext, CrudState<T>) onLoading;
|
||||
final Widget Function(BuildContext, CrudState<T>) onError;
|
||||
final Widget Function(BuildContext, CrudState<T>) onSuccess;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<CrudCubit<T>, CrudState<T>>(
|
||||
builder: (context, state) {
|
||||
switch (state.status) {
|
||||
case CrudStatus.idle:
|
||||
return onIdle?.call(context, state) ?? onError.call(context, state);
|
||||
case CrudStatus.loading:
|
||||
return onLoading.call(context, state);
|
||||
case CrudStatus.failure:
|
||||
return onError.call(context, state);
|
||||
case CrudStatus.success:
|
||||
return onSuccess.call(context, state);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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_crud_bloc/src/crud/cubit/crud_cubit.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/model.dart';
|
||||
|
||||
class CrudStreamBuilder<T extends Model<Object?, T>> extends StatelessWidget {
|
||||
const CrudStreamBuilder({
|
||||
Key? key,
|
||||
required this.onLoading,
|
||||
required this.onError,
|
||||
required this.onStream,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget Function(BuildContext, List<T?>) onStream;
|
||||
final Widget Function(BuildContext, CrudState<T>) onLoading;
|
||||
final Widget Function(BuildContext, CrudState<T>) onError;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<CrudCubit<T>, CrudState<T>>(
|
||||
builder: (context, state) {
|
||||
if (state.stream != null) {
|
||||
return StreamBuilder<List<T?>>(
|
||||
stream: state.stream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return onStream(context, snapshot.data!);
|
||||
} else {
|
||||
return onLoading(context, state);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return onError(context, state);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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/>.
|
||||
|
||||
export 'builder/builder.dart';
|
||||
export 'cubit/crud_cubit.dart';
|
||||
@@ -0,0 +1,219 @@
|
||||
// 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:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart';
|
||||
import 'package:wyatt_crud_bloc/src/repositories/crud_repository_interface.dart';
|
||||
|
||||
part 'crud_state.dart';
|
||||
|
||||
class CrudCubit<T extends Model<Object?, T>> extends Cubit<CrudState<T>> {
|
||||
final CrudRepositoryInterface<T> _crudRepository;
|
||||
|
||||
// ignore: prefer_const_constructors
|
||||
CrudCubit(this._crudRepository) : super(CrudState());
|
||||
// Here we can't use `const` because we need the generic type T
|
||||
|
||||
void reset() {
|
||||
// ignore: prefer_const_constructors
|
||||
emit(CrudState());
|
||||
// Same here, because of `const` we can't use T generic type
|
||||
}
|
||||
|
||||
Future<void> create(Model object, {String? id}) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
await _crudRepository.create(object, id: id);
|
||||
final data = state.data..addAll([object].cast<T>());
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: data,
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
// TODO(hpcl): implement Exception
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> delete(String id) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
await _crudRepository.delete(id);
|
||||
final data = state.data
|
||||
..removeWhere((element) => element!.id != null && element.id == id);
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: data,
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteAll() async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
await _crudRepository.deleteAll();
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: [],
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> get(String id) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
final data = await _crudRepository.get(id);
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: [data].cast<T>(),
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getAll() async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
final data = await _crudRepository.getAll();
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: data.cast<T>(),
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> query(List<QueryInterface> conditions) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
final data = await _crudRepository.query(conditions);
|
||||
emit(
|
||||
state.copyWith(
|
||||
data: data.cast<T>(),
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> streamOf({String? id}) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
final Stream<List<T?>> data = _crudRepository.stream(id: id);
|
||||
emit(
|
||||
state.copyWith(
|
||||
stream: data,
|
||||
status: CrudStatus.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> update(
|
||||
String id, {
|
||||
Model? object,
|
||||
Map<String, dynamic>? raw,
|
||||
}) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
await _crudRepository.update(
|
||||
id,
|
||||
object: object,
|
||||
raw: raw,
|
||||
);
|
||||
emit(state.copyWith(status: CrudStatus.success));
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAll(Map<String, Object?> raw) async {
|
||||
emit(state.copyWith(status: CrudStatus.loading));
|
||||
try {
|
||||
await _crudRepository.updateAll(raw);
|
||||
emit(state.copyWith(status: CrudStatus.success));
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: CrudStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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/>.
|
||||
|
||||
part of 'crud_cubit.dart';
|
||||
|
||||
enum CrudStatus {
|
||||
idle,
|
||||
loading,
|
||||
success,
|
||||
failure,
|
||||
}
|
||||
|
||||
class CrudState<T> extends Equatable {
|
||||
final CrudStatus status;
|
||||
final List<T?> data;
|
||||
final Stream<List<T?>>? stream;
|
||||
final String? errorMessage;
|
||||
|
||||
const CrudState({
|
||||
this.status = CrudStatus.idle,
|
||||
this.data = const [],
|
||||
this.stream,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, data];
|
||||
|
||||
CrudState<T> copyWith({
|
||||
CrudStatus? status,
|
||||
List<T?>? data,
|
||||
Stream<List<T?>>? stream,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return CrudState<T>(
|
||||
status: status ?? this.status,
|
||||
data: data ?? this.data,
|
||||
stream: stream ?? this.stream,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
// ignore: lines_longer_than_80_chars
|
||||
'CrudState(status: $status, data: $data, stream: ${stream != null ? 'listening' : 'null'}, errorMessage: $errorMessage)';
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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/>.
|
||||
|
||||
abstract class Model<O, T> {
|
||||
String? get id;
|
||||
Map<String, Object> toMap();
|
||||
T? from(O? object);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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/>.
|
||||
|
||||
export 'model.dart';
|
||||
export 'queries/queries.dart';
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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/>.
|
||||
|
||||
export 'queries_firestore.dart';
|
||||
export 'queries_interface.dart';
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart';
|
||||
|
||||
class QueryParserFirestore implements QueryParserInterface {
|
||||
@override
|
||||
Query parser(QueryInterface condition, Object query) {
|
||||
query as Query;
|
||||
if (condition is WhereQueryInterface) {
|
||||
switch (condition.type) {
|
||||
case WhereQueryType.isEqualTo:
|
||||
return query.where(condition.field, isEqualTo: condition.value);
|
||||
case WhereQueryType.isNotEqualTo:
|
||||
return query.where(condition.field, isNotEqualTo: condition.value);
|
||||
case WhereQueryType.isLessThan:
|
||||
return query.where(condition.field, isLessThan: condition.value);
|
||||
case WhereQueryType.isLessThanOrEqualTo:
|
||||
return query.where(
|
||||
condition.field,
|
||||
isLessThanOrEqualTo: condition.value,
|
||||
);
|
||||
case WhereQueryType.isGreaterThan:
|
||||
return query.where(condition.field, isGreaterThan: condition.value);
|
||||
case WhereQueryType.isGreaterThanOrEqualTo:
|
||||
return query.where(
|
||||
condition.field,
|
||||
isGreaterThanOrEqualTo: condition.value,
|
||||
);
|
||||
case WhereQueryType.arrayContains:
|
||||
return query.where(condition.field, arrayContains: condition.value);
|
||||
case WhereQueryType.arrayContainsAny:
|
||||
return query.where(
|
||||
condition.field,
|
||||
arrayContainsAny: condition.value as List<Object>,
|
||||
);
|
||||
case WhereQueryType.whereIn:
|
||||
return query.where(
|
||||
condition.field,
|
||||
whereIn: condition.value as List<Object>,
|
||||
);
|
||||
case WhereQueryType.whereNotIn:
|
||||
return query.where(
|
||||
condition.field,
|
||||
whereNotIn: condition.value as List<Object>,
|
||||
);
|
||||
case WhereQueryType.isNull:
|
||||
return query.where(condition.field, isNull: condition.value as bool);
|
||||
}
|
||||
} else if (condition is LimitQueryInterface) {
|
||||
return query.limit(condition.limit);
|
||||
} else if (condition is OrderByQueryInterface) {
|
||||
return query.orderBy(
|
||||
condition.field,
|
||||
descending: !condition.ascending,
|
||||
);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
class WhereQueryFirestore extends WhereQueryInterface {
|
||||
WhereQueryFirestore(WhereQueryType type, String field, Object value)
|
||||
: super(type, field, value);
|
||||
}
|
||||
|
||||
class LimitQueryFirestore extends LimitQueryInterface {
|
||||
LimitQueryFirestore(int limit) : super(limit);
|
||||
}
|
||||
|
||||
class OrderByQueryFirestore extends OrderByQueryInterface {
|
||||
OrderByQueryFirestore(String field, {bool ascending = true})
|
||||
: super(field, ascending: ascending);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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/>.
|
||||
|
||||
// ignore: one_member_abstracts
|
||||
abstract class QueryParserInterface {
|
||||
Object parser(QueryInterface condition, Object query);
|
||||
}
|
||||
|
||||
abstract class QueryInterface {}
|
||||
|
||||
enum WhereQueryType {
|
||||
isEqualTo,
|
||||
isNotEqualTo,
|
||||
isLessThan,
|
||||
isLessThanOrEqualTo,
|
||||
isGreaterThan,
|
||||
isGreaterThanOrEqualTo,
|
||||
arrayContains,
|
||||
arrayContainsAny,
|
||||
whereIn,
|
||||
whereNotIn,
|
||||
isNull,
|
||||
}
|
||||
|
||||
abstract class WhereQueryInterface extends QueryInterface {
|
||||
final WhereQueryType type;
|
||||
final String field;
|
||||
final Object value;
|
||||
|
||||
WhereQueryInterface(this.type, this.field, this.value);
|
||||
}
|
||||
|
||||
abstract class LimitQueryInterface extends QueryInterface {
|
||||
final int limit;
|
||||
|
||||
LimitQueryInterface(this.limit);
|
||||
}
|
||||
|
||||
abstract class OrderByQueryInterface extends QueryInterface {
|
||||
final String field;
|
||||
final bool ascending;
|
||||
|
||||
OrderByQueryInterface(this.field, {this.ascending = true});
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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:firebase_database/firebase_database.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart';
|
||||
import 'package:wyatt_crud_bloc/src/repositories/crud_repository_interface.dart';
|
||||
|
||||
class CrudRepositoryFirebaseDatabase<T> implements CrudRepositoryInterface<T> {
|
||||
final FirebaseDatabase _firebaseDatabase;
|
||||
final Model<Object, T> _parser;
|
||||
|
||||
late DatabaseReference _rootReference;
|
||||
|
||||
CrudRepositoryFirebaseDatabase(
|
||||
String root,
|
||||
Model<Object, T> parser, {
|
||||
FirebaseDatabase? firebaseDatabase,
|
||||
}) : _firebaseDatabase = firebaseDatabase ?? FirebaseDatabase.instance,
|
||||
_parser = parser {
|
||||
_rootReference = _firebaseDatabase.ref(root);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> create(Model object, {String? id}) {
|
||||
DatabaseReference _reference = _rootReference;
|
||||
if (id != null) {
|
||||
_reference = _reference.child(id);
|
||||
}
|
||||
return _reference.set(object.toMap());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) {
|
||||
final DatabaseReference _reference = _rootReference.child(id);
|
||||
return _reference.remove();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAll() {
|
||||
return _rootReference.remove();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T?> get(String id) async {
|
||||
final DatabaseEvent _event = await _rootReference.child(id).once();
|
||||
return _parser.from(_event.snapshot.value);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<T?>> getAll() async {
|
||||
final DatabaseEvent _event = await _rootReference.once();
|
||||
final List<T?> _objects = [];
|
||||
_event.snapshot.children.map((e) => _objects.add(_parser.from(e.value)));
|
||||
return _objects;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<T?>> query(List<QueryInterface> conditions) {
|
||||
// TODO(hpcl): implement query
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<T?>> stream({String? id, List<QueryInterface>? conditions}) {
|
||||
DatabaseReference _reference = _rootReference;
|
||||
if (id != null) {
|
||||
_reference = _reference.child(id);
|
||||
}
|
||||
|
||||
return _reference.onValue.map((e) {
|
||||
final List<T?> _objects = [];
|
||||
e.snapshot.children.map((e) => _objects.add(_parser.from(e.value)));
|
||||
return _objects;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(String id, {Model? object, Map<String, dynamic>? raw}) {
|
||||
// TODO(hpcl): implement update
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAll(Map<String, Object?> raw) {
|
||||
// TODO(hpcl): implement updateAll
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/queries/queries_firestore.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart';
|
||||
import 'package:wyatt_crud_bloc/src/repositories/crud_repository_interface.dart';
|
||||
|
||||
class CrudRepositoryFirestore<T> implements CrudRepositoryInterface<T> {
|
||||
final FirebaseFirestore _firestore;
|
||||
final Model<DocumentSnapshot, T> _parser;
|
||||
|
||||
late CollectionReference _collectionReference;
|
||||
|
||||
CrudRepositoryFirestore(
|
||||
String collection,
|
||||
Model<DocumentSnapshot, T> parser, {
|
||||
FirebaseFirestore? firestore,
|
||||
}) : _firestore = firestore ?? FirebaseFirestore.instance,
|
||||
_parser = parser {
|
||||
_collectionReference = _firestore.collection(collection);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> create(Model object, {String? id}) {
|
||||
if (id != null) {
|
||||
return _collectionReference.doc(id).set(object.toMap());
|
||||
} else {
|
||||
return _collectionReference.add(object.toMap());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) {
|
||||
return _collectionReference.doc(id).delete();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAll() async {
|
||||
final _batch = _firestore.batch();
|
||||
final QuerySnapshot snapshots = await _collectionReference.get();
|
||||
for (final DocumentSnapshot snapshot in snapshots.docs) {
|
||||
_batch.delete(snapshot.reference);
|
||||
}
|
||||
return _batch.commit();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T?> get(String id) async {
|
||||
final DocumentSnapshot snapshot = await _collectionReference.doc(id).get();
|
||||
return _parser.from(snapshot);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<T?>> getAll() async {
|
||||
final QuerySnapshot snapshots = await _collectionReference.get();
|
||||
return snapshots.docs.map(_parser.from).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<T?>> query(List<QueryInterface> conditions) async {
|
||||
Query query = _collectionReference;
|
||||
for (final condition in conditions) {
|
||||
query = QueryParserFirestore().parser(condition, query);
|
||||
}
|
||||
final QuerySnapshot snapshots = await query.get();
|
||||
return snapshots.docs.map(_parser.from).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<T?>> stream({
|
||||
String? id,
|
||||
List<QueryInterface>? conditions,
|
||||
bool includeMetadataChanges = false,
|
||||
}) {
|
||||
if (id != null) {
|
||||
return _collectionReference
|
||||
.doc(id)
|
||||
.snapshots(
|
||||
includeMetadataChanges: includeMetadataChanges,
|
||||
)
|
||||
.map<List<T?>>(
|
||||
(DocumentSnapshot snapshot) => [_parser.from(snapshot)],
|
||||
);
|
||||
} else {
|
||||
if (conditions != null) {
|
||||
Query query = _collectionReference;
|
||||
for (final condition in conditions) {
|
||||
query = QueryParserFirestore().parser(condition, query);
|
||||
}
|
||||
return query
|
||||
.snapshots(
|
||||
includeMetadataChanges: includeMetadataChanges,
|
||||
)
|
||||
.map((querySnapshot) {
|
||||
return querySnapshot.docs.map(_parser.from).toList();
|
||||
});
|
||||
} else {
|
||||
return _collectionReference
|
||||
.snapshots(
|
||||
includeMetadataChanges: includeMetadataChanges,
|
||||
)
|
||||
.map((querySnapshot) {
|
||||
return querySnapshot.docs.map(_parser.from).toList();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(String id, {Model? object, Map<String, Object?>? raw}) {
|
||||
if (object != null) {
|
||||
return _collectionReference.doc(id).update(object.toMap());
|
||||
} else {
|
||||
if (raw != null) {
|
||||
return _collectionReference.doc(id).update(raw);
|
||||
} else {
|
||||
throw Exception('You must provide an object or a raw map');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAll(Map<String, Object?> raw) async {
|
||||
final _batch = _firestore.batch();
|
||||
final QuerySnapshot snapshots = await _collectionReference.get();
|
||||
for (final DocumentSnapshot snapshot in snapshots.docs) {
|
||||
_batch.update(snapshot.reference, raw);
|
||||
}
|
||||
return _batch.commit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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:wyatt_crud_bloc/src/models/model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart';
|
||||
|
||||
abstract class CrudRepositoryInterface<T> {
|
||||
Future<void> create(Model object, {String? id});
|
||||
Future<void> delete(String id);
|
||||
Future<void> deleteAll();
|
||||
Future<T?> get(String id);
|
||||
Future<List<T?>> getAll();
|
||||
Future<List<T?>> query(List<QueryInterface> conditions);
|
||||
Stream<List<T?>> stream({String? id, List<QueryInterface>? conditions});
|
||||
Future<void> update(String id, {Model? object, Map<String, dynamic>? raw});
|
||||
Future<void> updateAll(Map<String, Object?> raw);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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/>.
|
||||
|
||||
export 'crud_repository_firestore.dart';
|
||||
export 'crud_repository_interface.dart';
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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/>.
|
||||
|
||||
library wyatt_crud_bloc;
|
||||
|
||||
export 'src/crud/crud.dart';
|
||||
export 'src/models/models.dart';
|
||||
export 'src/repositories/repositories.dart';
|
||||
@@ -0,0 +1,27 @@
|
||||
name: wyatt_crud_bloc
|
||||
description: Create/Read/Update/Delete BLoC for Flutter
|
||||
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_crud_bloc
|
||||
version: 0.0.2
|
||||
|
||||
environment:
|
||||
sdk: '>=2.16.2 <3.0.0'
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
flutter_bloc: ^8.0.1
|
||||
equatable: ^2.0.3
|
||||
cloud_firestore: ^3.1.12
|
||||
firebase_database: ^9.0.11
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
bloc_test: ^9.0.3
|
||||
wyatt_analysis:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_analysis-v2.0.1
|
||||
path: packages/wyatt_analysis
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.0.2
|
||||
|
||||
- **FEAT**: add list option validator.
|
||||
|
||||
## 0.0.1
|
||||
|
||||
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
|
||||
|
||||
@@ -14,50 +14,48 @@
|
||||
// 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:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:form_bloc_example/constants.dart';
|
||||
import 'package:form_bloc_example/cubit/custom_form_cubit.dart';
|
||||
import 'package:form_bloc_example/sign_up/sign_up_page.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([
|
||||
static List<FormEntry> getNormalEntries() {
|
||||
return const [
|
||||
FormEntry(formFieldName, Name.pure()),
|
||||
FormEntry(formFieldEmail, Email.pure()),
|
||||
FormEntry(formFieldPhone, Phone.pure()),
|
||||
FormEntry(
|
||||
formFieldList, ListOption<String>.pure(defaultValue: ['checkbox3'])),
|
||||
FormEntry(formFieldRadio, TextString.pure()),
|
||||
FormEntry(formFieldPro, Boolean.pure(), name: 'business'),
|
||||
FormEntry(formFieldHidden, Boolean.pure(), export: false),
|
||||
]);
|
||||
];
|
||||
}
|
||||
|
||||
static List<FormEntry> getBusinessEntries() {
|
||||
const entries = [
|
||||
FormEntry(formFieldSiren, Siren.pure()),
|
||||
FormEntry(formFieldIban, Iban.pure()),
|
||||
];
|
||||
return getNormalEntries() + entries;
|
||||
}
|
||||
|
||||
static FormData getNormalFormData() {
|
||||
return FormData(getNormalEntries());
|
||||
}
|
||||
|
||||
static FormData getProFormData() {
|
||||
return const FormData([
|
||||
FormEntry(formFieldName, Name.pure()),
|
||||
FormEntry(formFieldEmail, Email.pure()),
|
||||
FormEntry(formFieldPhone, Phone.pure()),
|
||||
FormEntry(formFieldPro, Boolean.pure(), name: 'business'),
|
||||
FormEntry(formFieldHidden, Boolean.pure(), export: false),
|
||||
FormEntry(formFieldSiren, Siren.pure()),
|
||||
FormEntry(formFieldIban, Iban.pure()),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<bool> onSubmit(FormDataState state) async {
|
||||
log(state.data.toMap().toString());
|
||||
return true;
|
||||
return FormData(getBusinessEntries());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
FormDataCubit _formCubit = FormDataCubit(
|
||||
entries: getNormalFormData(),
|
||||
onSubmit: onSubmit,
|
||||
);
|
||||
FormDataCubit _formCubit = CustomFormCubit(entries: getNormalFormData());
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => _formCubit,
|
||||
|
||||
@@ -19,6 +19,8 @@ const String formFieldPhone = 'phone';
|
||||
const String formFieldEmail = 'email';
|
||||
const String formFieldSiren = 'siren';
|
||||
const String formFieldIban = 'iban';
|
||||
const String formFieldList = 'list';
|
||||
const String formFieldRadio = 'radio';
|
||||
const String formFieldHidden = 'hidden';
|
||||
|
||||
const String formFieldPro = 'isPro';
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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:meta/meta.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
part 'custom_form_state.dart';
|
||||
|
||||
class CustomFormCubit extends FormDataCubit {
|
||||
CustomFormCubit({required FormData entries}) : super(entries: entries);
|
||||
|
||||
@override
|
||||
Future<void> submitForm() {
|
||||
log(state.data.toMap().toString());
|
||||
|
||||
return Future.value();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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/>.
|
||||
|
||||
part of 'custom_form_cubit.dart';
|
||||
|
||||
@immutable
|
||||
abstract class CustomFormState {}
|
||||
|
||||
class CustomFormInitial extends CustomFormState {}
|
||||
@@ -135,6 +135,112 @@ class _IbanInput extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _CheckListInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
final _input =
|
||||
state.data.input<List<String>>(formFieldList) as ListOption<String>;
|
||||
final _options = _input.value;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Checkbox1'),
|
||||
trailing: Checkbox(
|
||||
value: _options.contains('checkbox1'),
|
||||
onChanged: (_) {
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldList,
|
||||
_input.select('checkbox1'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Checkbox2'),
|
||||
trailing: Checkbox(
|
||||
value: _options.contains('checkbox2'),
|
||||
onChanged: (_) {
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldList,
|
||||
_input.select('checkbox2'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Checkbox3 (default)'),
|
||||
trailing: Checkbox(
|
||||
value: _options.contains('checkbox3'),
|
||||
onChanged: (_) {
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldList,
|
||||
_input.select('checkbox3'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RadioListInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
final _input =
|
||||
state.data.input<String>(formFieldRadio) as TextString;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Radio1'),
|
||||
trailing: Radio<bool>(
|
||||
groupValue: true,
|
||||
value: _input.value == 'radio1',
|
||||
onChanged: (_) {
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldRadio,
|
||||
const TextString.dirty('radio1'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Radio2'),
|
||||
trailing: Radio<bool>(
|
||||
groupValue: true,
|
||||
value: _input.value == 'radio2',
|
||||
onChanged: (_) {
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldRadio,
|
||||
const TextString.dirty('radio2'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Radio3'),
|
||||
trailing: Radio<bool>(
|
||||
groupValue: true,
|
||||
value: _input.value == 'radio3',
|
||||
onChanged: (_) {
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldRadio,
|
||||
const TextString.dirty('radio3'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CheckHiddenInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -201,8 +307,7 @@ class _SignUpButton extends StatelessWidget {
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: state.status.isValidated
|
||||
? () =>
|
||||
context.read<FormDataCubit>().submitForm()
|
||||
? () => context.read<FormDataCubit>().submitForm()
|
||||
: null,
|
||||
child: const Text('SIGN UP'),
|
||||
);
|
||||
@@ -244,8 +349,6 @@ class SignUpForm extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: Column(
|
||||
@@ -257,6 +360,10 @@ class SignUpForm extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
_PhoneInput(),
|
||||
const SizedBox(height: 8),
|
||||
_CheckListInput(),
|
||||
const SizedBox(height: 8),
|
||||
_RadioListInput(),
|
||||
const SizedBox(height: 8),
|
||||
_CheckHiddenInput(),
|
||||
const SizedBox(height: 8),
|
||||
_CheckIsProInput(),
|
||||
@@ -281,7 +388,6 @@ class SignUpForm extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,16 @@ import 'package:wyatt_form_bloc/src/form/form.dart';
|
||||
|
||||
part 'form_data_state.dart';
|
||||
|
||||
class FormDataCubit extends Cubit<FormDataState> {
|
||||
final Future<bool> Function(FormDataState state)? _onSubmit;
|
||||
abstract class FormDataCubit extends Cubit<FormDataState> {
|
||||
FormDataCubit({required FormData entries})
|
||||
: super(FormDataState(data: entries));
|
||||
|
||||
FormDataCubit({
|
||||
required FormData entries,
|
||||
Future<bool> Function(FormDataState state)? onSubmit,
|
||||
}) : _onSubmit = onSubmit,
|
||||
super(FormDataState(data: entries));
|
||||
|
||||
void dataChanged<T>(String field, FormInput dirtyValue) {
|
||||
/// Change value of a field.
|
||||
///
|
||||
/// Inputs:
|
||||
/// - `field`: The key of the field to change.
|
||||
/// - `dirtyValue`: The new value of the field.
|
||||
void dataChanged(String field, FormInput dirtyValue) {
|
||||
final _form = state.data.clone();
|
||||
|
||||
if (_form.contains(field)) {
|
||||
@@ -49,6 +49,11 @@ class FormDataCubit extends Cubit<FormDataState> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Update entries list.
|
||||
///
|
||||
/// Inputs:
|
||||
/// - `data`: The new entries list.
|
||||
/// - `operation`: The operation to perform on the entries set.
|
||||
void updateFormData(
|
||||
FormData data, {
|
||||
SetOperation operation = SetOperation.replace,
|
||||
@@ -78,13 +83,6 @@ class FormDataCubit extends Cubit<FormDataState> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submitForm() async {
|
||||
unawaited(
|
||||
_onSubmit?.call(state).then((bool reemit) {
|
||||
if (reemit) {
|
||||
emit(state);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
/// Submit the form.
|
||||
Future<void> submitForm();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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:wyatt_form_bloc/src/enums/enums.dart';
|
||||
import 'package:wyatt_form_bloc/src/form/form.dart';
|
||||
|
||||
/// {@template list_option}
|
||||
/// Form input for a list input
|
||||
/// {@endtemplate}
|
||||
class ListOption<T> extends FormInput<List<T>, ValidationError> {
|
||||
/// {@macro list_option}
|
||||
const ListOption.pure({List<T>? defaultValue})
|
||||
: super.pure(defaultValue ?? const []);
|
||||
|
||||
/// {@macro list_option}
|
||||
const ListOption.dirty({List<T>? value}) : super.dirty(value ?? const []);
|
||||
|
||||
ListOption select(T? v) {
|
||||
if (v == null) {
|
||||
return this;
|
||||
}
|
||||
if (value.contains(v)) {
|
||||
final List<T> newValue = List.from(value)..remove(v);
|
||||
return ListOption<T>.dirty(value: newValue);
|
||||
} else {
|
||||
final List<T> newValue = List.from(value)..add(v);
|
||||
return ListOption<T>.dirty(value: newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ValidationError? validator(List<T>? value) {
|
||||
return value?.isNotEmpty == true ? null : ValidationError.invalid;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export 'boolean.dart';
|
||||
export 'confirmed_password.dart';
|
||||
export 'email.dart';
|
||||
export 'iban.dart';
|
||||
export 'list_option.dart';
|
||||
export 'name.dart';
|
||||
export 'password.dart';
|
||||
export 'phone.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: wyatt_form_bloc
|
||||
description: Manage forms in Dart & Flutter with Bloc
|
||||
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_form_bloc
|
||||
version: 0.0.1
|
||||
version: 0.0.2
|
||||
|
||||
environment:
|
||||
sdk: '>=2.16.2 <3.0.0'
|
||||
@@ -16,5 +16,5 @@ dev_dependencies:
|
||||
wyatt_analysis:
|
||||
git:
|
||||
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||
ref: wyatt_analysis-v2.0.0
|
||||
ref: wyatt_analysis-v2.0.1
|
||||
path: packages/wyatt_analysis
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Files and directories created by pub.
|
||||
.dart_tool/
|
||||
.packages
|
||||
|
||||
# Conventional directory for build outputs.
|
||||
build/
|
||||
|
||||
# Omit committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
{
|
||||
"recommendations": [
|
||||
"psioniq.psi-header",
|
||||
"blaugold.melos-code"
|
||||
]
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.0.0
|
||||
|
||||
- Initial version.
|
||||
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
@@ -0,0 +1 @@
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||
@@ -0,0 +1,46 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
@@ -0,0 +1,10 @@
|
||||
# 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.
|
||||
|
||||
version:
|
||||
revision: 5464c5bac742001448fe4fc0597be939379f88ea
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
@@ -0,0 +1,16 @@
|
||||
# medium_feeds_example
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
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)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@@ -0,0 +1,29 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ArticleTile extends StatelessWidget {
|
||||
const ArticleTile({
|
||||
Key? key,
|
||||
required this.bannerUrl,
|
||||
required this.title,
|
||||
required this.summary,
|
||||
required this.author,
|
||||
required this.publishDate,
|
||||
required this.readingTime,
|
||||
}) : super(key: key);
|
||||
|
||||
final String bannerUrl;
|
||||
final String title;
|
||||
final String summary;
|
||||
final String author;
|
||||
final String publishDate;
|
||||
final String readingTime;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(bannerUrl),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
summary,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
author,
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
publishDate,
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
readingTime,
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:medium_feeds_example/article_tile.dart';
|
||||
import 'package:wyatt_medium_feeds/wyatt_medium_feeds.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: const WidgetTree(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArticleGrid extends StatelessWidget {
|
||||
const ArticleGrid({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mediumFeed = MediumFeed.fromPublicationName('flutter');
|
||||
return FutureBuilder(
|
||||
future: mediumFeed.parse(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final articles = mediumFeed.articles;
|
||||
return GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1,
|
||||
),
|
||||
itemCount: articles.length,
|
||||
itemBuilder: (context, index) {
|
||||
final article = articles[index];
|
||||
return ArticleTile(
|
||||
bannerUrl: article.images.isNotEmpty ? article.images[0] : '',
|
||||
author: article.author,
|
||||
title: article.title,
|
||||
summary: article.summary,
|
||||
readingTime:
|
||||
article.readingTime.inMinutes.toString() + ' min read',
|
||||
publishDate: article.publishDate!.day.toString() +
|
||||
'/' +
|
||||
article.publishDate!.month.toString() +
|
||||
'/' +
|
||||
article.publishDate!.year.toString(),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetTree extends StatelessWidget {
|
||||
const WidgetTree({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Medium Feeds Example'),
|
||||
),
|
||||
body: const ArticleGrid(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
name: medium_feeds_example
|
||||
description: A new Flutter project.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# 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.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.16.2 <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
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
wyatt_medium_feeds:
|
||||
path: ../
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^1.0.0
|
||||
|
||||
# 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.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - 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.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
@@ -0,0 +1,30 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:medium_feeds_example/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const MyApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user