Closed wwwdata closed 2 years ago
Hi @wwwdata, I'm not sure to understand, if the header is rendered after its child, it will not be sticky, isn't it? 😕 Can you attach a drawing or something like that to help me to understand?
Imagine a chat app like WhatsApp. You always have the days as headlines and you start at the bottom of the list and scroll up. So I always want to have the headline at the top of the sliver instead of the bottom, which is the case when it is just reversed.
I started looking at the code and trying to make it work on my own, I will maybe look tomorrow or monday again and see if I can make it work on my own. But if its super easy and you get it done in like a couple of minutes I would appreciate your help!
Ok I think I got it. I started something on a feature branch: https://github.com/letsar/flutter_sticky_header/tree/feature/reverse. Can you test it and tell me if I go in the right direction?
@letsar yes you are heading in the right direction! was also doing these exact changes when I started. Now the fadeout/move-away-animation of the header also needs to happen at the top and not bottom and that's it I guess.
This would be really useful
@letsar did you plan to finish this feature?
Hi,
I am implementing a chat widget and using your library to separate messages by date, you can see the same behavior on Whatsapp and Telegram.
I am building several slivers
that each contain messages on that date and a header which is basically a text showing the relevant date. I pass the slivers
to a CustomScrollView
with reverse
property set to true.
Everything works fine except that the headers are pinned to the bottom of the screen (I need them to stick to the top). How can I fix this? Any pointers would be appreciated.
.
.
.
return CustomScrollView(
slivers: _buildListItem(context, sectionIndex),
reverse: true,
controller: listScrollController,
);
.
.
.
List<Widget> _buildListItem(
BuildContext context,
List<ChatSection> chatSections,
) {
List<Widget> slivers = List();
chatSections.forEach((chatSection) {
slivers.add(_buildChatSliver(
context, chatSection.messages, chatSection.datetime));
});
return slivers;
}
SliverStickyHeaderBuilder _buildChatSliver(BuildContext context,
List<ChatMessage> chatMessages, DateTime dateTimeHeader) {
return SliverStickyHeaderBuilder(
overlapsContent: false,
builder: (context, state) {
DateTime now = DateTime.now();
String headerText = now.year != dateTimeHeader.year
? DateFormat("YYYY MMM dd").format(dateTimeHeader)
: DateFormat("MMM dd").format(dateTimeHeader);
return Container(
padding: EdgeInsets.fromLTRB(5, 0, 15, 5),
child: Center(
child: Container(
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
decoration: BoxDecoration(
color: Color.fromARGB(200, 150, 150, 150),
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Text(
headerText,
style: TextStyle(
color: Colors.white70,
fontSize: 12.0,
fontStyle: FontStyle.italic),
),
),
),
);
},
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, i) => _buildItem(chatMessages[i]),
childCount: chatMessages.length)),
);
}
Hi,
I am implementing a chat widget and using your library to separate messages by date, you can see the same behavior on Whatsapp and Telegram.
I am building several
slivers
that each contain messages on that date and a header which is basically a text showing the relevant date. I pass theslivers
to aCustomScrollView
withreverse
property set to true.Everything works fine except that the headers are pinned to the bottom of the screen (I need them to stick to the top). How can I fix this? Any pointers would be appreciated.
. . . return CustomScrollView( slivers: _buildListItem(context, sectionIndex), reverse: true, controller: listScrollController, ); . . .
List<Widget> _buildListItem( BuildContext context, List<ChatSection> chatSections, ) { List<Widget> slivers = List(); chatSections.forEach((chatSection) { slivers.add(_buildChatSliver( context, chatSection.messages, chatSection.datetime)); }); return slivers; }
SliverStickyHeaderBuilder _buildChatSliver(BuildContext context, List<ChatMessage> chatMessages, DateTime dateTimeHeader) { return SliverStickyHeaderBuilder( overlapsContent: false, builder: (context, state) { DateTime now = DateTime.now(); String headerText = now.year != dateTimeHeader.year ? DateFormat("YYYY MMM dd").format(dateTimeHeader) : DateFormat("MMM dd").format(dateTimeHeader); return Container( padding: EdgeInsets.fromLTRB(5, 0, 15, 5), child: Center( child: Container( padding: EdgeInsets.fromLTRB(5, 5, 5, 5), decoration: BoxDecoration( color: Color.fromARGB(200, 150, 150, 150), borderRadius: BorderRadius.all(Radius.circular(5))), child: Text( headerText, style: TextStyle( color: Colors.white70, fontSize: 12.0, fontStyle: FontStyle.italic), ), ), ), ); }, sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, i) => _buildItem(chatMessages[i]), childCount: chatMessages.length)), ); }
try using different library (https://pub.dev/packages/sticky_headers).
return StickyHeader(
header: _sectionHeader(dateText),
content: Column(
children: <Widget>[
if (isLast) _topWidget(),
for (var i in list.reversed)
ListTile(
title: ChatMessageWidget(
message: i,
senderUuid: widget.senderUuid,
key: Key('chatmsg$key$i'),
onLongPress: _onMsgPressed,
onTap: _onMsgPressed,
)
)
]
)
);
and then in build function:
return CustomScrollView(
key: Key('MessagesList'),
controller: _scrollController,
slivers: <Widget>[
SliverList(delegate: SliverChildBuilderDelegate((context, index) {
if (index == 0) {
return _bottomView();
} else {
final key = keys[index - 1];
return _buildSection(context, key, mapped[key], index == keys.length);
}
},
childCount: keys.length + 1)),
],
reverse: true,
);
works well for me!
Hi @letsar , any plans to pull this off? Nice plugin though. @wwwdata did you get a workaround you can share?
@pavelmeerkat Thanks for the insight. I was actually using that plugin but I was looking for something that would help me a implement scroll-to-item in the grouped list. I need to be able to scroll to individual items on a certain click. I am not yet a master in flutter but from your code sample above it looks like your use of keys here gives you more control on the position of the list item. The other thing I don't like about that plugin is that the sticky header is wobbly on scroll - doesn't look very cool.
@pkitatta no sorry I didn't look any further into this. For the App that I developed back then we found another good UI solution that worked out. And then I didn't look into this issue anymore.
@pkitatta no sorry I didn't look any further into this. For the App that I developed back then we found another good UI solution that worked out. And then I didn't look into this issue anymore.
@wwwdata thanks, I will probably build my own solution. I have been chatting a maker of a similar plugin, and he discouraged me from using the current sticky header plugins available (including his) for my app for performance reasons. I am building a chat app and need to group messages by date.
So, would gladly welcome any insight you can give on how you went about your solution - if you didn't patent it... :wink:
I'm really need this feature. Trying to write myself.
Is there any progress on this feature? Would be really useful
I've attempted to get it to work, currently I'm finding that all my attempts are pretty much the equivalent of just adding
axisDirection = flipAxisDirection(axisDirection)
which works, except that the header over scrolls by what appears to be the header height, and I can't seem to be able to fix this. I feel like its to do with it incorrectly pinning, but I don't really know
@pkitatta no sorry I didn't look any further into this. For the App that I developed back then we found another good UI solution that worked out. And then I didn't look into this issue anymore.
@wwwdata thanks, I will probably build my own solution. I have been chatting a maker of a similar plugin, and he discouraged me from using the current sticky header plugins available (including his) for my app for performance reasons. I am building a chat app and need to group messages by date.
So, would gladly welcome any insight you can give on how you went about your solution - if you didn't patent it... 😉
i solved this problem with visibility_detector package and notificationListener like this
Stack(
fit: StackFit.expand,
children: [
NotificationListener(
onNotification: (v) {
if (v
is ScrollStartNotification) {
if (!chat.showHeader) {
chat.showHeader = true;
setState(() {});
}
} else if (v
is UserScrollNotification) {
chat.handleShowHeader(() {
setState(() {});
});
}
return true;
},
child: CustomScrollView(
reverse: true,
cacheExtent: 1000,
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return VisibilityDetector(
key: ValueKey(chat
.messagesList[index]
.id),
onVisibilityChanged: (v) {
if (v.visibleFraction >
0.5) {
final bool _changed =
chat.changeDateTime(chat
.messagesList[
index]
.time);
if (_changed) {
setState(() {});
}
}
},
child: Container(
child: Message(
chat.messagesList[
index])),
);
},
addAutomaticKeepAlives:
false,
addRepaintBoundaries:
false,
childCount: chat
.messagesList
.length))
],
),
),
Align(
alignment: Alignment.topCenter,
child: AnimatedSwitcher(
duration: const Duration(
milliseconds: 200),
switchOutCurve: Curves.linear,
switchInCurve:
Curves.easeOutBack,
child: !chat.showHeader ||
chat.nowMessageView ==
null
? null
: Container(
width: 90,
alignment:
Alignment.topCenter,
child: Card(
elevation: 2,
color: Colors.black26,
child: Padding(
padding:
const EdgeInsets
.symmetric(
horizontal:
10,
vertical:
2.5),
child: Text(
chat.nowMessageView!
.differenceToWorld,
style:
overMessageStyle,
textAlign:
TextAlign
.center,
),
),
),
),
),
)
],
),
Methods
handleShowHeader(Function callBack) {
timer?.cancel();
if (!showHeader) return;
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
showHeader = false;
callBack.call();
});
}
DateTime? nowMessageView;
bool changeDateTime(DateTime newTime) {
if (nowMessageView?.day == newTime.day) {
return false;
}
nowMessageView = newTime;
return true;
}
I forked this repo and got reverse working for my use case if that helps anyone else. I haven't had a chance to test it for the other uses: https://github.com/charlesRmajor/flutter_sticky_header
Sent a PR with the updates to the feature/reverse branch if you'd like to have it, @letsar
@charlesRmajor
I forked this repo and got reverse working for my use case if that helps anyone else. I haven't had a chance to test it for the other uses: https://github.com/charlesRmajor/flutter_sticky_header
unfortunately this doesn't work for me.
all rendering areas have moved up, although the rendering itself looks normal. because of this, the optimizer breaks, hiding widgets that have gone beyond the screen. that is, the drawing area has disappeared, but their image is still there and flutter simply turns it off. also because of this, working out pressing the widget does not work.
Yeah ... I only got it working for my particular situation. I added a reverse2 example that was working to my fork. If it helps, here's how I'm using it currently:
CustomScrollView(
shrinkWrap: true,
reverse: true,
physics: ClampingScrollPhysics(),
slivers: [
SliverStickyHeader.builder(
reverse: true,
builder: (context, constraints) {...},
sliver:
SliverList(...
Yes, that's what I do. the SliverStickyHeaderState contains incorrect information, and the widget click area inside the SliverList still slides up if the header height is not zero.
Yeah ... I only got it working for my particular situation. I added a reverse2 example that was working to my fork. If it helps, here's how I'm using it currently:
CustomScrollView( shrinkWrap: true, reverse: true, physics: ClampingScrollPhysics(), slivers: [ SliverStickyHeader.builder( reverse: true, builder: (context, constraints) {...}, sliver: SliverList(...
@charlesRmajor your fork works for me, but constraints
from the builder SliverStickyHeaderWidgetBuilder
has wrong a value for isPinned
in the SliverStickyHeaderState
. Above the CustomScrollView
center Key isPinned is alway false
even when it's pinned.
I also found another package that support reverse parameter (https://pub.dev/packages/slyverin), but I need to know which Widget is pinned. I tried with VisibilityDetector
but it's ugly and pretty slow.
When using a
CustomScrollView
with thereverse
property set totrue
it would make sense to reverse theSliverStickyHeader
as well. In a reversed list, the header currently is at the bottom of the list. I suggest to add areverse
option toSliverStickyHeader
as well, so it renders after the other sliver widgets, instead of before.