onChange method

  1. @override
void onChange(
  1. TextEditingController textEditingController,
  2. String previousText
)
override

onChange is a function which is used to perform some action when the text is changed

Implementation

@override
void onChange(
    TextEditingController textEditingController, String previousText) {
  // Get cursor current position
  var cursorPosition = textEditingController.selection.base.offset;

  // if text is empty then we will clear the listItems and hide the panel
  if (textEditingController.text.isEmpty) {
    if (listItems.isNotEmpty) {
      resetMentionsTracker();
      mentionedUsersMap.clear();
      mentionCount.clear();
      lastCursorPos = cursorPosition;
      CometChatUIEvents.hidePanel(
          composerId, CustomUIPosition.composerPreview);
    }
    return;
  }

  //first we check if the current text has any part matching the regex pattern

  //the following logic is for when copy paste is involved
  if (mentionedUsersMap.isNotEmpty &&
      textEditingController.text.length > previousText.length + 1) {
    int extraCharactersLength =
        textEditingController.text.length - previousText.length;
    if (listItems.isNotEmpty) {
      resetMentionsTracker();
      CometChatUIEvents.hidePanel(
          composerId, CustomUIPosition.composerPreview);
    }

    int extraCharactersStartIndex = cursorPosition - extraCharactersLength;
    String newText = textEditingController.text
        .substring(cursorPosition - extraCharactersLength, cursorPosition);

    if (extraCharactersStartIndex > -1 && newText.isNotEmpty) {
      String prev = previousText.substring(0, extraCharactersStartIndex);
      Map<String, List<User?>> mentionedUsersMapCopy = mentionedUsersMap;
      mentionedUsersMap.forEach(
        (key, value) {
          RegExp currentPattern = RegExp(key);
          if (currentPattern.hasMatch(newText)) {
            //@john @harry @john
            final oldMatchesCount = currentPattern.allMatches(prev).length;
            //@john @john @harry @john
            final newMatchesCount = currentPattern.allMatches(newText).length;

            mentionedUsersMapCopy[key]?.insertAll(
                oldMatchesCount,
                List<User?>.generate(newMatchesCount, (int index) => null,
                    growable: false));
          }
        },
      );
      mentionedUsersMap = mentionedUsersMapCopy;
    }
    lastCursorPos = cursorPosition;

    return;
  }

  //check if new text has same length as previous text
  if (previousText != textEditingController.text &&
      previousText.length > textEditingController.text.length) {
    //first check if the mentionTracker is not empty
    if (mentionTracker.isNotEmpty) {
      if (searchOnActiveMention && cursorPosition == lastCursorPos - 1) {
        //check if the user has triggered search by going back to the end of a mention
        cursorInMentionTracker(
            cursorPosition, textEditingController, previousText);
      } else if (cursorPosition == mentionStartIndex) {
        // if cursor position is at the start of the mentionTracker
        // it means the @ the tracking character is removed by the user
        resetMentionsTracker();
        CometChatUIEvents.hidePanel(
            composerId, CustomUIPosition.composerPreview);
      } else {
        //if the cursor position is between the start and end of the mentionTracker
        //it means the user is deleting the mentionTracker
        if (cursorPosition > mentionStartIndex &&
            cursorPosition <= mentionEndIndex) {
          mentionTracker =
              mentionTracker.substring(0, cursorPosition - mentionStartIndex);
          mentionEndIndex = cursorPosition - 1;
          if (mentionTracker.isEmpty && onSearch != null) {
            onSearch!(null);
          }
        } else if (cursorPosition < mentionStartIndex) {
          //if the cursor position is before the start of the mentionTracker
          //it means the user is deleting the mentionTracker
          mentionStartIndex = mentionStartIndex -
              (previousText.length - textEditingController.text.length);
          mentionEndIndex = mentionEndIndex -
              (previousText.length - textEditingController.text.length);
        }
      }
    } else {
      if (previousText[cursorPosition] == trackingCharacter) {
        cursorInMentionTracker(
            cursorPosition, textEditingController, previousText);
      } else {
        int lastIndexOfTrackingChar = textEditingController.text
            .substring(0, cursorPosition)
            .lastIndexOf(trackingCharacter!);
        if (lastIndexOfTrackingChar != -1 &&
            (lastIndexOfTrackingChar == 0 ||
                lastIndexOfTrackingChar > 1 &&
                    textEditingController.text[lastIndexOfTrackingChar - 1] ==
                        " ")) {
          String tempTracker = textEditingController.text
              .substring(lastIndexOfTrackingChar, cursorPosition);

          if ((!tempTracker.contains("\n") &&
                  !tempTracker.contains("    ")) &&
              mentionedUsersMap.containsKey(tempTracker.trim())) {
            if (mentionedUsersMap.containsKey(tempTracker)) {
              cursorInMentionTracker(
                  cursorPosition, textEditingController, previousText);
            }
          } else if (cursorPosition < lastCursorPos - 1 ||
              cursorPosition > lastCursorPos) {
            //if there has been a jump in the cursor position
            //we reset the mentionTracker
            // we first hide the panel
            CometChatUIEvents.hidePanel(
                composerId, CustomUIPosition.composerPreview);
            cursorInMentionTracker(
                cursorPosition, textEditingController, previousText);
          } else if (!tempTracker.contains("\n") &&
              !tempTracker.contains(" ")) {
            mentionTracker = tempTracker;
            mentionStartIndex = lastIndexOfTrackingChar;
            mentionEndIndex = cursorPosition - 1;
          }
        }
      }
    }
  } else {
    String previousCharacter = cursorPosition == 0
        ? ""
        : textEditingController.text[cursorPosition - 1];

    bool isSpace = (cursorPosition == 1
        // &&( textEditingController.text.length<2  || ( textEditingController.text[1]==" " ))
        ) ||
        (textEditingController.text.length > 1 &&
            cursorPosition > 1 &&
            (textEditingController.text[cursorPosition - 2] == " " ||
                textEditingController.text[cursorPosition - 2] == "\n"));

    if (previousCharacter == trackingCharacter && isSpace) {
      mentionTracker = trackingCharacter!;
      mentionStartIndex = cursorPosition - 1;
      checkIfTrackerPlacedCausesDuplication(
          cursorPosition - 1, textEditingController);
    } else if (previousCharacter == trackingCharacter &&
        !isSpace &&
        !searchOnActiveMention) {
      int lastIndexOfTrackingChar = textEditingController.text
          .substring(0, cursorPosition)
          .lastIndexOf(trackingCharacter!);
      if (lastIndexOfTrackingChar != -1 &&
          (lastIndexOfTrackingChar == 0 ||
              lastIndexOfTrackingChar > 1 &&
                  textEditingController.text[lastIndexOfTrackingChar - 1] ==
                      " ")) {
        String tempTracker = textEditingController.text
            .substring(lastIndexOfTrackingChar, cursorPosition);

        if ((!tempTracker.contains("\n") && !tempTracker.contains("    ")) &&
            mentionedUsersMap.containsKey(tempTracker.trim())) {
          cursorInMentionTracker(
              cursorPosition, textEditingController, previousText);
        }
      }
    } else if (searchOnActiveMention && cursorPosition == lastCursorPos + 1) {
      searchOnActiveMention = false;
      resetMentionsTracker();
    } else if (mentionTracker.isNotEmpty) {
      mentionTracker += previousCharacter;
      mentionEndIndex = cursorPosition - 1;
      //if the current text length is greater than the previous text length
      if (cursorPosition <= mentionStartIndex) {
        //if the cursor position is before the start of the mentionTracker
        //it means the user is adding text before the mentionTracker

        cursorInMentionTracker(
            cursorPosition, textEditingController, previousText);
      } else if (mentionStartIndex <= cursorPosition &&
          mentionEndIndex >= cursorPosition) {
        //if the cursor position is between the start and end of the mentionTracker
        //it means the user is adding text between the mentionTracker
        cursorInMentionTracker(
            cursorPosition, textEditingController, previousText);
        mentionEndIndex = mentionEndIndex +
            (textEditingController.text.length - previousText.length);
        mentionTracker = textEditingController.text
            .substring(mentionStartIndex, mentionEndIndex + 1);
      }
    } else {
      int lastIndexOfTrackingChar = textEditingController.text
          .substring(0, cursorPosition)
          .lastIndexOf(trackingCharacter!);
      if (lastIndexOfTrackingChar != -1 &&
          (lastIndexOfTrackingChar == 0 ||
              lastIndexOfTrackingChar > 1 &&
                  textEditingController.text[lastIndexOfTrackingChar - 1] ==
                      " ")) {
        String tempTracker = textEditingController.text
            .substring(lastIndexOfTrackingChar, cursorPosition);

        // if(tempTracker){}else
        if ((!tempTracker.contains("\n") && !tempTracker.contains("    "))
            // &&  (!mentionedUsersMap.containsKey(tempTracker.trim()) && textEditingController.text.length>1 && textEditingController.text[cursorPosition-2]!=" ")
            &&
            mentionedUsersMap.containsKey(tempTracker.trim())) {
          if (mentionedUsersMap.containsKey(tempTracker)) {
            cursorInMentionTracker(
                cursorPosition, textEditingController, previousText);
          }
        } else if (cursorPosition < lastCursorPos ||
            cursorPosition > lastCursorPos + 1) {
          //if there has been a jump in the cursor position
          //we reset the mentionTracker
          // we first hide the panel
          CometChatUIEvents.hidePanel(
              composerId, CustomUIPosition.composerPreview);
          cursorInMentionTracker(
              cursorPosition, textEditingController, previousText);
        } else if (!tempTracker.contains("\n") &&
            !tempTracker.contains(" ")) {
          mentionTracker = tempTracker;
          mentionStartIndex = lastIndexOfTrackingChar;
          mentionEndIndex = cursorPosition - 1;
        }
      }
    }
  }

  /// if the mention tracker has four consecutive spaces then we will stop tracking the mention
  if ((mentionTracker.length > 1 &&
          mentionTracker[mentionTracker.length - 1] == "\n") ||
      mentionTracker.length > 4 &&
          mentionTracker
              .substring(mentionTracker.length - 4)
              .trim()
              .isEmpty) {
    String mention = mentionTracker;
    if (mentionedUsersMap.containsKey(mention)) {
      int matchesFound = textEditingController.text
          .substring(0, cursorPosition - mentionTracker.length)
          .allMatches(mention)
          .length;
      mentionedUsersMap[mention]!.insert(matchesFound, null);
    } else {
      mentionedUsersMap[mention] = [null];
    }
    mentionTracker = "";
    mentionStartIndex = 0;
    mentionEndIndex = 0;
    if (onSearch != null) {
      onSearch!(null);
    }

    CometChatUIEvents.hidePanel(composerId, CustomUIPosition.composerPreview);
  } else if (mentionTracker.isNotEmpty) {
    //if 10 users have been mentioned then we will show a message to the user
    //that the maximum limit of mentions has been reached
    if (mentionCount.length == 10) {
      CometChatUIEvents.showPanel(
          composerId,
          CustomUIPosition.composerPreview,
          (context) => Container(
                height: 40,
                padding: const EdgeInsets.symmetric(horizontal: 16.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      AssetConstants.info,
                      package: UIConstants.packageName,
                      color: _theme.palette.getAccent(),
                      height: 16.18,
                    ),
                    Padding(
                      padding: const EdgeInsets.only(left: 8.0),
                      child: Text(
                          cc.Translations.of(context).mentionsMaxLimitHit),
                    ),
                  ],
                ),
              ));
    } else {
      String searchKeyword = mentionTracker.substring(1);

      if ((mentionedUsersMap.containsKey(mentionTracker) &&
              !searchOnActiveMention) ||
          (interceptedMention != null &&
              (cursorPosition < textEditingController.text.length &&
                  mentionTracker.length < interceptedMention!.length) &&
              ((cursorPosition +
                          interceptedMention!.length -
                          mentionTracker.length <
                      textEditingController.text.length) &&
                  mentionTracker +
                          textEditingController.text.substring(
                              cursorPosition,
                              cursorPosition +
                                  interceptedMention!.length -
                                  mentionTracker.length) ==
                      interceptedMention))) {
        String key = interceptedMention ?? mentionTracker;

        int leftSideMatches = RegExp(key)
            .allMatches(textEditingController.text
                .substring(0, cursorPosition - mentionTracker.length))
            .length;

        mentionedUsersMap[key]!.insert(leftSideMatches, null);
      }

      if (listItems.isEmpty) {
        CometChatUIEvents.showPanel(
            composerId,
            CustomUIPosition.composerPreview,
            (context) => getLoadingIndicator(context, cometChatTheme));
      }

      if (onSearch != null) {
        onSearch!(mentionTracker);
      }
      initializeFetchRequest(searchKeyword, textEditingController);
    }
  }
  lastCursorPos = cursorPosition;
}