Skip to content

Overview

  • File: lib/widgets/chat_window.dart

The ChatWindow is a Flutter widget that provides a chat interface for the HVC Mobile application. It supports both regular chat functionality and in-call chat features, with capabilities for sending messages, viewing message history, and managing chat participants.

Class Structure

Class Definition

dart
class ChatWindow extends StatelessWidget {
  final RxList<Message> messageList;
  final RxBool loading;
  final PagingController<int, Message> pagingController;
  final Future Function() loader;
  final double width, height;
  final int userId;
  final Function()? onCloseOrCancel;
  final Function(String) sendMessage;
  final bool showNotification;
  final Function(bool) onNotificationChange;
  final Function(InCallChatOption?)? onInCallChatOptionChange;
  final ScrollController scrollController;
  final bool isForCallPage;
  final RxList<InCallChatOption>? options;

  ChatWindow({
    Key? key,
    required this.messageList,
    required this.width,
    required this.userId,
    required this.height,
    this.onCloseOrCancel,
    required this.sendMessage,
    required this.onNotificationChange,
    required this.showNotification,
    required this.loading,
    required this.pagingController,
    required this.loader,
    required this.scrollController,
    this.isForCallPage = false,
    this.options,
    this.onInCallChatOptionChange,
  }) : super(key: key);
}

Supporting Classes

dart
class InCallChatOption {
  final String name;
  final int id;

  InCallChatOption({required this.name, required this.id});
}

class LabeledSwitch extends StatelessWidget {
  const LabeledSwitch({
    Key? key,
    required this.label,
    required this.value,
    required this.onChanged,
  }) : super(key: key);

  final String label;
  final bool value;
  final ValueChanged<bool> onChanged;

  // Implementation...
}

Dependencies

Required Packages

  • flutter/material.dart - Core Flutter UI components
  • get/get_rx/src/rx_types/rx_types.dart - Reactive state types
  • get/get_state_manager/src/rx_flutter/rx_obx_widget.dart - Reactive widgets
  • infinite_scroll_pagination - Infinite scrolling functionality

Internal Dependencies

  • models/core_models.dart - Data models
  • shared/constants.dart - Shared constants
  • widgets/message_item.dart - Message display widget
  • widgets/no_item_loader.dart - Empty state widget

State Management

State Variables

dart
final TextEditingController smsController = TextEditingController(text: '');
RxBool hasText = false.obs;
final _formKey = GlobalKey<FormState>();

Reactive State

dart
Obx(() {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (scrollController.hasClients) {
      scrollController.animateTo(
        scrollController.position.maxScrollExtent,
        duration: const Duration(milliseconds: 500),
        curve: Curves.easeInOut,
      );
    }
  });
  return PagedListView<int, Message>(
    // Implementation...
  );
})

UI Components

Main Container

dart
Container(
  height: height,
  width: width,
  color: kAppColorWhite,
  child: Column(
    children: [
      Expanded(
        child: Obx(
          () => loading.isTrue && messageList.isEmpty
              ? const Center(child: CircularProgressIndicator())
              : RefreshIndicator(
                  child: isForCallPage
                      ? Column(
                          children: [
                            // In-call chat header
                            // Message list
                          ],
                        )
                      : listview,
                  onRefresh: () async {
                    await loader();
                  },
                ),
        ),
      ),
      // Message input area
    ],
  ),
)

Message List

dart
PagedListView<int, Message>(
  pagingController: pagingController,
  scrollController: scrollController,
  padding: const EdgeInsets.all(8),
  builderDelegate: PagedChildBuilderDelegate<Message>(
    animateTransitions: messageList.isNotEmpty,
    itemBuilder: (BuildContext context, Message message, int index) =>
        Container(
      margin: index == messageList.length - 1
          ? const EdgeInsets.only(bottom: 32)
          : null,
      child: MessageItem(
        message: message,
        isMe: message.fromId == userId,
      ),
    ),
    noItemsFoundIndicatorBuilder: (BuildContext context) => NoItemLoader(
      message: 'No Messages present',
      onTapHandler: () async => await loader(),
    ),
  ),
)

Message Input Area

dart
Container(
  decoration: BoxDecoration(
    border: hasText.value ? Border.all(color: kAppColorBlue) : null,
    color: kAppColorWhite,
    borderRadius: BorderRadius.circular(5),
  ),
  margin: const EdgeInsets.all(8),
  child: Form(
    key: _formKey,
    child: Row(
      children: [
        Expanded(
          child: TextField(
            controller: smsController,
            minLines: 1,
            maxLines: 4,
            keyboardType: TextInputType.multiline,
            style: hasText.value
                ? const TextStyle(color: kAppColorBlack)
                : null,
            decoration: InputDecoration(
              filled: true,
              hintText: 'Enter message',
              suffixIcon: Obx(
                () => IconButton(
                  onPressed: hasText.value
                      ? () {
                          if (smsController.text.isNotEmpty) {
                            scrollController.animateTo(
                              scrollController.position.maxScrollExtent + 64,
                              duration: const Duration(milliseconds: 500),
                              curve: Curves.easeInOut,
                            );
                            sendMessage(smsController.text);
                            smsController.clear();
                          }
                        }
                      : null,
                  icon: const Icon(Icons.send),
                ),
              ),
            ),
            autofocus: hasText.value,
            onChanged: (String text) {
              hasText.value = smsController.text.isNotEmpty;
            },
          ),
        ),
        const IconButton(
          onPressed: null,
          icon: Icon(Icons.attach_file),
        ),
      ],
    ),
  ),
)

Message Handling

Sending Messages

dart
onPressed: hasText.value
    ? () {
        if (smsController.text.isNotEmpty) {
          scrollController.animateTo(
            scrollController.position.maxScrollExtent + 64,
            duration: const Duration(milliseconds: 500),
            curve: Curves.easeInOut,
          );
          sendMessage(smsController.text);
          smsController.clear();
        }
      }
    : null,

Message Display

dart
MessageItem(
  message: message,
  isMe: message.fromId == userId,
)

Empty State Handling

dart
noItemsFoundIndicatorBuilder: (BuildContext context) => NoItemLoader(
  message: 'No Messages present',
  onTapHandler: () async => await loader(),
),

In-Call Chat Features

In-Call Chat Header

dart
Container(
  color: kAppColorBlue,
  padding: const EdgeInsets.symmetric(horizontal: 4),
  child: Row(
    children: [
      Expanded(
        child: DropdownButtonFormField<InCallChatOption>(
          decoration: getInputDecorationNoBorder(
              'Chat With:', false,
              style: whiteTextStyle),
          value: options?.first,
          onChanged: (InCallChatOption? newValue) {
            if (onInCallChatOptionChange != null) {
              onInCallChatOptionChange!(newValue);
            }
          },
          items: options?.map<DropdownMenuItem<InCallChatOption>>(
              (InCallChatOption value) {
            return DropdownMenuItem<InCallChatOption>(
              value: value,
              child: Text(value.name),
            );
          }).toList(),
        ),
      ),
      IconButton(
        onPressed: onCloseOrCancel,
        icon: const Icon(Icons.close, color: kAppColorWhite),
      ),
    ],
  ),
)

In-Call Chat Option

dart
class InCallChatOption {
  final String name;
  final int id;

  InCallChatOption({required this.name, required this.id});
}

Released under the MIT License.