> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-agent-native-development.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.

<Accordion title="AI Integration Quick Reference">
  | Field | Value |
  | - | - |
  | Package | `cometchat_chat_uikit` |
  | Import | `import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';` |
  | Purpose | Configure data fetching with RequestBuilders, provide custom BLoC instances, and listen to state changes. |
  | Key classes | `CometChatMessageList` · `CometChatConversations` |
  | Related | [Message List](/ui-kit/flutter/message-list) · [Conversations](/ui-kit/flutter/conversations) |
</Accordion>

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.