I 'am writing a message application whenever I insert a new message at the 0th index in the list all the messages within the offset are rerendered even though their properties have not been changed. How can I stop this?

Below in the code, you can see The ChatBook class which renders MessageList which is a a ScrollablePositionedListView.

I opted this in order to reach tagged messages instantly. As soon as I press the send button which is in InputBar The onPressSend Method is called which adds new message in the _messages array also I have provided _messages array inside InheritedWidget so that i can inherit it within the child since it required at multiple places. Whenever i add a new message simple a textmessage it re renders all the previous messages even though there properties have not been changed. Below are the required code snippets.

ChatBook class



/// This is the entry point of the [ChatBook] package.

class ChatBook extends StatefulWidget {
  const ChatBook({
    Key? key,
    required this.author,
    required this.onSendMessage,
    this.theme = const DefaultChatTheme(),
    this.giphyApiKey,
  }) : super(key: key);

  /// List of messages from which messagefor [Ayesavi ChatBook] will be retrieved by default this field is provided [DefaultChatTheme]

  /// Theme assigned to ChatBook for info see [ChatTheme]
  final ChatTheme theme;

  /// callback for onPress event on sendMessageButton in inputbar [InputBar]
  final void Function(Message currentMessage) onSendMessage;

  /// GiphyApiKey required to retrieve giphy from servers.
  final String? giphyApiKey;

  final User author;

  @override
  State<ChatBook> createState() => _ChatBookState();
}

class _ChatBookState extends State<ChatBook> {
  final List<Message> _messages = [];

  /// Initialising ItemScrollController
  /// It is needed for jumpTo and scrollTo methods to reach any particular item
  final ItemScrollController itemScrollController = ItemScrollController();

  /// Initialising ItemPositionListner
  /// Listens for the position of any item.
  final ItemPositionsListener itemPositionsListener =
      ItemPositionsListener.create();

  final TagMessageHelper _tagMessageHelper = TagMessageHelper();

  /// It is for holding the selectedGif after onPressedEvent on Gif

  /// GiphyCient is later initilising the [GiphyClient] in the [initState] method.
  late GiphyClient client;

  /// System used to generate random id for the user at the runtime by default initialised with aan empty String
  String randomId = "";

  /// For holding giphy Api Key
  //! Store API keys in the env mode separate it is advised so to keep your API keys private to you only

  @override
  void initState() {
    super.initState();
    if (widget.giphyApiKey != null) {
      client = GiphyClient(apiKey: widget.giphyApiKey!, randomId: '');
      WidgetsBinding.instance.addPostFrameCallback((_) {
        client.getRandomId().then((value) {
          setState(() {
            randomId = value;
          });
        });
      });
    }
  }

  void onPressSend(
    Message sendingMessage,
  ) {
    setState(() {
      _messages.insert(0, sendingMessage);
    });
    widget.onSendMessage.call(sendingMessage.copyWith(
      self: false,
    ));
  }

  @override
  Widget build(BuildContext context) {
    return GiphyGetWrapper(

        /// GiphyGetWrapper is used to get giphys where ever called in the application.
        giphy_api_key: widget.giphyApiKey!,
        builder: (stream, giphyGetWrapper) {
          stream.listen((gif) {
            // widget.onGiphyPressed?.call(gif);
            onPressSend(GifMessage(
                author: const User(id: ""),
                id: '',
                gif: gif,
                createdAt: DateTime.now().millisecondsSinceEpoch,
                self: true,
                status: Status.seen));
          });

          return InheritedProperties(
            tagHelper: _tagMessageHelper,
            theme: widget.theme,
            giphyGetWrapper: giphyGetWrapper,
            author: widget.author,
            //* Needed for inheriting acess of messages to all its child widgets.
            child: InheritedMessagesWidget(
              messages: _messages,
              child: Column(
                children: <Widget>[
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 18.00,
                      ),
                      child: MessageList(
                        controller: itemScrollController,
                        positionsListener: itemPositionsListener,
                      ),
                    ),
                  ),
                  const SizedBox(
                    height: 10,
                  ),
                  // RepaintBoundary(
                  // child:
                  ValueListenableBuilder(
                      valueListenable: _tagMessageHelper.tagNotifier,
                      builder: (_, value, __) {
                        if (value == null) return const SizedBox();
                        return TaggedMessageIndicator(
                            message: value as Message);
                      }),
                  // ),
                  ConstrainedBox( 
                      constraints:
                          const BoxConstraints(maxHeight: 150, minHeight: 60),
                      child: InputBar(
                        giphyGetWrapper: giphyGetWrapper,
                        onSendMessage: onPressSend,
                      ))
                ],
              ),
            ),
          );
        });
  }
}

