Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ChatsList don't like listview behavior #25

Closed
lucasjinreal opened this issue May 14, 2021 · 5 comments
Closed

ChatsList don't like listview behavior #25

lucasjinreal opened this issue May 14, 2021 · 5 comments
Labels
invalid This doesn't seem right

Comments

@lucasjinreal
Copy link

lucasjinreal commented May 14, 2021

this is normal ListView:

image

this is ChatsList
image

Hi, I am not here report a bug or problem of this package. On the opposite using this lib I managed to build a useful chat App.

But I found a slightly bias behavior might not notice by you. As you can see, when the List scroll to the bottom, ChatsList can not handle with the bottomBar, while ListView can handle, on the other words, ListView the last item will not blind by the navigationBar, this can be notice easily if your bottomBar blurred.

Is there a way to replace ChatsList with ListView while also preserve all other features? I really don't know why they behavior such bias

@jonasN5
Copy link
Owner

jonasN5 commented May 14, 2021

Hi, I am not here report a bug or problem of this package. On the opposite using this lib I managed to build a useful chat App.

There is nothing wrong with reporting a bug. On the contrary, reporting a bug leads to a better library!

Using the example app, replace this to test your problem (juste copy paste the whole thing into the files):

chat_viewmodel.dart

import 'package:example/models/chat.dart';
import 'package:example/models/chat_message.dart';
import 'package:example/models/chat_user.dart';
import 'package:example/utils/app_const.dart';
import 'package:chat_ui_kit/chat_ui_kit.dart';

class ChatViewModel {
  ChatViewModel() {
    chatMessages = {
      "test_chat_id_0": [
        ChatMessage(
            author: chatUsers[1],
            text: "you? :)",
            creationTimestamp: DateTime.now().millisecondsSinceEpoch - 5000),
        ChatMessage(
            author: chatUsers[1],
            text: "not much",
            creationTimestamp: DateTime.now().millisecondsSinceEpoch - 3000),
        ChatMessage(
            author: localUser,
            text: "sup",
            creationTimestamp: DateTime.now().millisecondsSinceEpoch),
      ],
      "test_chat_id_1": [
        ChatMessage(
            type: ChatMessageType.image,
            author: localUser,
            attachment: 'assets/other/paris_e_tower.jpg',
            creationTimestamp: DateTime(2020, 12, 29).millisecondsSinceEpoch)
      ],
      "test_chat_id_2": [
        ChatMessage(
            author: localUser,
            text: "Xmas was awesome!",
            creationTimestamp: DateTime(2020, 12, 28).millisecondsSinceEpoch)
      ],
      "test_chat_id_3": [
        ChatMessage(
            author: localUser,
            text: "Let's create a group",
            creationTimestamp: DateTime(2020, 12, 27).millisecondsSinceEpoch)
      ],
      "test_chat_id_4": [
        ChatMessage(
            author: localUser,
            type: ChatMessageType.image,
            attachment: 'assets/other/paris_e_tower.jpg',
            creationTimestamp:
                DateTime(2020, 12, 26, 10, 5, 4).millisecondsSinceEpoch),
        ChatMessage(
            author: localUser,
            text: "Check out where I went during my last holidays",
            creationTimestamp:
                DateTime(2020, 12, 26, 10, 5, 2).millisecondsSinceEpoch),
        ChatMessage(
            author: localUser,
            text: "Me three",
            creationTimestamp:
                DateTime(2020, 12, 22, 8, 6, 2).millisecondsSinceEpoch),
        ChatMessage(
            author: chatUsers[5],
            text: "Me too",
            creationTimestamp:
                DateTime(2020, 12, 22, 8, 6).millisecondsSinceEpoch),
        ChatMessage(
            author: chatUsers[1],
            text: "I like it",
            creationTimestamp:
                DateTime(2020, 12, 22, 8, 5).millisecondsSinceEpoch),
        ChatMessage(
            author: chatUsers[3],
            text: "Do you guys like the new title?",
            creationTimestamp:
                DateTime(2020, 12, 22, 8, 1).millisecondsSinceEpoch),
        ChatMessage(
            author: chatUsers[3],
            type: ChatMessageType.renameChat,
            text: "Paradise",
            creationTimestamp:
                DateTime(2020, 12, 22, 8).millisecondsSinceEpoch),
      ],
      "test_chat_id_5": [
        ChatMessage(
            author: chatUsers[5],
            text: "What are you doing on new year's eve?",
            creationTimestamp: DateTime(2020, 12, 23).millisecondsSinceEpoch)
      ],
    };
  }

