Merge pull request #268 from GetStream/feature/expose-builders
Feature/expose builders
This commit is contained in:
@@ -67,8 +67,6 @@ class ChannelListView extends StatefulWidget {
|
||||
this.channelWidget,
|
||||
this.channelPreviewBuilder,
|
||||
this.separatorBuilder,
|
||||
this.errorBuilder,
|
||||
this.emptyBuilder,
|
||||
this.onImageTap,
|
||||
this.onStartChatPressed,
|
||||
this.swipeToAction = false,
|
||||
@@ -77,17 +75,15 @@ class ChannelListView extends StatefulWidget {
|
||||
this.padding,
|
||||
this.selectedChannels = const [],
|
||||
this.onViewInfoTap,
|
||||
this.errorBuilder,
|
||||
this.emptyBuilder,
|
||||
this.loadingBuilder,
|
||||
this.listBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
final Widget Function(Error error) errorBuilder;
|
||||
|
||||
/// If true a default swipe to action behaviour will be added to this widget
|
||||
final bool swipeToAction;
|
||||
|
||||
/// The builder used when the channel list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
/// The query filters to use.
|
||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||
/// You can also filter other built-in channel fields.
|
||||
@@ -147,6 +143,18 @@ class ChannelListView extends StatefulWidget {
|
||||
|
||||
final ViewInfoCallback onViewInfoTap;
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
final ErrorBuilder errorBuilder;
|
||||
|
||||
/// The builder that will be used in case of loading
|
||||
final WidgetBuilder loadingBuilder;
|
||||
|
||||
/// The builder which is used when list of channels loads
|
||||
final Function(BuildContext, List<Channel>) listBuilder;
|
||||
|
||||
/// The builder used when the channel list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
@override
|
||||
_ChannelListViewState createState() => _ChannelListViewState();
|
||||
}
|
||||
@@ -161,18 +169,22 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
Widget build(BuildContext context) {
|
||||
var child = ChannelListCore(
|
||||
channelListController: _channelListController,
|
||||
listBuilder: (context, list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
emptyBuilder: (BuildContext context) {
|
||||
return _buildEmptyWidget();
|
||||
},
|
||||
errorBuilder: (BuildContext context, dynamic error) {
|
||||
return _buildErrorWidget(context);
|
||||
},
|
||||
loadingBuilder: (BuildContext context) {
|
||||
return _buildLoadingWidget();
|
||||
},
|
||||
listBuilder: widget.listBuilder ??
|
||||
(context, list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
emptyBuilder: widget.emptyBuilder ??
|
||||
(BuildContext context) {
|
||||
return _buildEmptyWidget();
|
||||
},
|
||||
errorBuilder: widget.errorBuilder ??
|
||||
(BuildContext context, dynamic error) {
|
||||
return _buildErrorWidget(context);
|
||||
},
|
||||
loadingBuilder: widget.loadingBuilder ??
|
||||
(BuildContext context) {
|
||||
return _buildLoadingWidget();
|
||||
},
|
||||
pagination: widget.pagination,
|
||||
options: widget.options,
|
||||
sort: widget.sort,
|
||||
@@ -236,10 +248,6 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
|
||||
Widget _buildEmptyWidget() {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context);
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
|
||||
@@ -126,6 +126,10 @@ class MessageListView extends StatefulWidget {
|
||||
this.messageHighlightColor,
|
||||
this.onShowMessage,
|
||||
this.showConnectionStateTile = false,
|
||||
this.loadingBuilder,
|
||||
this.emptyBuilder,
|
||||
this.messageListBuilder,
|
||||
this.errorWidgetBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Function used to build a custom message widget
|
||||
@@ -185,6 +189,20 @@ class MessageListView extends StatefulWidget {
|
||||
|
||||
final bool showConnectionStateTile;
|
||||
|
||||
/// Function called when messages are fetched
|
||||
final Widget Function(BuildContext, List<Message>) messageListBuilder;
|
||||
|
||||
/// Function used to build a loading widget
|
||||
final WidgetBuilder loadingBuilder;
|
||||
|
||||
/// Function used to build an empty widget
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
/// Callback triggered when an error occurs while performing the given request.
|
||||
/// This parameter can be used to display an error message to users in the event
|
||||
/// of a connection failure.
|
||||
final ErrorBuilder errorWidgetBuilder;
|
||||
|
||||
@override
|
||||
_MessageListViewState createState() => _MessageListViewState();
|
||||
}
|
||||
@@ -244,41 +262,45 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MessageListCore(
|
||||
loadingBuilder: (context) {
|
||||
return Center(
|
||||
child: const CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
emptyBuilder: (context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'No chats here yet...',
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
);
|
||||
},
|
||||
messageListBuilder: (context, list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
loadingBuilder: widget.loadingBuilder ??
|
||||
(context) {
|
||||
return Center(
|
||||
child: const CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
emptyBuilder: widget.emptyBuilder ??
|
||||
(context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'No chats here yet...',
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
);
|
||||
},
|
||||
messageListBuilder: widget.messageListBuilder ??
|
||||
(context, list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
messageListController: _messageListController,
|
||||
parentMessage: widget.parentMessage,
|
||||
showScrollToBottom: widget.showScrollToBottom,
|
||||
errorWidgetBuilder: (BuildContext context, Object error) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Something went wrong',
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
);
|
||||
},
|
||||
errorWidgetBuilder: widget.errorWidgetBuilder ??
|
||||
(BuildContext context, Object error) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Something went wrong',
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,14 +53,16 @@ class MessageSearchListView extends StatefulWidget {
|
||||
this.sortOptions,
|
||||
this.paginationParams,
|
||||
this.messageFilters,
|
||||
this.emptyBuilder,
|
||||
this.errorBuilder,
|
||||
this.separatorBuilder,
|
||||
this.itemBuilder,
|
||||
this.onItemTap,
|
||||
this.showResultCount = true,
|
||||
this.pullToRefresh = true,
|
||||
this.showErrorTile = false,
|
||||
this.emptyBuilder,
|
||||
this.errorBuilder,
|
||||
this.loadingBuilder,
|
||||
this.childBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Message String to search on
|
||||
@@ -94,12 +96,6 @@ class MessageSearchListView extends StatefulWidget {
|
||||
/// Function called when tapping on a [MessageSearchItem]
|
||||
final MessageSearchItemTapCallback onItemTap;
|
||||
|
||||
/// The builder used when the channel list is empty.
|
||||
final EmptyMessageSearchBuilder emptyBuilder;
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
final Widget Function(Error error) errorBuilder;
|
||||
|
||||
/// Builder used to create a custom item separator
|
||||
final IndexedWidgetBuilder separatorBuilder;
|
||||
|
||||
@@ -111,6 +107,18 @@ class MessageSearchListView extends StatefulWidget {
|
||||
|
||||
final bool showErrorTile;
|
||||
|
||||
/// The builder that is used when the search messages are fetched
|
||||
final Widget Function(List<GetMessageResponse>) childBuilder;
|
||||
|
||||
/// The builder used when the channel list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
final ErrorBuilder errorBuilder;
|
||||
|
||||
/// The builder that will be used in case of loading
|
||||
final WidgetBuilder loadingBuilder;
|
||||
|
||||
@override
|
||||
_MessageSearchListViewState createState() => _MessageSearchListViewState();
|
||||
}
|
||||
@@ -128,101 +136,98 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
paginationParams: widget.paginationParams,
|
||||
messageFilters: widget.messageFilters,
|
||||
messageSearchListController: _messageSearchListController,
|
||||
emptyBuilder: (context) {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context, widget.messageQuery);
|
||||
}
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Text('There are no messages currently'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
errorBuilder: (BuildContext context, dynamic error) {
|
||||
if (error is Error) {
|
||||
print((error).stackTrace);
|
||||
}
|
||||
|
||||
if (widget.errorBuilder != null) {
|
||||
return widget.errorBuilder(error);
|
||||
}
|
||||
|
||||
var message = error.toString();
|
||||
if (error is DioError) {
|
||||
if (error.type == DioErrorType.RESPONSE) {
|
||||
message = error.message;
|
||||
} else {
|
||||
message = 'Check your connection and retry';
|
||||
}
|
||||
}
|
||||
return InfoTile(
|
||||
showMessage: widget.showErrorTile,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: 'An error occurred.',
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 2.0),
|
||||
child: Icon(Icons.error_outline),
|
||||
),
|
||||
),
|
||||
TextSpan(text: 'Error loading messages'),
|
||||
],
|
||||
emptyBuilder: widget.emptyBuilder ??
|
||||
(context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Text('There are no messages currently'),
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Text(message),
|
||||
),
|
||||
RaisedButton(
|
||||
onPressed: () {
|
||||
_messageSearchListController.loadData();
|
||||
},
|
||||
child: Text('Retry'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
loadingBuilder: (context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
errorBuilder: widget.emptyBuilder ??
|
||||
(BuildContext context, dynamic error) {
|
||||
if (error is Error) {
|
||||
print((error).stackTrace);
|
||||
}
|
||||
|
||||
var message = error.toString();
|
||||
if (error is DioError) {
|
||||
if (error.type == DioErrorType.RESPONSE) {
|
||||
message = error.message;
|
||||
} else {
|
||||
message = 'Check your connection and retry';
|
||||
}
|
||||
}
|
||||
return InfoTile(
|
||||
showMessage: widget.showErrorTile,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: 'An error occurred.',
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 2.0),
|
||||
child: Icon(Icons.error_outline),
|
||||
),
|
||||
),
|
||||
TextSpan(text: 'Error loading messages'),
|
||||
],
|
||||
),
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Text(message),
|
||||
),
|
||||
RaisedButton(
|
||||
onPressed: () {
|
||||
_messageSearchListController.loadData();
|
||||
},
|
||||
child: Text('Retry'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
childBuilder: (list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
loadingBuilder: widget.loadingBuilder ??
|
||||
(context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
childBuilder: widget.childBuilder ??
|
||||
(list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@ class UserListView extends StatefulWidget {
|
||||
/// Instantiate a new UserListView
|
||||
const UserListView({
|
||||
Key key,
|
||||
this.errorBuilder,
|
||||
this.emptyBuilder,
|
||||
this.filter,
|
||||
this.options,
|
||||
this.sort,
|
||||
@@ -61,18 +59,16 @@ class UserListView extends StatefulWidget {
|
||||
this.pullToRefresh = true,
|
||||
this.groupAlphabetically = false,
|
||||
this.crossAxisCount = 1,
|
||||
this.errorBuilder,
|
||||
this.emptyBuilder,
|
||||
this.loadingBuilder,
|
||||
this.listBuilder,
|
||||
}) : assert(
|
||||
crossAxisCount == 1 || groupAlphabetically == false,
|
||||
'Cannot group alphabetically when crossAxisCount > 1',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
final Widget Function(Error error) errorBuilder;
|
||||
|
||||
/// The builder used when the channel list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
/// The query filters to use.
|
||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||
/// You can also filter other built-in channel fields.
|
||||
@@ -130,6 +126,18 @@ class UserListView extends StatefulWidget {
|
||||
/// The number of children in the cross axis.
|
||||
final int crossAxisCount;
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
final Widget Function(Error error) errorBuilder;
|
||||
|
||||
/// The builder that will be used to build the list
|
||||
final Widget Function(BuildContext context, List<ListItem> users) listBuilder;
|
||||
|
||||
/// The builder that will be used for loading
|
||||
final WidgetBuilder loadingBuilder;
|
||||
|
||||
/// The builder used when the channel list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
@override
|
||||
_UserListViewState createState() => _UserListViewState();
|
||||
}
|
||||
@@ -143,32 +151,36 @@ class _UserListViewState extends State<UserListView>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var child = UserListCore(
|
||||
errorBuilder: (err) {
|
||||
return _buildError(err);
|
||||
},
|
||||
emptyBuilder: (context) {
|
||||
return _buildEmpty();
|
||||
},
|
||||
loadingBuilder: (context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
errorBuilder: widget.errorBuilder ??
|
||||
(err) {
|
||||
return _buildError(err);
|
||||
},
|
||||
emptyBuilder: widget.emptyBuilder ??
|
||||
(context) {
|
||||
return _buildEmpty();
|
||||
},
|
||||
loadingBuilder: widget.loadingBuilder ??
|
||||
(context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
listBuilder: (context, list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
listBuilder: widget.listBuilder ??
|
||||
(context, list) {
|
||||
return _buildListView(list);
|
||||
},
|
||||
pagination: widget.pagination,
|
||||
options: widget.options,
|
||||
sort: widget.sort,
|
||||
@@ -195,10 +207,6 @@ class _UserListViewState extends State<UserListView>
|
||||
Widget _buildError(Error error) {
|
||||
print((error).stackTrace);
|
||||
|
||||
if (widget.errorBuilder != null) {
|
||||
return widget.errorBuilder(error);
|
||||
}
|
||||
|
||||
var message = error.toString();
|
||||
if (error is DioError) {
|
||||
final dioError = error as DioError;
|
||||
@@ -246,10 +254,6 @@ class _UserListViewState extends State<UserListView>
|
||||
}
|
||||
|
||||
Widget _buildEmpty() {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context);
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
|
||||
Reference in New Issue
Block a user