digit_crud_bloc 0.0.1 copy "digit_crud_bloc: ^0.0.1" to clipboard
digit_crud_bloc: ^0.0.1 copied to clipboard

This package provides the core logic and services for managing entity-based data flows in a modular and extensible way. It supports dynamic entity mapping, relationship traversal, nested field resolut [...]

DIGIT CRUD BLoC #

A core package that handles all Create, Read, Update, and Delete (CRUD) operations for entities in the HCM application. It acts as the data engine behind feature modules like Registration & Delivery, Closed Household, Inventory, etc. — managing how data is searched, saved, updated, and deleted across related tables.

Installation #

Add this to your pubspec.yaml:

dependencies:
  digit_crud_bloc: ^0.0.3-dev

Then run:

flutter pub get

Features #

  • Create, search, update, and delete any entity (Household, Individual, Task, Stock, etc.)
  • Search across multiple related tables in a single query
  • Filter results using various operators (exact match, contains, within radius, etc.)
  • Proximity-based search — find entities within a given distance using GPS coordinates
  • Automatically load nested/related data (e.g., fetch household along with its members)
  • Pagination and ordering support for large result sets
  • Works with any entity model from digit_data_model

Getting Started #

Step 1: Extend CrudService #

Create your own service by extending CrudService and implementing getRepositoryForEntity for your entity types:

import 'package:digit_crud_bloc/digit_crud_bloc.dart';

class MyCrudService extends CrudService {
  final BuildContext context;

  MyCrudService({
    required this.context,
    required super.relationshipMap,
    required super.nestedModelMappings,
    required super.searchEntityRepository,
  });

  @override
  DataRepository? getRepositoryForEntity(EntityModel entity) {
    if (entity is HouseholdModel) {
      return context.repository<HouseholdModel, HouseholdSearchModel>(context);
    } else if (entity is IndividualModel) {
      return context.repository<IndividualModel, IndividualSearchModel>(context);
    } else if (entity is HouseholdMemberModel) {
      return context.repository<HouseholdMemberModel, HouseholdMemberSearchModel>(context);
    } else if (entity is ProjectBeneficiaryModel) {
      return context.repository<ProjectBeneficiaryModel, ProjectBeneficiarySearchModel>(context);
    } else if (entity is TaskModel) {
      return context.repository<TaskModel, TaskSearchModel>(context);
    }
    return null;
  }
}

Step 2: Implement DynamicEntityModelListener #

Provide a mapper to convert raw database rows into typed entity models:

class MyEntityModelMapper extends DynamicEntityModelListener {
  @override
  EntityModel? dynamicEntityModelFromMap(
    String modelName,
    Map<String, dynamic> map,
  ) {
    switch (modelName) {
      case 'household':
        return HouseholdModelMapper.fromMap(map);
      case 'individual':
        return IndividualModelMapper.fromMap(map);
      case 'householdMember':
        return HouseholdMemberModelMapper.fromMap(map);
      case 'projectBeneficiary':
        return ProjectBeneficiaryModelMapper.fromMap(map);
      case 'task':
        return TaskModelMapper.fromMap(map);
      default:
        return null;
    }
  }
}

Step 3: Initialize the Singleton #

Before using any CRUD features, initialize the singleton with all required dependencies:

CrudBlocSingleton.instance.setData(
  crudService: myCrudService,
  dynamicEntityModelListener: MyEntityModelMapper(),
);

Step 4: Use CrudBloc #

// Create the bloc
final crudBloc = CrudBloc(service: myCrudService);

// Initialize
crudBloc.add(const CrudEvent.initialize());

Usage Examples #

Search Entities #

final searchParams = GlobalSearchParameters(
  filters: [
    SearchFilter(
      root: 'household',
      field: 'clientReferenceId',
      operator: 'equals',
      value: 'HOUSEHOLD-1234',
    ),
  ],
  primaryModel: 'household',
  primaryKeyField: 'clientReferenceId',
  select: ['household', 'individual', 'householdMember', 'projectBeneficiary', 'task'],
  pagination: PaginationParams(limit: 10, offset: 0),
  filterLogic: MultiTableFilterLogic.and,
);