  List<ChatUser> chatUsers = [
    ChatUser(
        id: AppConstants.LOCAL_USER_ID,
        username: "MrJ",
        fullname: "Jonas S.",
        avatarURL: 'assets/avatars/local_user_avatar.png'),
    ChatUser(
        id: "local_user_id_0",
        username: "Kiraf",
        fullname: "Kira Fowler",
        avatarURL: 'assets/avatars/woman0.jpg'),
    ChatUser(
        id: "local_user_id_1",
        username: "Eleanor",
        avatarURL: 'assets/avatars/woman1.jpg'),
    ChatUser(
        id: "local_user_id_2",
        username: "crysty",
        fullname: "Crystal Leblanc",
        avatarURL: 'assets/avatars/woman2.jpg'),
    ChatUser(
        id: "local_user_id_3",
        username: "Ewan",
        fullname: "Ewan Kemp",
        avatarURL: 'assets/avatars/man0.jpg'),
    ChatUser(
        id: "local_user_id_4",
        username: "Wilfredh",
        fullname: "Wilfred Hanna",
        avatarURL: 'assets/avatars/man1.jpg'),
    ChatUser(
        id: "local_user_id_5",
        username: "tyler.henry",
        fullname: "Tyler Henry",
        avatarURL: 'assets/avatars/man2.jpg'),
  ];

  ChatUser get localUser => chatUsers[0];

  Map<String, List<ChatMessage>> chatMessages;

  ChatsListController controller;

