dependencies:
stream_chat_flutter: ^4.0.0 # full UI, core and client packages
stream_chat_flutter_core: ^4.0.0 # core and client packages
stream_chat: ^4.0.0 # client package
Migration Guide v4.0
Version 4.0.0 of the Stream Chat Flutter SDK carries significant architectural changes to improve the developer experience by giving you more control and flexibility in how you use our core components and UI widgets.
This guide is intended to enumerate and better explain the changes in the SDK.
If you find any bugs or have any questions, please file an issue on our GitHub repository. We want to support you as much as we can with this migration.
Code examples:
- See our Stream Chat Flutter tutorial for an up-to-date guide using the latest Stream Chat version.
- See the Stream Flutter Samples repository with our full fledged messaging sample application.
All of our documentation has also been updated to support v4, so all of the guides and examples will have updated code.
Dependencies
To migrate to v4.0.0, update your pubspec.yaml
with the correct Stream chat package you’re using:
Name Changes
The majority of the Stream Chat widgets and classes have now been renamed to have a “Stream” prefix associated with them. This increases Stream widgets’ discoverability and avoids name conflicts when importing.
For example, MessageListView
is now called StreamMessageListView
, and UserAvatar
is renamed to StreamUserAvatar
.
The old class names are deprecated and will be removed in the next major release (v5.0.0).
See the sections below on “deprecated classes” for a complete list of changes. Some of these classes/widgets have undergone functional changes as well, that will be explore in the following sections.
Removed Functionality/Widgets
This section highlights functionality removed.
Removed Methods And Classes
In version 4 we removed the following deprecated methods and classes:
Channel.banUser
Channel.unbanUser
ClientState.user
ClientState.userStream
MessageWidget.allRead
MessageWidget.readList
StreamChat.user
StreamChat.userStream
StreamChatCore.user
StreamChatCore.userStream
These were marked as deprecated in v3.
Video Compression
The automatic video compression when uploading a video has been removed. You can integrate this yourself by manipulating attachments using a custom attachment uploader.
Slidable Channel List Item
The default slidable channel preview behavior has been removed. We have created a guide showing you how you can easily add this functionality yourself.
Pin Permission
pinPermissions
is no longer needed in the MessageListView widget. The permissions are automatically fetched for each Stream project. To enable users to pin the message, make sure the pin permissions are granted for different types of users on your Stream application dashboard.
Deprecated Classes
This section covers all the deprecated classes and widgets in the Stream chat packages. Some of these have also undergone functional changes, for example, MessageInput and ChannelsBloc. These are discussed in more detail below.
The majority of the Stream widgets and classes have now been renamed to have a “Stream” prefix associated with them.
Changes:
AttachmentTitle
in favor ofStreamAttachmentTitle
AttachmentUploadStateBuilder
in favor ofStreamAttachmentsUploadStateBuilder
AttachmentWidget
in favor ofStreamAttachmentWidget
AvatarThemeData
in favor ofStreamAvatarThemeData
ChannelAvatar
in favor ofStreamChannelAvatar
ChannelBottomSheet
in favor ofStreamChannelInfoBottomSheet
ChannelHeader
in favor ofStreamChannelHeader
ChannelHeaderTheme
in favor ofStreamChannelHeaderTheme
ChannelHeaderThemeData
in favor ofStreamChannelHeaderThemeData
ChannelInfo
in favor ofStreamChannelInfo
ChannelListHeader
in favor ofStreamChannelListHeader
ChannelListHeaderTheme
in favor ofStreamChannelListHeaderTheme
ChannelListHeaderThemeData
in favor ofStreamChannelListHeaderThemeData
ChannelListView
in favor ofStreamChannelListView
ChannelListViewTheme
in favor ofStreamChannelListViewTheme
ChannelListViewThemeData
in favor ofStreamChannelListViewThemeData
ChannelListHeader
in favor ofStreamChannelListHeader
ChannelListView
in favor ofStreamChannelListView
ChannelName
in favor ofStreamChannelName
ChannelPreview
in favor ofStreamChannelListTile
ChannelPreviewTheme
in favor ofStreamChannelPreviewTheme
ChannelPreviewThemeData
in favor ofStreamChannelPreviewThemeData
ChannelName
in favor ofStreamChannelName
ColorTheme
in favor ofStreamColorTheme
CommandsOverlay
in favor ofStreamCommandsOverlay
ConnectionStatusBuilder
in favor ofStreamConnectionStatusBuilder
DateDivider
in favor ofStreamDateDivider
DeletedMessage
in favor ofStreamDeletedMessage
EmojiOverlay
in favor ofStreamEmojiOverlay
FileAttachment
in favor ofStreamFileAttachment
FullScreenMedia
in favor ofStreamFullScreenMedia
GalleryFooter
in favor ofStreamGalleryFooter
GalleryFooterThemeData
in favor ofStreamGalleryFooterThemeData
GalleryHeader
in favor ofStreamGalleryHeader
GalleryHeaderTheme
in favor ofStreamGalleryHeaderTheme
GalleryHeaderThemeData
in favor ofStreamGalleryHeaderThemeData
GiphyAttachment
in favor ofStreamGiphyAttachment
GradientAvatar
in favor ofStreamGradientAvatar
GroupAvatar
in favor ofStreamGroupAvatar
ImageAttachment
in favor ofStreamImageAttachment
ImageGroup
in favor ofStreamImageGroup
InfoTile
in favor ofStreamInfoTile
MediaListView
in favor ofStreamMediaListView
MessageAction
in favor ofStreamMessageAction
MessageActionsModal
in favorStreamMessageActionsModal
MessageInput
in favor ofStreamMessageInput
MessageInputTheme
in favor ofStreamMessageInputTheme
MessageInputThemeData
in favor ofStreamMessageInputThemeData
MessageInputState
in favor ofStreamMessageInput
MessageListView
in favor ofStreamMessageListView
MessageListViewTheme
in favor ofStreamMessageListViewTheme
MessageListViewThemeData
in favor ofStreamMessageListViewThemeData
MessageSearchListView
in favor ofStreamMessageSearchListView
MessageSearchListViewTheme
in favor ofStreamMessageSearchListViewTheme
MessageSearchListViewThemeData
in favor ofStreamMessageSearchListViewThemeData
MessageReactionsModal
in favor ofStreamMessageReactionsModal
MessageSearchItem
in favor ofStreamMessageSearchItem
MessageSearchListView
in favor ofStreamMessageSearchListView
MessageText
in favor ofStreamMessageText
MessageWidget
in favor ofStreamMessageWidget
MessageThemeData
in favor ofStreamMessageThemeData
MultiOverlay
in favor ofStreamMultiOverlay
OptionListTile
in favor ofStreamOptionListTile
QuotedMessageWidget
in favor ofStreamQuotedMessageWidget
ReactionBubble
in favor ofStreamReactionBubble
ReactionIcon
in favor ofStreamReactionIcon
ReactionPicker
in favor ofStreamReactionPicker
SendingIndicator
in favor ofStreamSendingIndicator
SystemMessage
in favor ofStreamSystemMessage
TextTheme
in favor ofStreamTextTheme
ThreadHeader
in favor ofStreamThreadHeader
TypingIndicator
in favor ofStreamTypingIndicator
UnreadIndicator
in favor ofSteamUnreadIndicator
UploadProgressIndicator
in favor ofStreamUploadProgressIndicator
UrlAttachment
in favor ofStreamUrlAttachment
UserAvatar
in favor ofStreamUserAvatar
UserItem
in favor ofStreamUserItem
UserListView
in favor ofStreamUserListView
UserListViewTheme
in favor ofStreamUserListViewTheme
UserListViewThemeData
in favor ofStreamUserListViewThemeData
UserMentionTile
in favor ofStreamUserMentionTile
UserMentionsOverlay
in favor ofStreamUserMentionsOverlay
VideoAttachment
in favor ofStreamVideoAttachment
VideoService
in favor ofStreamVideoService
VideoThumbnailImage
in favor ofStreamVideoThumbnailImage
VisibleFootnote
in favor ofStreamVisibleFootnote
ChannelListView to StreamChannelListView
The ChannelListView
widget has been deprecated, and it is now recommended to use StreamChannelListView
.
Version 4 of the Stream Chat Flutter packages introduces a new controller called, StreamChannelListController
. This controller manages the content for a channel list; it lets you perform tasks such as:
- Load initial data.
- Use channel events handlers.
- Load more data using
loadMore
. - Replace the previously loaded channels.
- Return/Create a new channel and start watching it.
- Pause and Resume all subscriptions added to this composite.
For more information see the StreamChannelListView
documentation.
ChannelsBloc to StreamChannelListController
The ChannelsBloc
widget should be replaced with StreamChannelListController
. This controller provides all the functionality needed to query and manipulate channel data previously accessible through ChannelsBloc
.
For more information see the StreamChannelListController
documentation.
StreamChannelListView Examples
Let’s explore some examples of the functional differences when using the new StreamChannelListView
.
The StreamChannelListController provides various methods, such as:
deleteChannel
loadMore
muteChannel
deleteChannel
For a complete list with additional information, see the code documentation.
Basic Use
The following code demonstrates the old way of creating a ChannelListPage, that displays a list of channels:
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
filter: Filter.in_(
'members',
[StreamChat.of(context).currentUser!.id],
),
sort: const [SortOption('last_message_at')],
limit: 20,
channelWidget: const ChannelPage(),
),
),
);
}
}
In v4 this can now be achieved with the following:
class ChannelListPage extends StatefulWidget {
const ChannelListPage({
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
@override
State<ChannelListPage> createState() => _ChannelListPageState();
}
class _ChannelListPageState extends State<ChannelListPage> {
late final _controller = StreamChannelListController(
client: widget.client,
filter: Filter.in_(
'members',
[StreamChat.of(context).currentUser!.id],
),
channelStateSort: const [SortOption('last_message_at')],
);
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
body: RefreshIndicator(
onRefresh: _controller.refresh,
child: StreamChannelListView(
controller: _controller,
onChannelTap: (channel) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: const ChannelPage(),
),
),
),
),
),
);
}
As you can see, the ChannelsBloc has been replaced with a StreamChannelListController, where the filter, limit, and sort arguments can be set. The above code also demonstrates how to refresh the channel list by calling _controller.refresh()
.
MessageSearchListView to StreamMessageSearchListView
The MessageSearchListView
widget has been deprecated, and it is now recommended to use StreamMessageSearchListView
.
Version 4 of the Stream Chat Flutter packages introduces a new controller called, StreamMessageSearchListController
. This controller manages the content when searching for a message; it lets you perform tasks such as:
- Load initial data.
- Set filters and search terms.
- Load more data using
loadMore
. - Refresh data.
For more information see the StreamMessageSearchListView
documentation.
MessageSearchBloc to StreamMessageSearchListController
The MessageSearchBloc
widget should be replaced with a StreamMessageSearchListController
. This controller provides all the functionality needed to query and manipulate message search data previously accessible through MessageSearchBloc
.
For more information see the StreamMessageSearchListController
documentation.
StreamMessageSearchListView Example
The following code demonstrates the old way of searching for messages:
class SearchExample extends StatelessWidget {
const SearchExample({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MessageSearchBloc(
child: MessageSearchListView(
showErrorTile: true,
messageQuery: 'message query',
filters: Filter.in_('members', const ['user-id']),
sortOptions: const [
SortOption(
'created_at',
direction: SortOption.ASC,
),
],
pullToRefresh: false,
limit: 30,
emptyBuilder: (context) => const Text('Nothing to show'),
itemBuilder: (context, messageResponse) {
/// Return widget
}
onItemTap: (messageResponse) {
/// Handle on tap
}
),
);
}
}
In v4, this can now be achieved with the following:
class SearchExample extends StatefulWidget {
const SearchExample({
Key? key,
}) : super(key: key);
@override
State<SearchExample> createState() => _SearchExampleState();
}
class _SearchExampleState extends State<SearchExample> {
late final StreamMessageSearchListController _messageSearchListController =
StreamMessageSearchListController(
client: StreamChat.of(context).client,
filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]),
limit: 5,
searchQuery: '',
sort: [
const SortOption(
'created_at',
direction: SortOption.ASC,
),
],
);
search() {
_messageSearchListController.searchQuery = 'search-value';
_messageSearchListController.doInitialLoad();
}
@override
dispose() {
_messageSearchListController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamMessageSearchListView(
controller: _messageSearchListController,
emptyBuilder: (context) => const Text('Nothing to show'),
itemBuilder: (
context,
messageResponses,
index,
defaultWidget,
) {
return defaultWidget.copyWith(); // modify default widget
});
}
}
UserListView to StreamUserListView
The UserListView
widget has been deprecated, and it is now recommended to use StreamUserListView
.
Version 4 of the Stream Chat Flutter packages introduces a new controller called, StreamUserListController
. This controller manages the content when retrieving Stream users; it let’s you perform tasks, such as:
- Load data.
- Set filters.
- Refresh data.
For more information see the StreamUserListView
documentation.
UsersBloc to StreamUserListController
The UsersBloc
widget should be replaced with a StreamUserListController
. This controller provides all the functionality needed to query and manipulate user data previously accessible through UsersBloc
.
For more information see the StreamUserListController
documentation.
StreamUserListView Example
The following code demonstrates the old way of displaying all users:
class UsersExample extends StatelessWidget {
const UsersExample({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return UsersBloc(
child: UserListView(
groupAlphabetically: true,
onUserTap: (user, _) {
/// Handle on tap
},
limit: 25,
filter: Filter.and([
Filter.autoComplete('name', 'some-name'),
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
]),
sort: const [
SortOption(
'name',
direction: 1,
),
],
),
);
}
}
In v4, this can now be achieved with the following:
class UsersExample extends StatefulWidget {
const UsersExample({
Key? key,
}) : super(key: key);
@override
State<UsersExample> createState() => _UsersExampleState();
}
class _UsersExampleState extends State<UsersExample> {
late final userListController = StreamUserListController(
client: StreamChat.of(context).client,
limit: 25,
filter: Filter.and([
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
]),
sort: [
const SortOption(
'name',
direction: 1,
),
],
);
void _load() {
userListController.filter = Filter.and([
Filter.autoComplete('name', 'some-name'),
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
]);
userListController.doInitialLoad();
}
@override
dispose() {
userListController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamUserListView(
controller: userListController,
onUserTap: (user) {
/// Handle on tap
},
emptyBuilder: (context) => const Text('Nothing to show'),
itemBuilder: (
context,
users,
index,
defaultWidget,
) {
return defaultWidget.copyWith(); // modify default widget
},
);
}
}
MessageInput to StreamMessageInput
The MessageInput
widget has been deprecated, and it is now recommended to use StreamMessageInput
.
Version 4 of the Stream Chat Flutter packages introduces a new controller called, MessageInputController
. This controller maintains the state of the message input and exposes various methods to allow you to customize and manipulate the underlying Message value.
Creating a separate controller allows easier control over the message input content by moving logic out of the deprecated MessageInput
and into the controller. This controller can then be created, managed, and exposed in whatever way you like.
The widget is also separated into smaller components: StreamCountDownButton
, StreamAttachmentPicker
, etc.
❗The
MessageInputController
is exposed by thestream_chat_flutter_core
package. This allows you to use the controller even if you’re not using the UI components.
As a result of this extra control, it is no longer needed for the new StreamMessageInput
widget to expose these MessageInput
arguments:
parentMessage
: parent message in case of a threadeditMessage
: message to editinitialMessage
: message to start withquotedMessage
: message to quote/replyonQuotedMessageCleared
: callback for clearing quoted messagetextEditingController
: the text controller of the text field
The following arguments are newly introduced to the StreamMessageInput
, and are not available on the old MessageInput
:
messageInputController
: the controller for the message inputattachmentsPickerBuilder
: builder for bottom sheet when attachment picker is openedsendButtonBuilder
: builder for creating send buttonvalidator
: a callback function that validates the messagerestorationId
: restoration ID to save and restore the state of the MessageInputenableSafeArea
: wraps the StreamMessageInput widget with a SafeArea widgetelevation
: elevation of the StreamMessageInput widgetshadow
: Shadow for the StreamMessageInput widget
For more information see the StreamMessageInput
documentation.
StreamMessageInput Examples
Let’s explore some examples of the functional differences when using the new StreamMessageInput
.
Basic Use
Unless you want to programmatically manipulate the value of the message input, then there is no difference in how you would use the message input widget.
The following code demonstrates the old way of creating a ChannelPage widget that displays a chat screen:
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const ChannelHeader(),
body: Column(
children: const <Widget>[
Expanded(
child: MessageListView(),
),
MessageInput(),
],
),
);
}
}
In v4 this is the same, the only difference being that all the Stream widgets are now prefixed with Stream. For example, MessageListView becomes StreamMessageListView, and so forth.
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) => Scaffold(
appBar: const StreamChannelHeader(),
body: Column(
children: const <Widget>[
Expanded(
child: StreamMessageListView(),
),
StreamMessageInput(),
],
),
);
}
However, you can optionally pass in a MessageInputController in the StreamMessageInput, which gives extra control over the message input value.
Thread Page
The following code demonstrates the old way of creating a thread page:
class ThreadPage extends StatelessWidget {
const ThreadPage({
Key? key,
this.parent,
}) : super(key: key);
final Message? parent;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ThreadHeader(
parent: parent!,
),
body: Column(
children: <Widget>[
Expanded(
child: MessageListView(
parentMessage: parent,
),
),
MessageInput(
parentMessage: parent,
),
],
),
);
}
}
In v4 the only difference is the Stream prefix and the way that the parent message is passed to the message input:
class ThreadPage extends StatelessWidget {
const ThreadPage({
Key? key,
this.parent,
}) : super(key: key);
final Message? parent;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: StreamThreadHeader(
parent: parent!,
),
body: Column(
children: <Widget>[
Expanded(
child: StreamMessageListView(
parentMessage: parent,
),
),
StreamMessageInput(
messageInputController: MessageInputController(
message: Message(parentId: parent!.id),
),
),
],
),
);
}
}
To send a thread message, you need to specify the message’s parent ID for which you’re creating a thread.
Reply/Quote Message
The following code demonstrates the old way of replying to a message:
...
void _reply(Message message) {
setState(() => _quotedMessage = message);
}
...
MessageInput
quotedMessage: _quotedMessage,
onQuotedMessageCleared: () {
setState(() => _quotedMessage = null);
},
),
To reply to a message in v4:
...
void _reply(Message message) {
_messageInputController.quotedMessage = message;
}
...
StreamMessageInput(
messageInputController: _messageInputController,
),
The controller makes it much simpler to dynamically modify the message input.
Stream Chat Flutter Core
Various changes have been made to the Core package, most notably, the introduction of all of the controllers mentioned above.
These controllers replace the business logic implementations (Bloc). Please note that this is not related to the well-known Flutter Bloc package, but instead refers to the naming we used for our business logic components.
In this version we’re introducing controllers in place of their bloc counterparts: StreamChannelListController in favor of ChannelsBlocStreamMessageSearchListController in favor of MessageSearchBlocStreamUserListController in favor of UsersBloc
The Bloc components are deprecated in v4.0.0 but can still be used. They will be removed in the next major release (v5.0.0).
Additionally, we also now have the StreamMessageInputController, as discussed above. This can be used outside of our UI package as well.
Finally, the following Core builders are also deprecated as their functionality can be replaced using their controller counterparts:
- ChannelListCore
- MessageSearchListCore
- UserListCore