> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-rn-guide-message-privately.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# BLoC & Data

> Configure data fetching with RequestBuilders, provide custom BLoC instances, and listen to state changes.

Each component's BLoC manages data fetching, state transitions, and business logic. You can configure it via RequestBuilders, provide a custom BLoC instance, or extend the default one.

## Configuring Data Fetching with RequestBuilders

Use request builders to control what data the component fetches. Pass the builder instance — not the result of `.build()`.

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    CometChatMessageList(
      user: user,
      messagesRequestBuilder: MessagesRequestBuilder()
        ..uid = user.uid
        ..searchKeyword = 'hello'
        ..limit = 30,
    )
    ```
  </Tab>
</Tabs>

<Warning>
  The following parameters in `MessagesRequestBuilder` will always be altered inside the message list: UID, GUID, types, categories.
</Warning>

### Request Builder by Component

| Component                | Builder Property              | Builder Type                  |
| ------------------------ | ----------------------------- | ----------------------------- |
| `CometChatConversations` | `conversationsRequestBuilder` | `ConversationsRequestBuilder` |
| `CometChatUsers`         | `usersRequestBuilder`         | `UsersRequestBuilder`         |
| `CometChatGroups`        | `groupsRequestBuilder`        | `GroupsRequestBuilder`        |
| `CometChatGroupMembers`  | `groupMembersRequestBuilder`  | `GroupMembersRequestBuilder`  |
| `CometChatMessageList`   | `messagesRequestBuilder`      | `MessagesRequestBuilder`      |

## Providing a Custom BLoC

Each component accepts an optional BLoC parameter. Provide your own instance to override default behavior:

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    final myBloc = MessageListBloc(
      user: user,
      parentMessageId: null,
      types: MessageTemplateUtils.getAllMessageTypes(),
      categories: MessageTemplateUtils.getAllMessageCategories(),
    );

    CometChatMessageList(
      user: user,
      messageListBloc: myBloc,
    )
    ```
  </Tab>
</Tabs>

### BLoC Parameters by Component

| Component                  | BLoC Property         | BLoC Type             |
| -------------------------- | --------------------- | --------------------- |
| `CometChatConversations`   | `conversationsBloc`   | `ConversationsBloc`   |
| `CometChatMessageList`     | `messageListBloc`     | `MessageListBloc`     |
| `CometChatMessageComposer` | `messageComposerBloc` | `MessageComposerBloc` |

## Extending the Default BLoC

Extend the default BLoC class and override hooks to add custom behavior:

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    class CustomMessageListBloc extends MessageListBloc {
      CustomMessageListBloc({required User user})
          : super(user: user);

      @override
      void onItemAdded(BaseMessage item, List<BaseMessage> updatedList) {
        // Custom logic when a message is added
        debugPrint('Message added: ${item.id}');
        super.onItemAdded(item, updatedList);
      }

      @override
      void onItemRemoved(BaseMessage item, List<BaseMessage> updatedList) {
        // Custom logic when a message is removed
        super.onItemRemoved(item, updatedList);
      }

      @override
      void onItemUpdated(BaseMessage oldItem, BaseMessage newItem, List<BaseMessage> updatedList) {
        // Custom logic when a message is updated
        super.onItemUpdated(oldItem, newItem, updatedList);
      }
    }
    ```
  </Tab>
</Tabs>

### ListBase Hooks

All list-based BLoCs use the `ListBase` mixin with these override hooks:

| Hook                                           | Called When                      |
| ---------------------------------------------- | -------------------------------- |
| `onItemAdded(item, updatedList)`               | An item is added to the list     |
| `onItemRemoved(item, updatedList)`             | An item is removed from the list |
| `onItemUpdated(oldItem, newItem, updatedList)` | An item is updated in the list   |
| `onListCleared(previousList)`                  | The list is cleared              |
| `onListReplaced(previousList, newList)`        | The entire list is replaced      |

## Component-Specific BLoC Events

Each component's BLoC has its own events and methods. See the individual component docs for details:

* [Message List — BLoC Events & Methods](/ui-kit/flutter/message-list#advanced)
* [Conversations — BLoC Events](/ui-kit/flutter/conversations#advanced)
* [Users — BLoC Events](/ui-kit/flutter/users#advanced)
* [Groups — BLoC Events](/ui-kit/flutter/groups#advanced)
* [Group Members — BLoC Events](/ui-kit/flutter/group-members#advanced)

## Lifecycle Callbacks

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    CometChatMessageList(
      user: user,
      onLoad: (messages) {
        debugPrint('Loaded ${messages.length} messages');
      },
      onEmpty: () {
        debugPrint('No messages found');
      },
      onError: (e) {
        debugPrint('Error: ${e.message}');
      },
    )
    ```
  </Tab>
</Tabs>

## Related

* [Message List](/ui-kit/flutter/message-list) — Full message list component reference.
* [Customization Overview](/ui-kit/flutter/customization-overview) — See all customization categories.

***

## Repository & Datasource Overrides

Both `CometChatConversations` and `CometChatMessageList` follow the same clean architecture stack. There are two override points:

```
Widget → BLoC → UseCase → Repository → DataSource (Remote + Local)
                  ↑               ↑
          override here    or here
```

### 1. Datasource Override