  List<ChatWithMembers> generateExampleChats() {
    final List<ChatWithMembers> _chats = [];
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_0"].first,
        chat: Chat(id: "test_chat_id_0", ownerId: localUser.id, unreadCount: 2),
        members: [chatUsers[0], chatUsers[1]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_1"].first,
        chat: Chat(id: "test_chat_id_1", ownerId: localUser.id),
        members: [chatUsers[0], chatUsers[4]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_2"].first,
        chat: Chat(id: "test_chat_id_2", ownerId: localUser.id),
        members: [chatUsers[0], chatUsers[5]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_3"].first,
        chat: Chat(id: "test_chat_id_3", ownerId: localUser.id),
        members: [chatUsers[0], chatUsers[1], chatUsers[3]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_4"].first,
        chat: Chat(
            id: "test_chat_id_4", ownerId: chatUsers[3].id, name: "Paradise"),
        members: [chatUsers[0], chatUsers[1], chatUsers[3], chatUsers[5]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_5"].first,
        chat: Chat(id: "test_chat_id_5", ownerId: chatUsers[2].id),
        members: [chatUsers[0], chatUsers[2]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_5"].first,
        chat: Chat(id: "test_chat_id_6", ownerId: chatUsers[2].id),
        members: [chatUsers[0], chatUsers[2]]));
    _chats.add(ChatWithMembers(
        lastMessage: chatMessages["test_chat_id_5"].first,
        chat: Chat(id: "test_chat_id_7", ownerId: chatUsers[2].id),
        members: [chatUsers[0], chatUsers[2]]));
    return _chats;
  }
}

chats_screen.dart

import 'package:chat_ui_kit/chat_ui_kit.dart';
import 'package:example/models/chat.dart';
import 'package:example/models/chat_message.dart';
import 'package:example/ui/chat_viewmodel.dart';
import 'package:flutter/material.dart';

import '../utils/date_formatter.dart';
import '../utils/app_colors.dart';
import '../ui/chat_screen.dart';

class ChatsScreen extends StatefulWidget {
  @override
  _ChatsScreenSate createState() => _ChatsScreenSate();
}

class _ChatsScreenSate extends State<ChatsScreen> {
  final ChatViewModel _model = ChatViewModel();

  @override
  void initState() {
    _model.controller = ChatsListController();
    _model.controller.addAll(_model.generateExampleChats());
    super.initState();
  }

  /// Called from [NotificationListener] when the user scrolls
  void handleScrollEvent(ScrollNotification scroll) {
    if (scroll.metrics.pixels == scroll.metrics.maxScrollExtent) {
      //_model.getMoreChats();
    }
  }

  /// Called when the user pressed an item (a chat)
  void onItemPressed(ChatWithMembers chat) {
    //navigate to the chat
    Navigator.of(context).push(MaterialPageRoute(
        builder: (context) => ChatScreen(ChatScreenArgs(chat: chat))));
    //reset unread count
    if (chat.isUnread) {
      chat.chat.unreadCount = 0;
    }
  }

  /// Called when the user long pressed an item (a chat)
  void onItemLongPressed(ChatWithMembers chat) {
    showDialog(
        context: context,
        builder: (_) {
          return AlertDialog(
              content: Text(
                  "This chat and any related message will be deleted permanently."),
              actions: [
                TextButton(
                    onPressed: () {
                      Navigator.of(context).pop();
                      //delete in DB, from the current list in memory and update UI
                      _model.controller.removeItem(chat);
                    },
                    child: Text("ok")),
                Padding(
                  padding: EdgeInsets.only(left: 16),
                  child: TextButton(
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                      child: Text("cancel")),
                ),
              ]);
        });
  }

  /// Build the last message depending on how many members the Chat has
  /// and on the message type [ChatMessage.type]
  Widget _buildLastMessage(BuildContext context, int index, ChatBase item) {
    final _chat = item as ChatWithMembers;
    //display avatar only if not a 1 to 1 conversation
    final bool displayAvatar = item.members.length > 2;
    //display an icon if there's an attachment
    Widget attachmentIcon;
    if (_chat.lastMessage.hasAttachment) {
      final _type = _chat.lastMessage.type;
      final iconColor = AppColors.chatsAttachmentIconColor(context);
      if (_type == ChatMessageType.audio) {
        attachmentIcon = Icon(Icons.keyboard_voice, color: iconColor);
      } else if (_type == ChatMessageType.video) {
        attachmentIcon = Icon(Icons.videocam, color: iconColor);
      } else if (_type == ChatMessageType.image) {
        attachmentIcon = Icon(Icons.image, color: iconColor);
      }
    }

    //get the message label
    String messageText = _chat.lastMessage.messageText(_model.localUser.id);

    return Padding(
        padding: EdgeInsets.only(top: 8),
        child: Row(children: [
          if (displayAvatar)
            Padding(
                padding: EdgeInsets.only(right: 8),
                child: ClipOval(
                    child: Image.asset(item.lastMessage.author.avatar,
                        width: 24, height: 24, fit: BoxFit.cover))),
          if (attachmentIcon != null)
            Padding(padding: EdgeInsets.only(right: 8), child: attachmentIcon),
          Expanded(
              child: Text(
            messageText,
            overflow: TextOverflow.ellipsis,
          ))
        ]));
  }

  Widget _buildTileWrapper(
      BuildContext context, int index, ChatBase item, Widget child) {
    return InkWell(
        onTap: () => onItemPressed(item),
        onLongPress: () => onItemLongPressed(item),
        child: Column(children: [
          Padding(padding: EdgeInsets.only(right: 16), child: child),
          Divider(
            height: 1.5,
            thickness: 1.5,
            color: AppColors.chatsSeparatorLineColor(context),
            //56 default GroupAvatar size + 32 padding
            indent: 56.0 + 32.0,
            endIndent: 16.0,
          )
        ]));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Chats"),
          automaticallyImplyLeading: false,
        ),
        bottomNavigationBar: BottomNavigationBar(
            //fixedColor: Colors.transparent,
            //unselectedItemColor: Colors.transparent,
            //backgroundColor: Colors.transparent,
            items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.search), label: "Search"),
          BottomNavigationBarItem(
              icon: Icon(Icons.add_circle_outline), label: "Publish"),
          BottomNavigationBarItem(
              icon: Icon(Icons.notifications), label: "Notifications"),
        ]),
        body: ChatsList(
            controller: _model.controller,
            appUserId: _model.localUser.id,
            scrollHandler: handleScrollEvent,
            groupAvatarStyle: GroupAvatarStyle(
                withSeparator: true, separatorColor: Colors.white),
            builders: ChatsListTileBuilders(
                groupAvatarBuilder:
                    (context, imageIndex, itemIndex, size, item) {
                  final chat = item as ChatWithMembers;
                  return Image.asset(chat.membersWithoutSelf[imageIndex].avatar,
                      width: size.width,
                      height: size.height,
                      fit: BoxFit.cover);
                },
                lastMessageBuilder: _buildLastMessage,
                wrapper: _buildTileWrapper,
                dateBuilder: (context, date) => Padding(
                    padding: EdgeInsets.only(left: 16),
                    child: Text(DateFormatter.getVerboseDateTimeRepresentation(
                        context, date)))),
            areItemsTheSame: (ChatBase oldItem, ChatBase newItem) =>
                oldItem.id == newItem.id));
  }

  @override
  void dispose() {
    _model.controller.dispose();
    super.dispose();
  }
}

