feat(crud)!: move crud firestore implementation into his own package

This commit is contained in:
2023-11-13 18:03:10 +01:00
parent af841baefb
commit 74593fa9fc
107 changed files with 1392 additions and 2410 deletions
@@ -0,0 +1,151 @@
// Copyright (C) 2023 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/user_advanced_cubit.dart';
import 'package:crud_bloc_example/user_entity.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
class AdvancedCubitView extends StatelessWidget {
const AdvancedCubitView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Advanced Cubit"),
),
body: BlocProvider(
create: (context) =>
UserAdvancedCubit(context.read<CrudRepository<User>>())..getAll(),
child: Builder(builder: (context) {
return Column(
children: [
const Text("Data:"),
BlocBuilder<UserAdvancedCubit, CrudState>(
buildWhen: (previous, current) {
if (current is CrudLoading && current is! CrudReading) {
return false;
}
return true;
},
builder: (context, state) {
return Expanded(
child: CrudBuilder.typed<CrudListLoaded<User?>>(
state: state,
builder: ((context, state) {
return ListView.builder(
itemCount: state.data.length,
itemBuilder: (context, index) {
final user = state.data.elementAt(index);
return ListTile(
title: Text(user?.name ?? 'Error'),
subtitle: Text(user?.email ?? 'Error'),
onTap: () {
context
.read<UserAdvancedCubit>()
.delete((user?.id)!);
},
onLongPress: () {
context.read<UserAdvancedCubit>().update(
UpdateParameters(
id: user?.id ?? '',
raw: {
'email': '${user?.id}@updated.io',
}),
);
},
);
},
);
}),
initialBuilder: (context, state) =>
const Center(child: CircularProgressIndicator()),
loadingBuilder: (context, state) =>
const Center(child: CircularProgressIndicator()),
errorBuilder: (context, state) => Text("Error: $state"),
),
);
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final r = Random().nextInt(1000);
context.read<UserAdvancedCubit>().create(
User(
id: '$r',
name: 'Wyatt $r',
email: '$r@wyattapp.io',
phone: '06$r',
),
);
},
child: BlocBuilder<UserAdvancedCubit, CrudState>(
buildWhen: (previous, current) {
if (current is CrudLoading && current is! CrudCreating) {
return false;
}
return true;
},
builder: (context, state) {
return state is CrudCreating
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: const Text("Create");
},
),
),
ElevatedButton(
onPressed: () {
context.read<UserAdvancedCubit>().getAll();
},
child: BlocBuilder<UserAdvancedCubit, CrudState>(
buildWhen: (previous, current) {
if (current is CrudLoading && current is! CrudReading) {
return false;
}
return true;
},
builder: (context, state) {
return state is CrudReading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: const Text("GetAll");
},
),
),
const SizedBox(height: 20),
],
);
}),
),
);
}
}
+50 -76
View File
@@ -14,41 +14,43 @@
// 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:crud_bloc_example/user_cubit.dart';
import 'package:crud_bloc_example/advanced_cubit_view.dart';
import 'package:crud_bloc_example/basic_cubit_view.dart';
import 'package:crud_bloc_example/user_entity.dart';
import 'package:crud_bloc_example/user_model.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);
const MyApp({required this.crudDataSource, Key? key}) : super(key: key);
final CrudDataSource crudDataSource;
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
final CrudDataSource<User> userLocalDataSource =
CrudInMemoryDataSourceImpl<User>(toMap: (user) => user.toMap());
final CrudRepository<User> userRepository =
CrudRepositoryImpl(crudDataSource: userLocalDataSource);
final CrudRepository<User> userRepository = CrudRepositoryImpl(
crudDataSource: crudDataSource,
modelMapper: ModelMapper(
fromJson: (json) => UserModel.fromJson(json ?? {}),
toJson: (user) => UserModel(
id: user.id,
name: user.name,
email: user.email,
phone: user.phone,
).toJson(),
),
);
return RepositoryProvider<CrudRepository<User>>.value(
value: userRepository,
child: MultiBlocProvider(
providers: [
BlocProvider<UserCubit>(
create: (context) => UserCubit(userRepository)..read(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
),
);
}
@@ -63,60 +65,32 @@ class MyHomePage extends StatelessWidget {
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: 20),
const Text("Data:"),
BlocBuilder<UserCubit, CrudState>(
builder: (context, state) {
return CrudBuilder.typed<CrudListLoaded<User?>>(
state: state,
builder: ((context, state) {
return ListView.builder(
shrinkWrap: true,
itemCount: state.data.length,
itemBuilder: (context, index) {
final user = state.data.elementAt(index);
return ListTile(
title: Text(user?.name ?? 'Error'),
subtitle: Text(user?.id ?? 'Error'),
onTap: () {
context.read<UserCubit>().delete(id: (user?.id)!);
},
);
},
);
}),
initialBuilder: (context, state) => const Text("Loading..."),
loadingBuilder: (context, state) => const Text("Loading..."),
errorBuilder: (context, state) => Text("Error: $state"),
);
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final r = Random().nextInt(1000);
context.read<UserCubit>().create(
User(
id: '$r',
name: 'Wyatt $r',
email: '$r@wyattapp.io',
phone: '06$r'),
);
},
child: const Text("Create"),
),
ElevatedButton(
onPressed: () {
context.read<UserCubit>().read();
},
child: const Text("GetAll"),
),
const SizedBox(height: 20),
],
),
body: Column(
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const BasicCubitView(),
),
);
},
child: const Text('Basic example'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AdvancedCubitView(),
),
);
},
child: const Text('Advanced example'),
),
],
),
);
}
@@ -0,0 +1,150 @@
// Copyright (C) 2023 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/user_cubit.dart';
import 'package:crud_bloc_example/user_entity.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
class BasicCubitView extends StatelessWidget {
const BasicCubitView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Basic Cubit"),
),
body: BlocProvider(
create: (context) =>
UserCubit(context.read<CrudRepository<User>>())..read(),
child: Builder(builder: (context) {
return Column(
children: [
BlocBuilder<UserCubit, CrudState>(
buildWhen: (previous, current) {
if (current is CrudLoading && current is! CrudReading) {
return false;
}
return true;
},
builder: (context, state) {
return Expanded(
child: CrudBuilder.typed<CrudListLoaded<User?>>(
state: state,
builder: ((context, state) {
return ListView.builder(
itemCount: state.data.length,
itemBuilder: (context, index) {
final user = state.data.elementAt(index);
return ListTile(
title: Text(user?.name ?? 'Error'),
subtitle: Text(user?.email ?? 'Error'),
onTap: () {
context
.read<UserCubit>()
.delete(id: (user?.id)!);
},
onLongPress: () {
context.read<UserCubit>().update(
single: UpdateParameters(
id: user?.id ?? '',
raw: {
'email': '${user?.id}@updated.io',
}),
);
},
);
},
);
}),
initialBuilder: (context, state) =>
const Center(child: CircularProgressIndicator()),
loadingBuilder: (context, state) =>
const Center(child: CircularProgressIndicator()),
errorBuilder: (context, state) => Text("Error: $state"),
),
);
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final r = Random().nextInt(1000);
context.read<UserCubit>().create(
User(
id: '$r',
name: 'Wyatt $r',
email: '$r@wyattapp.io',
phone: '06$r',
),
);
},
child: BlocBuilder<UserCubit, CrudState>(
buildWhen: (previous, current) {
if (current is CrudLoading && current is! CrudCreating) {
return false;
}
return true;
},
builder: (context, state) {
return state is CrudCreating
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: const Text("Create");
},
),
),
ElevatedButton(
onPressed: () {
context.read<UserCubit>().read();
},
child: BlocBuilder<UserCubit, CrudState>(
buildWhen: (previous, current) {
if (current is CrudLoading && current is! CrudReading) {
return false;
}
return true;
},
builder: (context, state) {
return state is CrudReading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: const Text("Read");
},
),
),
const SizedBox(height: 20),
],
);
}),
),
);
}
}
@@ -0,0 +1,63 @@
// 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.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyAYS14uXupkS158Q5QAFP1864UrUN_yDSk',
appId: '1:136771801992:android:8482c9b90bc29de697203d',
messagingSenderId: '136771801992',
projectId: 'tchat-beta',
databaseURL: 'https://tchat-beta.firebaseio.com',
storageBucket: 'tchat-beta.appspot.com',
);
}
@@ -0,0 +1,44 @@
// 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/app_bloc_observer.dart';
import 'package:crud_bloc_example/firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
import 'package:wyatt_crud_bloc_firestore/wyatt_crud_firestore.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = AppBlocObserver();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
final CrudDataSource crudDataSource = CrudDataSourceFirestoreImpl(
'users',
id: (_, json) => json['id'] as String,
fromFirestore: (document, snapshot) => document.data() ?? {},
toFirestore: (object, options) => object,
);
runApp(MyApp(
crudDataSource: crudDataSource,
));
}
@@ -18,10 +18,17 @@ import 'package:crud_bloc_example/app.dart';
import 'package:crud_bloc_example/app_bloc_observer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = AppBlocObserver();
runApp(const MyApp());
final CrudDataSource crudDataSource = CrudDataSourceInMemoryImpl(
id: (_, json) => json['id'] as String?,
);
runApp(MyApp(
crudDataSource: crudDataSource,
));
}
@@ -0,0 +1,54 @@
// 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/user_entity.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
/// A [CrudAdvancedCubit] for [User].
class UserAdvancedCubit extends CrudAdvancedCubit<User> {
final CrudRepository<User> crudRepository;
UserAdvancedCubit(this.crudRepository);
@override
ModelIdentifier<User> get modelIdentifier => ModelIdentifier(
getIdentifier: (user) => user.id ?? '',
);
@override
Create<User>? get crudCreate => Create(crudRepository);
@override
Delete<User>? get crudDelete => Delete(crudRepository);
@override
DeleteAll<User>? get crudDeleteAll => DeleteAll(crudRepository);
@override
Get<User>? get crudGet => Get(crudRepository);
@override
GetAll<User>? get crudGetAll => GetAll(crudRepository);
@override
Search<User>? get crudSearch => Search(crudRepository);
@override
Update<User>? get crudUpdate => Update(crudRepository);
@override
UpdateAll<User>? get crudUpdateAll => UpdateAll(crudRepository);
}
@@ -14,28 +14,29 @@
// 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/models.dart';
import 'package:crud_bloc_example/user_entity.dart';
import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';
/// A [CrudCubit] for [User].
class UserCubit extends CrudCubit<User> {
final CrudRepository<User> _crudRepository;
final CrudRepository<User> crudRepository;
UserCubit(this._crudRepository);
UserCubit(this.crudRepository);
@override
CreateOperation<User, dynamic>? get createOperation =>
Create(_crudRepository);
DefaultCreate<User>? get createOperation => Create(crudRepository);
@override
DeleteOperation<User, dynamic>? get deleteOperation =>
Delete(_crudRepository);
DefaultDelete? get deleteOperation => Delete(crudRepository);
@override
ReadOperation<User, dynamic, dynamic>? get readOperation =>
GetAll(_crudRepository);
DefaultRead? get readOperation => GetAll(crudRepository);
@override
UpdateOperation<User, dynamic>? get updateOperation =>
Update(_crudRepository);
DefaultUpdate? get updateOperation => Update(crudRepository);
@override
ModelIdentifier<User> get modelIdentifier => ModelIdentifier(
getIdentifier: (user) => user.id ?? '',
);
}
@@ -1,4 +1,4 @@
// Copyright (C) 2022 WYATT GROUP
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
@@ -14,15 +14,15 @@
// 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/wyatt_crud_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class User extends ObjectModel {
@override
class User extends Entity with EquatableMixin {
final String? id;
final String? name;
final String? email;
final String? phone;
final String name;
final String email;
final String phone;
const User({
required this.name,
@@ -31,15 +31,6 @@ class User extends ObjectModel {
this.id,
});
Map<String, Object> toMap() {
return {
'name': name ?? '',
'email': email ?? '',
'phone': phone ?? '',
};
}
@override
String toString() =>
'User(id: $id, name: $name, email: $email, phone: $phone)';
List<Object?> get props => [id, name, email, phone];
}
@@ -0,0 +1,39 @@
// Copyright (C) 2023 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/user_entity.dart';
import 'package:json_annotation/json_annotation.dart';
part 'user_model.g.dart';
@JsonSerializable()
class UserModel extends User {
UserModel({
super.id,
required super.name,
required super.email,
required super.phone,
});
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
@override
String toString() =>
'UserModel(id: $id, name: $name, email: $email, phone: $phone)';
}
@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
UserModel _$UserModelFromJson(Map<String, dynamic> json) => UserModel(
id: json['id'] as String?,
name: json['name'] as String,
email: json['email'] as String,
phone: json['phone'] as String,
);
Map<String, dynamic> _$UserModelToJson(UserModel instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
'email': instance.email,
'phone': instance.phone,
};