MessageList



class MessageList extends StatefulWidget {
  const MessageList(
      {Key? key, required this.controller, required this.positionsListener})
      : super(key: key);

  final ItemScrollController controller;
  final ItemPositionsListener positionsListener;

  @override
  State<MessageList> createState() => _MessageListState();
}

class _MessageListState extends State<MessageList> {
  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ScrollablePositionedList.builder(
      reverse: true,
      itemScrollController: widget.controller,
      itemPositionsListener: widget.positionsListener,
      initialScrollIndex: 0,
      itemCount: InheritedMessagesWidget.of(context).messages.length,
      itemBuilder: (_, index) {
        return MessageBuilder(
          message: InheritedMessagesWidget.of(context).messages[index],
          prevMessage:
              index != InheritedMessagesWidget.of(context).messages.length - 1
                  ? InheritedMessagesWidget.of(context).messages[index + 1]
                  : null,
        );
      },
      scrollDirection: Axis.vertical,
    );
  }
}

MessageBuilder



class MessageBuilder extends StatefulWidget {
  const MessageBuilder({Key? key, required this.message, this.prevMessage})
      : super(key: key);
  final Message message;
  final Message? prevMessage;
  @override
  State<MessageBuilder> createState() => _MessageBuilderState();
}

class _MessageBuilderState extends State<MessageBuilder> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _dateProvider(widget.message, widget.prevMessage),
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 2.0),
          child: Container(
            width: double.infinity,
            alignment:
                widget.message.self != null && widget.message.self == true
                    ? Alignment.centerRight
                    : Alignment.centerLeft,
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                LimitedBox(
                  maxWidth: 6.5 / 10 * (MediaQuery.of(context).size.width),
                  child: IntrinsicWidth(
                      child: RepaintBoundary(
                    child: Swipeable(
                      maxOffset: .7,
                      movementDuration: const Duration(milliseconds: 500),
                      background: const Align(
                          alignment: Alignment.centerLeft,
                          child: Icon(
                            Icons.share,
                            size: 20,
                            color: Colors.white,
                          )),
                      direction: SwipeDirection.startToEnd,
                      onSwipe: (dir) {
                        if (SwipeDirection.startToEnd == dir) {
                          logger.log("swiped");
                          InheritedProperties.of(context)
                              .tagHelper
                              .tagNotifier
                              .value = widget.message;
                        }
                      },
                      confirmSwipe: (direction) async {
                        logger.log((SwipeDirection.startToEnd == direction)
                            .toString());
                        return SwipeDirection.startToEnd == direction;
                      },
                      allowedPointerKinds: const {
                        PointerDeviceKind.mouse,
                        PointerDeviceKind.stylus,
                        PointerDeviceKind.touch,
                        PointerDeviceKind.trackpad,
                        PointerDeviceKind.unknown,
                      },
                      key: const ValueKey(1),
                      child: Bubble(
                        padding: const BubbleEdges.all(0),
                        showNip: _nipGiver(widget.message, widget.prevMessage),
                        nip: widget.message.self == true
                            ? BubbleNip.rightTop
                            : BubbleNip.leftTop,
                        color: _bubbleColorGiver(widget.message),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            _messageProviderWidget(widget.message)!,
                            Padding(
                              padding:
                                  const EdgeInsets.symmetric(horizontal: 8.0),
                              child: Row(
                                  mainAxisAlignment: MainAxisAlignment.end,
                                  children: [
                                    Text(
                                        DateFormat.jm().format(
                                            DateTime.fromMillisecondsSinceEpoch(
                                                widget.message.createdAt!)),
                                        style: widget.message.self == true
                                            ? InheritedProperties.of(context)
                                                .theme
                                                .sentTimeTextStyle
                                            : InheritedProperties.of(context)
                                                .theme
                                                .receivedTimeTextStyle),
                                    const SizedBox(
                                      width: 5,
                                    ),
                                    if (widget.message.self == true)
                                      _statusProvider(widget.message),
                                  ]),
                            ),
                          ],
                        ),
                      ),
                    ),
                  )),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }

  Widget? _messageProviderWidget(Message message) {
    MessageType type = message.type;
    switch (type) {
      case MessageType.text:
        return TextMessageWidget(
          message: message as TextMessage,
        );
      case MessageType.gif:
        return GifMessageWidget(message: message as GifMessage);
      case MessageType.audio:
        message as AudioMessage;
        return AudioMessageWidget(message: message);
      default:
        return null;
    }
  }

  bool? _nipGiver(Message currentMessage, Message? prevMessage) {
    if (prevMessage != null &&
        sameDay(currentMessage.createdAt!, prevMessage.createdAt) == true &&
        currentMessage.self == prevMessage.self) {
      return false;
    } else {
      return true;
    }
  }

  Widget _dateProvider(Message currentMessage, Message? prevMessage) {
    if (prevMessage != null &&
        sameDay(currentMessage.createdAt!, prevMessage.createdAt) == false) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 20.0),
        child: Text(
          DateFormat('dd MMM yyyy').format(
              DateTime.fromMicrosecondsSinceEpoch(currentMessage.createdAt!)),
          style: InheritedProperties.of(context).theme.dateHeaderTextStyle,
        ),
      );
    } else if (prevMessage == null) {
      return Padding(
        padding: const EdgeInsets.all(10.0),
        child: Text(
          DateFormat('dd MMM yyyy').format(
              DateTime.fromMicrosecondsSinceEpoch(currentMessage.createdAt!)),
          style: InheritedProperties.of(context).theme.dateHeaderTextStyle,
        ),
      );
    } else {
      return const SizedBox();
    }
  }

  Widget _statusProvider(Message message) {
    switch (message.status) {
      case Status.seen:
        return SvgPicture.asset(
          'assets/double_tick.svg',
          color: Colors.blue,
          height: 10,
          width: 10,
        );
      case Status.delivered:
        return SvgPicture.asset(
          'assets/double_tick.svg',
          color: Colors.grey,
          height: 10,
          width: 10,
        );
      case Status.error:
        return const Icon(Icons.error_outline, color: Colors.red, size: 18);
      case Status.sending:
        return const SizedBox(
            height: 10, width: 10, child: CupertinoActivityIndicator());
      case Status.sent:
        return SvgPicture.asset(
          'asset/single_tick.svg',
          color: Colors.grey,
          height: 10,
          width: 10,
        );
      default:
        return const SizedBox();
    }
  }

  Color _bubbleColorGiver(Message message) {
    if (["text", 'audio', 'video'].contains(message.type.name)) {
      return widget.message.self == true
          ? InheritedProperties.of(context).theme.sentMessageBubbleColor
          : InheritedProperties.of(context).theme.receivedMessageBubbleColor;
    } else if (message.type.name == 'gif') {
      return Colors.transparent;
    } else {
      return Colors.transparent;
    }
  }
}

