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.

  • Model
  • Data Source
    • In Memory
    • Firestore
  • Repository
  • Use Case
    • Create
    • Get
    • Get All
    • Update
    • Update All
    • Delete
    • Delete All
    • Query
  • 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 a model class that extends the ObjectModel class.

class User extends ObjectModel {
  @override
  final String? id;

  final String? name;
  
  const User({
    required this.name,
    this.id,
  });

  Map<String, Object> toMap() {
    return {
      'name': name ?? '',
    };
  }

  @override
  String toString() => 'User(id: $id, name: $name)';
}

You have to implement a bloc.

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

  UserCubit(this._crudRepository);

  @override
  CreateOperation<User, dynamic>? get createOperation =>
      Create(_crudRepository);

  @override
  DeleteOperation<User, dynamic>? get deleteOperation =>
      Delete(_crudRepository);

  @override
  ReadOperation<User, dynamic, dynamic>? get readOperation =>
      GetAll(_crudRepository);

  @override
  UpdateOperation<User, dynamic>? get updateOperation =>
      Update(_crudRepository);
}

You can also use the CrudAdvancedCubit class to implement a bloc that handles all the use cases.

Then you can use the bloc in your widget with a data source and a repository.

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 CrudDataSource<User> userLocalDataSource =
        CrudInMemoryDataSourceImpl<User>(toMap: (user) => user.toMap());

    final CrudRepository<User> userRepository =
        CrudRepositoryImpl(crudDataSource: userLocalDataSource);

    return RepositoryProvider<CrudRepository<User>>.value(
      value: userRepository,
      child: BlocProvider<UserCubit>(
        create: (context) => UserCubit(userRepository)..read(),
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(),
        ),
      ),
    );
  }
}

And anywhere in your widget tree you can use the BlocBuilder to build your widget.

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