Here is the result:

image

As you can see, the last item is displayed fully above the bottomNavBar, as expected. It seems to me your problem stems from whatever you use to do the blurring effect on your bottomNavBar.
Now, here's what I'm thinking:
-when would you want to use a blurring effect? When you want to see the content behind the blurring layer, which therefore has to OVERLAP said content
-thus, it is actually expected to see the last chat item behind you bottomBar, in a stacked way
Which means there would be nothing wrong in your pictures.

@lucasjinreal
Copy link
Author

@themadmrj thanks for your detailed analysis. I shall test your code first.

-when would you want to use a blurring effect? When you want to see the content behind the blurring layer, which therefore has to OVERLAP said content

I am using a custom bottom navbar, which from this lib: https://github.com/rickywen911/custom_bubble_navigation_bar
I am using it because of it's effect seems nice, and with some default animations original doesn't provided. Hope this info provides more detail. However, the original ListView can handle with this navbar, (the last item will not blinded).

-thus, it is actually expected to see the last chat item behind you bottomBar, in a stacked way
Which means there would be nothing wrong in your pictures.

It's true, for blurred effect, needs stacked way so that content can viewed on the behind of navbar. But problems is, the last item, can not jump above the navbar and stucked at the very bottom.

@lucasjinreal
Copy link
Author

lucasjinreal commented May 16, 2021

@themadmrj Oh, I just realise the difference:

THe exmple provided, it doesn't have any content behind navbar. so it shouldn't have my problem. My problem mainly is content is fullscreen, and the last item can not jump above navbar.

I paste some code hope it's not too messy:

Scaffold(
            extendBody: true,
            // backgroundColor: Colors.red,
            // backgroundColor: Colors.transparent,
            backgroundColor: Theme.of(context).dialogBackgroundColor,
            appBar: PreferredSize(
                preferredSize: const Size(double.infinity, kToolbarHeight),
                child: getNavBar(_homeBloc, _chatBloc)),
            bottomNavigationBar: CustomNavigationBar(
              // iconSize: 30.0,
              selectedColor: COLOR_PRIMARY_APP,
              strokeColor: COLOR_PRIMARY_APP.withAlpha(70),
              bubbleCurve: Curves.bounceInOut,
              unSelectedColor: Colors.grey[600],
              backgroundColor: Theme.of(context).dialogBackgroundColor,
              borderRadius: Radius.circular(16.0),
              blurEffect: true,
              opacity: 0.7,
              items: [
                CustomNavigationBarItem(
                  icon: Icon(FluentIcons.chat_24_filled),
                ),
                CustomNavigationBarItem(
                  icon: Icon(
                    FluentIcons.contact_card_24_filled,
                  ),
                ),
                CustomNavigationBarItem(
                  icon: Icon(
                    FluentIcons.lightbulb_24_filled,
                  ),
                ),
              ],
              currentIndex: _currentIndex,
              onTap: (index) {
                HapticFeedback.lightImpact();
                _homeBloc.setIndex(index);
                print('crtIndex;;;;; $index');
                setState(() {
                  _currentIndex = index;
                });
              },
              isFloating: true,
            ),
            // bottomNavigationBar: BottomNavigationBar(
            //   // iconSize: 30.0,
            //   // selectedColor: COLOR_PRIMARY_APP,
            //   // strokeColor: Colors.red,
            //   // unSelectedColor: Colors.grey[600],
            //   backgroundColor: Colors.white,
            //   // borderRadius: Radius.circular(16.0),
            //   // blurEffect: true,
            //   // opacity: 0.6,
            //   items: [
            //     BottomNavigationBarItem(
            //         icon: Icon(FluentIcons.chat_24_filled), label: "aaa"),
            //     BottomNavigationBarItem(
            //         icon: Icon(
            //           FluentIcons.contact_card_24_filled,
            //         ),
            //         label: "aaa"),
            //     BottomNavigationBarItem(
            //         icon: Icon(
            //           FluentIcons.lightbulb_24_filled,
            //         ),
            //         label: "aaa"),
            //   ],
            //   currentIndex: _currentIndex,
            //   onTap: (index) {
            //     setState(() {
            //       _currentIndex = index;
            // });
            // },
            // isFloating: true,
            // ),
            body: contactsPage);

Here is using extendBody: true, <- I think this is the key, I made content to fullscreen

And contactsPage with:

@override
  Widget build(BuildContext context) {
    ContactBloc contactBloc = new ContactBloc();

    HomeBloc homeBloc = BlocProvider.of(context);
    homeBloc.setIndex(1);

    return StreamBuilder(
        stream: contactBloc.contactsStream,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            List<Contact> cs = snapshot.data;
            return ListView.builder(
                itemCount: cs.length,
                itemBuilder: (context, i) {
                  return Column(children: <Widget>[
                    _buildContactRow(cs[i]),
                    Divider(
                        indent: 32, height: 1, color: Colors.grey.withAlpha(20))
                  ]);
                });
          } else {
            return SpinKitFadingCircle(
              color: COLOR_PRIMARY_APP,
            );
          }
        });
  }

contactsPage only is a ListView. It can solve with last item. But same main page scafffold, using ChatsLists, there will blind the last item.

@jonasN5
Copy link
Owner

jonasN5 commented May 29, 2021

Indeed, it is as you say, the last item will not move above the navbar. Sadly I cannot help you with this specific usecase. As you can see, chat_ui_kit uses ImplicitlyAnimatedList, which is an external library. Therefore, your problem has to do with ImplicitlyAnimatedList and not really chat_ui_kit. I encourage you to open a bug report on their library (which you have not done yet, as far as I could check). What I usually do in such specific cases, is debug it myself by changing the libs until I found the problem and then submit a PR.
Closing for now, as it is not a bug originating from chat_ui_kit.

@jonasN5 jonasN5 closed this as completed May 29, 2021
@lucasjinreal
Copy link
Author

thanks, I have posted an issue to that lib:

https://github.com/bnxm/implicitly_animated_reorderable_list/issues/77

@jonasN5 jonasN5 added the invalid This doesn't seem right label May 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

2 participants