TextMessageWidget



class TextMessageWidget extends StatefulWidget {
  const TextMessageWidget({
    Key? key,
    required this.message,
  }) : super(key: key);

  final TextMessage message;
  @override
  State<TextMessageWidget> createState() => _TextMessageWidgetState();
}

class _TextMessageWidgetState extends State<TextMessageWidget> {
  @override
  Widget build(BuildContext context) {
    final bodyTextStyle = widget.message.self == true
        ? InheritedProperties.of(context).theme.sentMessageTextStyle
        : InheritedProperties.of(context).theme.receivedMessageTextStyle;

    final boldTextStyle = widget.message.self == true
        ? InheritedProperties.of(context).theme.sentMessageBoldTextStyle
        : InheritedProperties.of(context).theme.receivedMessageBoldTextStyle;

    final codeTextStyle = widget.message.self == true
        ? InheritedProperties.of(context).theme.sentMessageBodyCodeTextStyle
        : InheritedProperties.of(context)
            .theme
            .receivedMessageBodyCodeTextStyle;

    RegExp exp = RegExp(r'(?:(?:https?|ftp):)?[\w/\-?=%.]+\.[\w/\-?=%.]+');

    String? _urlGiver(String text) {
      String? urlText;
      Iterable<RegExpMatch> matches = exp.allMatches(text);
      for (var match in matches) {
        urlText = (text.substring(match.start, match.end));
      }
      return urlText;
    }

    _urlGiver(widget.message.text);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (_urlGiver(widget.message.text) != null)
          AnyLinkPreview.builder(
              placeholderWidget: const SizedBox(
                height: 0,
                width: 0,
              ),
              errorWidget: const SizedBox(
                height: 0,
                width: 0,
              ),
              link: _urlGiver(widget.message.text)!,
              itemBuilder: (_, metadata, image) {
                return GestureDetector(
                  onTap: () {
                    if (metadata.url != null) {
                      launchUrl(Uri.parse(metadata.url!));
                    }
                  },
                  child: Card(
                    clipBehavior: Clip.hardEdge,
                    margin: EdgeInsets.zero,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        if (image != null) ...[
                          ClipRRect(
                            borderRadius: const BorderRadius.only(
                                bottomRight: Radius.circular(5),
                                bottomLeft: Radius.circular(5)),
                            child: Image(image: image),
                          ),
                        ],
                        Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                metadata.title!,
                                style: const TextStyle(
                                    fontWeight: FontWeight.bold),
                              ),
                              if (metadata.desc != null &&
                                  metadata.desc != '' &&
                                  metadata.desc !=
                                      'A new Flutter project.') ...[
                                const SizedBox(height: 10),
                                Text(metadata.desc!)
                              ]
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              }),
        const SizedBox(
          height: 5,
        ),
        Padding(
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
            child: ParsedText(
              selectable: true,
              text: widget.message.text,
              style: bodyTextStyle,
              parse: [
                MatchText(
                  onTap: (mail) async {
                    final url = Uri(scheme: 'mailto', path: mail);
                    if (await canLaunchUrl(url)) {
                      await launchUrl(url);
                    }
                  },
                  pattern:
                      r'([a-zA-Z0-9+._-][email protected][a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)',
                  style: const TextStyle(
                    decoration: TextDecoration.underline,
                  ),
                ),
                MatchText(
                  onTap: (urlText) async {
                    final protocolIdentifierRegex = exp;
                    if (!urlText.startsWith(protocolIdentifierRegex)) {
                      urlText = 'https://$urlText';
                    }
                    {
                      final url = Uri.tryParse(urlText);
                      if (url != null && await canLaunchUrl(url)) {
                        await launchUrl(
                          url,
                          mode: LaunchMode.externalApplication,
                        );
                      }
                    }
                  },
                  pattern:
                      r'((http|ftp|https):\/\/)?([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\[email protected]?^=%&/~+#-])?',
                  style: const TextStyle(
                    color: Colors.blue,
                    decoration: TextDecoration.underline,
                  ),
                ),
                MatchText(
                  pattern: PatternStyle.bold.pattern,
                  style: boldTextStyle ??
                      bodyTextStyle.merge(PatternStyle.bold.textStyle),
                  renderText:
                      ({required String str, required String pattern}) => {
                    'display': str.replaceAll(
                      PatternStyle.bold.from,
                      PatternStyle.bold.replace,
                    ),
                  },
                ),
                MatchText(
                  pattern: PatternStyle.italic.pattern,
                  style: bodyTextStyle.merge(PatternStyle.italic.textStyle),
                  renderText:
                      ({required String str, required String pattern}) => {
                    'display': str.replaceAll(
                      PatternStyle.italic.from,
                      PatternStyle.italic.replace,
                    ),
                  },
                ),
                MatchText(
                  pattern: PatternStyle.lineThrough.pattern,
                  style:
                      bodyTextStyle.merge(PatternStyle.lineThrough.textStyle),
                  renderText:
                      ({required String str, required String pattern}) => {
                    'display': str.replaceAll(
                      PatternStyle.lineThrough.from,
                      PatternStyle.lineThrough.replace,
                    ),
                  },
                ),
                MatchText(
                  pattern: PatternStyle.code.pattern,
                  style: codeTextStyle ??
                      bodyTextStyle.merge(PatternStyle.code.textStyle),
                  renderText:
                      ({required String str, required String pattern}) => {
                    'display': str.replaceAll(
                      PatternStyle.code.from,
                      PatternStyle.code.replace,
                    ),
                  },
                ),
                MatchText(
                  pattern: PatternStyle.at.pattern,
                  style: codeTextStyle ??
                      bodyTextStyle.merge(PatternStyle.at.textStyle),
                  renderText:
                      ({required String str, required String pattern}) => {
                    'display': str.replaceAll(
                      PatternStyle.code.from,
                      PatternStyle.code.replace,
                    ),
                  },
                ),
              ],
            )

            // SelectableLinkify(text: widget.message.text,onOpen: (element){
            //   _launchInBrowser(Uri.parse(element.url));
            // },),
            ),
      ],
    );
  }
}