wyatt_crud_bloc (0.2.0)

Published 2023-11-14 13:51:08 +00:00 by hugo in Wyatt-FOSS/wyatt-packages

Installation

dart pub add wyatt_crud_bloc:0.2.0 --hosted-url=

About this package

Create/Read/Update/Delete BLoC for Flutter

CRUD BloC

Style: Wyatt Analysis SDK: Flutter

CRUD Bloc Pattern utilities for Flutter.

This package defines a set of classes that can be used to implement the CRUD Bloc Pattern.

  • Data Source
  • Repository
  • Use Case
    • Create
    • Get
    • Get All
    • Update
    • Update All
    • Delete
    • Delete All
    • Search
  • Bloc
    • Standard (C R U D), you have to choose the responsiblity of the bloc for each use case. For example, you can have a cubit that only handles the creation of an entity, and another cubit that only handles the deletion of an entity. Each cubit can only have one operation responsibility of each type, for example you can't have a cubit that handles get and get all.
    • Advanced, you can set every use case to be handled by the bloc. This is useful if you want to have a single bloc that handles all the operations of an entity.

Usage

Create your entity using Wyatt Architecture and Equatable

import 'package:equatable/equatable.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';

class User extends Entity with EquatableMixin {
  final String? id;

  final String name;
  final String email;
  final String phone;

  const User({
    required this.name,
    required this.email,
    required this.phone,
    this.id,
  });

  @override
  List<Object?> get props => [id, name, email, phone];
}

Then, you can create your model for this entity with Freezed, JsonSerializable or any other library.

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)';
}

This exemple uses JsonSerializable to generate the model, but you can use any other library. Or you can even create your own model without any library.

Then you have to create a CRUD Cubit. Each Crud cubit can only handle one operation of each type. For example, you can't have a cubit that handles get and get all. (See the CrudAdvancedCubit for this use case).

You also have to create a modelIdentifier that will be used to identify your model. This is used to identify the model in the state of the cubit. For example, if you have a list of users, you can use the id of the user to identify it in the state. It's up to you to choose how you want to identify your model.

import 'package:wyatt_crud_bloc/wyatt_crud_bloc.dart';

/// A [CrudCubit] for [User].
class UserCubit extends CrudCubit<User> {
  final CrudRepository<User> crudRepository;

  UserCubit(this.crudRepository);

  @override
  DefaultCreate<User>? get createOperation => Create(crudRepository);

  @override
  DefaultDelete? get deleteOperation => Delete(crudRepository);

  @override
  DefaultRead? get readOperation => GetAll(crudRepository);

  @override
  DefaultUpdate? get updateOperation => Update(crudRepository);

  @override
  ModelIdentifier<User> get modelIdentifier => ModelIdentifier(
        getIdentifier: (user) => user.id ?? '',
      );
}

In this example, the modelIdentifier is the id of the user. But you can use any other property of the user to identify it. But make sure that the property you use is unique.

In this example, the CrudCubit handles Create, Delete, GetAll and Update operations. But you can choose which operation you want to handle. For example, you can have a cubit that only handles the creation of an entity, and another cubit that only handles the deletion of an entity.

Then you can configure the data source and the repository.

In Memory

The base implementation of the data source is the CrudInMemoryDataSourceImpl. This data source stores the data in memory. This is useful for testing.

final CrudDataSource crudDataSource = CrudDataSourceInMemoryImpl(
  id: (operation, json) => json['id'] as String?,
);

The id parameter is used to get the id of the raw data. You can use the operation parameter to get the operation that is being performed. This is useful if you want to have different ids for each operation. For example, you can have a different id for the creation and the update of an entity.

Firestore

You can use the CrudFirestoreDataSourceImpl to store your data in Firestore.

Make sure to add the wyatt_crud_bloc_firestore package to your dependencies.

final CrudDataSource crudDataSource = CrudDataSourceFirestoreImpl(
  'users',
  id: (_, json) => json['id'] as String,
  fromFirestore: (document, snapshot) => document.data() ?? {},
  toFirestore: (object, options) => object,
);

The id parameter is used to get the id of the raw data. You can use the operation parameter to get the operation that is being performed. This is useful if you want to have different ids for each operation. For example, you can have a different id for the creation and the update of an entity.

The fromFirestore and toFirestore parameters are used to convert the data from and to Firestore.

Finally, you can use the CrudRepositoryImpl to create your repository.

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(),
  ),
);

The modelMapper parameter is used to convert the data from and to the model. In the toJson make sure to use the same data structure as the one used in the data source. For example the previous id() function is used to get the id of the raw data, if id is missing from the raw data, the id() function will not works.

And in your widget tree you can use the CrudBuilder to build your UI.

...
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"),
      ),
    );
  },
),
...

This piece of code is used to build a list of users. When you tap on a user, it will delete it. When you long press on a user, it will update it. Also, the loading state is not always displayed, it will only be displayed when the state is CrudReading, so when the state is CrudUpdating or CrudDeleting, the loading state will not be displayed.

Details
Pub
2023-11-14 13:51:08 +00:00
403
38 KiB
Assets (1)
0.2.0.tar.gz 38 KiB
Versions (3) View all
0.2.0 2023-11-14
0.1.1+1 2023-08-28
0.1.1 2023-04-13