crudBloc.add(CrudEvent.search(searchParams));

Create Entities #

crudBloc.add(CrudEvent.create(entities: [householdModel, individualModel]));

Update Entities #

crudBloc.add(CrudEvent.update(entities: [updatedHouseholdModel]));

Delete Entities #

crudBloc.add(CrudEvent.delete(entities: [entityToDelete]));

Listen to State Changes #

BlocListener<CrudBloc, CrudState>(
  listener: (context, state) {
    state.when(
      initial: () => print('Initial'),
      loading: () => print('Loading...'),
      loaded: (results, totalCount) {
        // results is Map<String, List<EntityModel>>
        final households = results['household'] ?? [];
        final individuals = results['individual'] ?? [];
        print('Found ${households.length} households, ${individuals.length} individuals');
      },
      persisted: (entities) => print('Saved ${entities.length} entities'),
      error: (message) => print('Error: $message'),
    );
  },
)

Core Classes #

CrudBloc #

The BLoC that manages CRUD state.

Events:

Event Description
CrudEvent.initialize() Initialize the service
CrudEvent.search(GlobalSearchParameters) Search entities with filters
CrudEvent.create(entities) Create new entities
CrudEvent.update(entities) Update existing entities
CrudEvent.delete(entities) Delete entities

States:

State Description
CrudStateInitial Initial state before any operation
CrudStateLoading Operation in progress
CrudStateLoaded(results, totalCount) Search results loaded
CrudStatePersisted(entities) Create/update/delete succeeded
CrudStateError(message) Operation failed

GlobalSearchParameters #

The main search query configuration.

Parameter Type Description
filters List<SearchFilter> Filter conditions
select List<String> Entity types to return
primaryModel String? Primary table for pagination/count
primaryKeyField String? Primary key field name
pagination PaginationParams? Limit and offset
filterLogic MultiTableFilterLogic AND or OR across tables (default: AND)
orderBy SearchOrderBy? Ordering configuration

SearchFilter #

Parameter Type Description
field String Field name to filter
operator String Filter operator (see table below)
value dynamic Filter value
root String Table name the field belongs to
coordinates LatLng? Required for within operator

RelationshipMapping #

Defines how two entity tables are related.

Parameter Type Description
from String Source table name
to String Target table name
localKey String Key field in source table
foreignKey String Key field in target table

NestedModelMapping #

Defines nested fields that should be loaded from related tables.

Parameter Type Description
rootModel String Parent model name
fields Map<String, NestedFieldMapping> Nested field configurations

Supported Filter Operators #

Operator Description Example
equals Exact match field == value
notEqual Not equal field != value
contains Partial text match field LIKE %value%
in Value in list field IN (a, b, c)
notIn Value not in list field NOT IN (a, b, c)
isNull Field is null field IS NULL
isNotNull Field is not null field IS NOT NULL
equalsAny OR across multiple columns col1 == val OR col2 == val
within Geospatial radius search (Haversine) Entities within X km

Helper Classes #

Class Purpose
QueryBuilder SQL query construction and field mapping utilities
HydrationHelper Loads and attaches nested model data to raw rows
RelationshipGraphHelper BFS-based pathfinding between entity relationships
MultiTableFilterResolver Resolves filters across multiple tables to primary key constraints

Supported Entities #

Works with all entities from digit_data_model:

  • Household, Individual, Household Member
  • Project Beneficiary, Task
  • Stock, Stock Reconciliation
  • Facility, Project Facility
  • Referral, HF Referral
  • Product Variant, Project Resource
0
likes
30
points
131
downloads

Publisher

unverified uploader

Weekly Downloads

This package provides the core logic and services for managing entity-based data flows in a modular and extensible way. It supports dynamic entity mapping, relationship traversal, nested field resolution, and repository-driven CRUD operations.

License

MIT (license)

Dependencies

async, collection, dart_mappable, digit_data_model, dio, drift, flutter, flutter_bloc, freezed_annotation, intl, pluto_grid, recase, stream_transform, uuid

More

Packages that depend on digit_crud_bloc