Implement the abstract datasource interfaces to swap the data layer entirely — e.g. a REST API instead of the CometChat SDK, or a persistent cache instead of in-memory.

#### Conversations

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    abstract class ConversationsRemoteDataSource {
      Future<List<Conversation>> getConversations({int limit, String? fromId});
      Future<Conversation> getConversation(String conversationId);
      Future<void> deleteConversation(String conversationWith, String conversationType);
      Future<void> markMessageAsRead(BaseMessage message);
      Future<Conversation> updateConversation(Conversation conversation);
    }

    abstract class ConversationsLocalDataSource {
      Future<void> cacheConversations(List<Conversation> conversations);
      Future<List<Conversation>> getCachedConversations();
      Future<void> cacheConversation(Conversation conversation);
      Future<Conversation?> getCachedConversation(String conversationId);
      Future<void> removeCachedConversation(String conversationId);
      Future<void> clearCache();
      Future<bool> hasCachedData();
    }
    ```
  </Tab>
</Tabs>

#### Message List

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    abstract class MessageListRemoteDataSource {
      Future<GetMessagesResult> getMessages({...});
      Future<List<BaseMessage>> fetchPreviousMessages({required MessagesRequest request});
      Future<List<BaseMessage>> fetchNextMessages({required MessagesRequest request});
      Future<void> markAsRead(BaseMessage message);
      Future<void> markAsDelivered(BaseMessage message);
      Future<User?> getLoggedInUser();
      Future<Conversation> getConversation({...});
      Future<Conversation> markMessageAsUnread(BaseMessage message);
    }

    abstract class MessageListLocalDataSource {
      Future<void> cacheMessages(String conversationId, List<BaseMessage> messages);
      Future<List<BaseMessage>> getCachedMessages(String conversationId);
      // ... cache CRUD methods
    }
    ```
  </Tab>
</Tabs>

***

### 2. Repository Override

The repository interfaces sit above the datasources. Override here to change business logic — caching strategy, error handling, retry logic — without touching the datasource layer.

#### Conversations

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    abstract class ConversationsRepository {
      Future<Result<List<Conversation>>> getConversations({int limit, String? fromId});
      Future<Result<Conversation>> getConversationById(String conversationId);
      Future<Result<void>> deleteConversation(String conversationId);
      Future<Result<Conversation>> updateConversation(Conversation conversation);
      Future<Result<User?>> getLoggedInUser();
      Future<Result<void>> markAsDelivered(BaseMessage message);
      Future<Result<Conversation>> getConversation({...});
    }
    ```
  </Tab>
</Tabs>

#### Message List

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    abstract class MessageListRepository {
      Future<Result<List<BaseMessage>>> getMessages({...});
      Future<Result<List<BaseMessage>>> fetchPreviousMessages({...});
      Future<Result<List<BaseMessage>>> fetchNextMessages({...});
      Future<Result<void>> markAsRead(BaseMessage message);
      Future<Result<void>> markAsDelivered(BaseMessage message);
      Future<Result<User?>> getLoggedInUser();
      Future<Result<Conversation>> getConversation({...});
      Future<Result<Conversation>> markMessageAsUnread(BaseMessage message);
    }
    ```
  </Tab>
</Tabs>

***

### 3. Wiring via the Service Locator

The service locators are the injection point. Both are singletons with a `setup()` method. Call `reset()` first, then `setup()` with your custom implementations before the widget mounts.

#### Datasource-level override (Conversations)

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    class MyConversationsRemoteDataSource implements ConversationsRemoteDataSource {
      @override
      Future<List<Conversation>> getConversations({int limit = 30, String? fromId}) async {
        // Your REST API call, mock data, etc.
      }
      // ... implement remaining methods
    }

    // Before mounting CometChatConversations:
    await ConversationsServiceLocator.instance.reset();

    final remote = MyConversationsRemoteDataSource();
    final local = ConversationsLocalDataSourceImpl(); // keep default cache
    final repo = ConversationsRepositoryImpl(
      remoteDataSource: remote,
      localDataSource: local,
    );
    ```
  </Tab>
</Tabs>

#### Repository-level override (Message List)

<Tabs>
  <Tab title="Dart">
    ```dart theme={null}
    class MyMessageListRepository implements MessageListRepository {
      @override
      Future<Result<List<BaseMessage>>> getMessages({...}) async {
        // Custom logic
      }
      // ...
    }

    // Before mounting CometChatMessageList:
    await MessageListServiceLocator.instance.reset();
    // Wire your repo into use cases and pass to the BLoC
    ```
  </Tab>
</Tabs>

***

### Key Points

| Point                                            | Detail                                                                                                                                                                         |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `reset()`                                        | Sets `_isInitialized = false` — always call before re-wiring                                                                                                                   |
| `setup()`                                        | Guarded by `_isInitialized` — safe to call multiple times normally                                                                                                             |
| `localDataSource` in `MessageListRepositoryImpl` | Optional — pass `null` to disable caching entirely                                                                                                                             |
| Fallback caching                                 | `ConversationsRepositoryImpl` falls back to local cache when remote fails — your custom remote inherits this if you use `ConversationsRepositoryImpl` with a custom datasource |
| `Result<T>`                                      | All repository methods return `Result<T>` (`Success`/`Failure`) — your implementations must do the same                                                                        |
