add support for messageListView header/footer
Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
@@ -147,6 +148,8 @@ class MessageListView extends StatefulWidget {
|
|||||||
this.messageHighlightColor,
|
this.messageHighlightColor,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.showConnectionStateTile = false,
|
this.showConnectionStateTile = false,
|
||||||
|
this.headerBuilder,
|
||||||
|
this.footerBuilder,
|
||||||
this.loadingBuilder,
|
this.loadingBuilder,
|
||||||
this.emptyBuilder,
|
this.emptyBuilder,
|
||||||
this.systemMessageBuilder,
|
this.systemMessageBuilder,
|
||||||
@@ -235,6 +238,12 @@ class MessageListView extends StatefulWidget {
|
|||||||
/// Function called when messages are fetched
|
/// Function called when messages are fetched
|
||||||
final Widget Function(BuildContext, List<Message>)? messageListBuilder;
|
final Widget Function(BuildContext, List<Message>)? messageListBuilder;
|
||||||
|
|
||||||
|
///
|
||||||
|
final WidgetBuilder? headerBuilder;
|
||||||
|
|
||||||
|
///
|
||||||
|
final WidgetBuilder? footerBuilder;
|
||||||
|
|
||||||
/// Function used to build a loading widget
|
/// Function used to build a loading widget
|
||||||
final WidgetBuilder? loadingBuilder;
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
@@ -407,6 +416,12 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final itemCount = messages.length + // total messages
|
||||||
|
2 + // top + bottom loading indicator
|
||||||
|
2 + // header + footer
|
||||||
|
1 // parent message
|
||||||
|
;
|
||||||
|
|
||||||
return InfoTile(
|
return InfoTile(
|
||||||
showMessage: widget.showConnectionStateTile && showStatus,
|
showMessage: widget.showConnectionStateTile && showStatus,
|
||||||
tileAnchor: Alignment.topCenter,
|
tileAnchor: Alignment.topCenter,
|
||||||
@@ -448,16 +463,49 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
itemScrollController: _scrollController,
|
itemScrollController: _scrollController,
|
||||||
reverse: true,
|
reverse: true,
|
||||||
addAutomaticKeepAlives: false,
|
addAutomaticKeepAlives: false,
|
||||||
itemCount:
|
itemCount: itemCount,
|
||||||
messages.length + 2 + (_isThreadConversation ? 1 : 0),
|
|
||||||
|
// Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages)
|
||||||
|
// eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)|
|
||||||
|
// ParentMessage -> 7 (count-1)
|
||||||
|
// Separator(ThreadSeparator) -> 6 (count-2)
|
||||||
|
// Header -> 6 (count-2)
|
||||||
|
// Separator(Header -> 8??52) -> 5 (count-3)
|
||||||
|
// TopLoader -> 5 (count-3)
|
||||||
|
// Separator(2||8) -> 4 (count-4)
|
||||||
|
// Message -> 4 (count-4)
|
||||||
|
// Separator(2||8) -> 3 (count-5)
|
||||||
|
// Message -> 3 (count-5)
|
||||||
|
// Separator(2||8) -> 2 (count-6)
|
||||||
|
// Message -> 2 (count-6)
|
||||||
|
// Separator(2||8) -> 1 (count-7)
|
||||||
|
// BottomLoader -> 1 (count-7)
|
||||||
|
// Separator(Footer -> 8??30) -> 0 (count-8)
|
||||||
|
// Footer -> 0 (count-8)
|
||||||
|
|
||||||
separatorBuilder: (context, i) {
|
separatorBuilder: (context, i) {
|
||||||
if (i == messages.length) return const Offstage();
|
if (i == itemCount - 2) {
|
||||||
if (i == 0) return const SizedBox(height: 30);
|
if (widget.parentMessage == null) {
|
||||||
if (i == messages.length + 1) {
|
return const Offstage();
|
||||||
|
}
|
||||||
return _buildThreadSeparator();
|
return _buildThreadSeparator();
|
||||||
}
|
}
|
||||||
|
if (i == itemCount - 3) {
|
||||||
|
if (widget.headerBuilder == null) {
|
||||||
|
return const SizedBox(height: 52);
|
||||||
|
}
|
||||||
|
return const SizedBox(height: 8);
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
if (widget.footerBuilder == null) {
|
||||||
|
return const SizedBox(height: 30);
|
||||||
|
}
|
||||||
|
return const SizedBox(height: 8);
|
||||||
|
}
|
||||||
|
|
||||||
final message = messages[i];
|
if (i == 1 || i == itemCount - 4) return const Offstage();
|
||||||
|
|
||||||
|
final message = messages[i - 2];
|
||||||
final nextMessage = messages[i - 1];
|
final nextMessage = messages[i - 1];
|
||||||
if (!Jiffy(message.createdAt.toLocal()).isSame(
|
if (!Jiffy(message.createdAt.toLocal()).isSame(
|
||||||
nextMessage.createdAt.toLocal(),
|
nextMessage.createdAt.toLocal(),
|
||||||
@@ -494,40 +542,51 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
return const SizedBox(height: 2);
|
return const SizedBox(height: 2);
|
||||||
},
|
},
|
||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
if (i == messages.length + 2) {
|
if (i == itemCount - 1) {
|
||||||
if (widget.parentMessageBuilder != null) {
|
if (widget.parentMessage == null) return const Offstage();
|
||||||
return widget.parentMessageBuilder!(
|
return widget.parentMessageBuilder?.call(
|
||||||
context,
|
context,
|
||||||
widget.parentMessage,
|
widget.parentMessage,
|
||||||
);
|
) ??
|
||||||
} else {
|
buildParentMessage(widget.parentMessage!);
|
||||||
return buildParentMessage(widget.parentMessage!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (i == messages.length + 1) {
|
|
||||||
|
if (i == itemCount - 2) {
|
||||||
|
return widget.headerBuilder?.call(context) ??
|
||||||
|
const Offstage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == itemCount - 3) {
|
||||||
return _buildLoadingIndicator(
|
return _buildLoadingIndicator(
|
||||||
streamChannel!,
|
streamChannel!,
|
||||||
QueryDirection.top,
|
QueryDirection.top,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
|
||||||
|
if (i == 1) {
|
||||||
return _buildLoadingIndicator(
|
return _buildLoadingIndicator(
|
||||||
streamChannel!,
|
streamChannel!,
|
||||||
QueryDirection.bottom,
|
QueryDirection.bottom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final message = messages[i - 1];
|
|
||||||
|
if (i == 0) {
|
||||||
|
return widget.footerBuilder?.call(context) ??
|
||||||
|
const Offstage();
|
||||||
|
}
|
||||||
|
|
||||||
|
final message = messages[i - 2];
|
||||||
|
|
||||||
Widget messageWidget;
|
Widget messageWidget;
|
||||||
|
|
||||||
if (i == 1) {
|
if (i == 2) {
|
||||||
messageWidget = _buildBottomMessage(
|
messageWidget = _buildBottomMessage(
|
||||||
context,
|
context,
|
||||||
message,
|
message,
|
||||||
messages,
|
messages,
|
||||||
streamChannel!,
|
streamChannel!,
|
||||||
);
|
);
|
||||||
} else if (i == messages.length - 1) {
|
} else if (i == messages.length - 4) {
|
||||||
messageWidget = _buildTopMessage(
|
messageWidget = _buildTopMessage(
|
||||||
context,
|
context,
|
||||||
message,
|
message,
|
||||||
@@ -578,7 +637,6 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Text(
|
child: Text(
|
||||||
// ignore: lines_longer_than_80_chars
|
|
||||||
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: _streamTheme.channelTheme.channelHeaderTheme.subtitle,
|
style: _streamTheme.channelTheme.channelHeaderTheme.subtitle,
|
||||||
@@ -1238,15 +1296,7 @@ class _LoadingIndicator extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (context, data) {
|
builder: (context, data) {
|
||||||
if (!data) {
|
if (!data) return const Offstage();
|
||||||
if (!isThreadConversation && direction == QueryDirection.top) {
|
|
||||||
return const SizedBox(
|
|
||||||
height: 52,
|
|
||||||
width: double.infinity,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return const Offstage();
|
|
||||||
}
|
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
|
|||||||
Reference in New Issue
Block a user