diff --git a/docusaurus/docs/Flutter/assets/chat_overview_page-2fbd5bbfb70c5623bd37ff7d6c41bf4d.png b/docusaurus/docs/Flutter/assets/chat_overview_page-2fbd5bbfb70c5623bd37ff7d6c41bf4d.png new file mode 100644 index 00000000..9e92437d Binary files /dev/null and b/docusaurus/docs/Flutter/assets/chat_overview_page-2fbd5bbfb70c5623bd37ff7d6c41bf4d.png differ diff --git a/docusaurus/docs/Flutter/assets/firebase_notifications_toggle-5aeabfcbdc24cb8f1fea7d41d0e845fc.png b/docusaurus/docs/Flutter/assets/firebase_notifications_toggle-5aeabfcbdc24cb8f1fea7d41d0e845fc.png new file mode 100644 index 00000000..c5670d0d Binary files /dev/null and b/docusaurus/docs/Flutter/assets/firebase_notifications_toggle-5aeabfcbdc24cb8f1fea7d41d0e845fc.png differ diff --git a/docusaurus/docs/Flutter/guides/adding_push_notifications.mdx b/docusaurus/docs/Flutter/guides/adding_push_notifications.mdx index c90f6814..328ba546 100644 --- a/docusaurus/docs/Flutter/guides/adding_push_notifications.mdx +++ b/docusaurus/docs/Flutter/guides/adding_push_notifications.mdx @@ -1,11 +1,15 @@ --- id: adding_push_notifications sidebar_position: 1 -title: Adding Push Notifications +title: Adding Push Notifications (V1 legacy) --- Adding Push Notifications To Your Application +:::note +Version 1 (legacy) of push notifications won't be removed immediately but there won't be any new features. That's why new applications are highly recommended to use version 2 from the beginning to leverage upcoming new features. +::: + ### Introduction Push notifications are a core part of the experience for a messaging app. Users often need to be notified diff --git a/docusaurus/docs/Flutter/guides/adding_push_notifications_v2.mdx b/docusaurus/docs/Flutter/guides/adding_push_notifications_v2.mdx new file mode 100644 index 00000000..285c2205 --- /dev/null +++ b/docusaurus/docs/Flutter/guides/adding_push_notifications_v2.mdx @@ -0,0 +1,301 @@ +--- +id: adding_push_notifications_v2 +sidebar_position: 1 +title: Adding Push Notifications (V2) +--- + +Adding Push Notifications To Your Application + +### Introduction + +Push notifications are a core part of the experience for a messaging app. Users often need to be notified +of new messages and old notifications sometimes need to be updated silently. + +This guide details how to add push notifications to your app. + +You can read more about Stream’s [push delivery logic](https://getstream.io/chat/docs/flutter-dart/push_introduction/?language=dart#push-delivery-rules). + +### Setup FCM + +To integrate push notifications in your Flutter app, you need to use the package [firebase_messaging](https://pub.dev/packages/firebase_messaging). + + +Follow the [Firebase documentation](https://firebase.flutter.dev/docs/messaging/overview/) to set up the plugin for Android and iOS. + + +Once that's done, FCM should be able to send push notifications to your devices. + +### Integration With Stream + +#### Step 1 - Get the Firebase Credentials + +These credentials are the [private key file](https://firebase.google.com/docs/admin/setup#:~:text=To%20generate%20a%20private%20key%20file%20for%20your%20service%20account%3A) for your service account, in firebase console. + +To generate a private key file for your service account, in the Firebase console: + +- Open Settings > Service Accounts. + +- Click **Generate New Private Key**, then confirm by clicking **Generate Key**. + +- Securely store the JSON file containing the key. + +This JSON file contains the credentials which needs to be uploaded to Stream’s server as explained in next step. + +#### Step 2 - Upload the Firebase Credentials to Stream + +You can upload your Firebase credentials using either the dashboard or the app settings API (available only in backend SDKs). + +##### Using the Stream Dashboard + +1. Go to the **Chat Overview** page on Stream Dashboard + +![](../assets/chat_overview_page-2fbd5bbfb70c5623bd37ff7d6c41bf4d.png) + +2. Enable **Firebase Notification** toggle on **Chat Overview** + +![](../assets/firebase_notifications_toggle-5aeabfcbdc24cb8f1fea7d41d0e845fc.png) + +3. Enter your Firebase Credentials and press "Save". + +##### Using the API + +You can also enable Firebase notifications and upload the Firebase credentials using one of our server SDKs. + +For example, using the JavaScript SDK: + +```js +const client = StreamChat.getInstance('api_key', 'api_secret'); +client.updateAppSettings({ + push_config: { + version: 'v2' + }, + firebase_config: { + credentials_json: fs.readFileSync( + './firebase-credentials.json', + 'utf-8', + ), + }); +``` +### Registering a Device With Stream Backend + +Once you configure a Firebase server key and set it up on Stream dashboard then a device that is supposed to receive push notifications needs to be registered on the Stream backend. This is usually done by listening for Firebase device token updates and passing them to the backend as follows: + +```dart +firebaseMessaging.onTokenRefresh.listen((token) { + client.addDevice(token, PushProvider.firebase); +}); +``` + +### Receiving Notifications + +Push notifications behave a bit differently depending on whether you are using iOS or Android. +See [here](https://firebase.flutter.dev/docs/messaging/usage#message-types) to understand the difference between **notification** and **data** payloads. + +#### iOS + +On iOS we send both a **notification** and a **data** payload. +This means you don't need to do anything special to get the notification to show up. However, you might want to handle the data payload to perform some logic when the user taps on the notification. + +To update the template, you can use a backend SDK. +For example, using the javascript SDK: + +```js +const client = StreamChat.getInstance(‘api_key’, ‘api_secret’); +const apn_template = `{ + "aps": { + "alert": { + "title": "New message from {{ sender.name }}", + "body": "{{ truncate message.text 2000 }}" + }, + "mutable-content": 1, + "category": "stream.chat" + }, + "stream": { + "sender": "stream.chat", + "type": "message.new", + "version": "v2", + "id": "{{ message.id }}", + "cid": "{{ channel.cid }}" + } +}`; + +client.updateAppSettings({ + firebase_config: { + apn_template, + }); +``` + +#### Android +On Android we send only a **data** payload. This gives you more flexibility and lets you decide what to do with the notification. + +For example, you can listen and generate a notification from them. + +To generate a notification when a **data-only** message is received and the app is in background: + +```dart +Future onBackgroundMessage(RemoteMessage message) async { + final chatClient = StreamChatClient(apiKey); + + chatClient.connectUser( + User(id: userId), + userToken, + connectWebSocket: false, + ); + + handleNotification(message, chatClient); +} + +void handleNotification( + RemoteMessage message, + StreamChatClient chatClient, +) async { + + final data = message.data; + + if (data['type'] == 'message.new') { + final flutterLocalNotificationsPlugin = await setupLocalNotifications(); + final messageId = data['id']; + final response = await chatClient.getMessage(messageId); + + flutterLocalNotificationsPlugin.show( + 1, + 'New message from ${response.message.user.name} in ${response.channel.name}', + response.message.text, + NotificationDetails( + android: AndroidNotificationDetails( + 'new_message', + 'New message notifications channel', + )), + ); + } +} + +FirebaseMessaging.onBackgroundMessage(onBackgroundMessage); +``` + +In the above example, you get the message details using the `getMessage` method and then you use the [flutter_local_notifications](https://pub.dev/packages/flutter_local_notifications) package to show the actual notification. + +##### Using a Template on Android + +It's still possible to add a **notification** payload to Android notifications. +You can do so by adding a template using a backend SDK. +For example, using the javascript SDK: + +```js +const client = StreamChat.getInstance(‘api_key’, ‘api_secret’); +const notification_template = ` +{ + "title": "{{ sender.name }} @ {{ channel.name }}", + "body": "{{ message.text }}", + "click_action": "OPEN_ACTIVITY_1", + "sound": "default" +}`; + +client.updateAppSettings({ + firebase_config: { + notification_template, + }); +``` + +### Possible Issues + +Make sure to read the [general push notification docs](https://getstream.io/chat/docs/flutter-dart/push_introduction/?language=dart) in order to avoid known gotchas that may make your relationship with notifications difficult 😢. + +### Testing if Push Notifications are Setup Correctly + +If you're not sure whether you've set up push notifications correctly, for example, you don't always receive them, or they don’t work reliably, then you can follow these steps to make sure your config is correct and working: +1. Clone our repo for push testing: `git clone git@github.com:GetStream/chat-push-test.git` +2. `cd flutter` +3. In that folder run `flutter pub get` +4. Input your api key and secret in `lib/main.dart` +5. Change the bundle identifier/application ID and development team/user so you can run the app on your physical device.**Do not** run on an iOS simulator, as it will not work. Testing on an Android emulator is fine. +6. Add your `google-services.json/GoogleService-Info.plist` +7. Run the app +8. Accept push notification permission (iOS only) +9. Tap on `Device ID` and copy it +11. After configuring [stream-cli](https://github.com/GetStream/stream-cli), run the following command using your user ID: +```shell +stream chat:push:test -u +``` + +You should get a test push notification 🥳 + + +### Foreground Notifications + +Sometimes you may want to show a notification when the app is in the foreground. +For example, when you're in a channel and you receive a new message from someone in another channel. + +For this scenario, you can also use the `flutter_local_notifications` package to show a notification. + +You need to listen for new events using `FirebaseMessaging.onMessage.listen()` and handle them accordingly: + +```dart +FirebaseMessaging.onMessage.listen((message) async { + handleNotification( + message, + chatClient, + ); +}); +``` + +:::note +You should also check that the channel of the message is different than the channel in the foreground. +How you do this depends on your app infrastructure and how you handle navigation. +Take a look at the [Stream Chat v1 sample app](https://github.com/GetStream/flutter-samples/blob/main/packages/stream_chat_v1/lib/home_page.dart#L11) to see how we're doing it over there. +::: + +### Saving Notification Messages to the Offline Storage (Only Android) + +When the app is closed you may want to save received messages when you receive them via a notification so that later on when you open the app they're already there. + +To do this you need to integrate the package [stream_chat_persistence](https://pub.dev/packages/stream_chat_persistence) in our app that exports a persistence client, see [here](https://pub.dev/packages/stream_chat_persistence#usage) how to set it up. + +Then calling `FirebaseMessaging.onBackgroundMessage(...)` you need to use a TOP-LEVEL or STATIC function to handle background messages; here is an example: + +```dart +Future onBackgroundMessage(RemoteMessage message) async { + final chatClient = StreamChatClient(apiKey); + final persistenceClient = StreamChatPersistenceClient(); + + await persistenceClient.connect(userId); + + chatClient.connectUser( + User(id: userId), + userToken, + connectWebSocket: false, + ); + + handleNotification(message, chatClient); +} + +void handleNotification( + RemoteMessage message, + StreamChatClient chatClient, +) async { + final data = message.data; + if (data['type'] == 'message.new') { + final flutterLocalNotificationsPlugin = await setupLocalNotifications(); + final messageId = data['id']; + final cid = data['cid']; + final response = await chatClient.getMessage(messageId); + await persistenceClient.updateMessages(cid, [response.message]); + + persistenceClient.disconnect(); + + flutterLocalNotificationsPlugin.show( + 1, + 'New message from ${response.message.user.name} in ${response.channel.name}', + response.message.text, + NotificationDetails( + android: AndroidNotificationDetails( + 'new_message', + 'New message notifications channel', + )), + ); + } +} + +FirebaseMessaging.onBackgroundMessage(onBackgroundMessage); +``` + diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 84b57f16..a4de2b67 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -8,7 +8,7 @@ - Minor fixes and improvements. -## Upcoming +## 3.6.0 🐞 Fixed @@ -19,10 +19,12 @@ channel update. - [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) Fix `Unsupported operation: Cannot remove from an unmodifiable list`. - [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete message from client. +- Send only `user_id` while reconnecting. ✅ Added - Handle `event.message` in `channel.truncate` events +- Added additional parameters to `channel.truncate` ## 3.5.1 diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 0c3a6620..995de15c 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1055,10 +1055,23 @@ class Channel { return _client.deleteChannel(id!, type); } - /// Removes all messages from the channel. - Future truncate() async { + /// Removes all messages from the channel up to [truncatedAt] or now if + /// [truncatedAt] is not provided. + /// If [skipPush] is true, no push notification will be sent. + /// [Message] is the system message that will be sent to the channel. + Future truncate({ + Message? message, + bool? skipPush, + DateTime? truncatedAt, + }) async { _checkInitialized(); - return _client.truncateChannel(id!, type); + return _client.truncateChannel( + id!, + type, + message: message, + skipPush: skipPush, + truncatedAt: truncatedAt, + ); } /// Accept invitation to the channel. diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 76a72c93..0fca0d2c 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -328,7 +328,9 @@ class StreamChatClient { _chatPersistenceClient = _originalChatPersistenceClient; await _chatPersistenceClient!.connect(ownUser.id); } - final connectedUser = await openConnection(); + final connectedUser = await openConnection( + includeUserDetailsInConnectCall: true, + ); return state.currentUser = connectedUser; } catch (e, stk) { if (e is StreamWebSocketError && e.isRetriable) { @@ -341,7 +343,11 @@ class StreamChatClient { } /// Creates a new WebSocket connection with the current user. - Future openConnection() async { + /// If [includeUserDetailsInConnectCall] is true it will include the current + /// user details in the connect call. + Future openConnection({ + bool includeUserDetailsInConnectCall = false, + }) async { assert( state.currentUser != null, 'User is not set on client, ' @@ -371,7 +377,10 @@ class StreamChatClient { _ws.connectionStatusStream.skip(1).listen(_connectionStatusHandler); try { - final event = await _ws.connect(user); + final event = await _ws.connect( + user, + includeUserDetails: includeUserDetailsInConnectCall, + ); return user.merge(event.me); } catch (e, stk) { logger.severe('error connecting ws', e, stk); @@ -940,14 +949,23 @@ class StreamChatClient { channelType, ); - /// Removes all messages from the channel + /// Removes all messages from the channel up to [truncatedAt] or now if + /// [truncatedAt] is not provided. + /// If [skipPush] is true, no push notification will be sent. + /// [Message] is the system message that will be sent to the channel. Future truncateChannel( String channelId, - String channelType, - ) => + String channelType, { + Message? message, + bool? skipPush, + DateTime? truncatedAt, + }) => _chatApi.channel.truncateChannel( channelId, channelType, + message: message, + skipPush: skipPush, + truncatedAt: truncatedAt, ); /// Mutes the channel diff --git a/packages/stream_chat/lib/src/core/api/channel_api.dart b/packages/stream_chat/lib/src/core/api/channel_api.dart index 0b299d95..93d17870 100644 --- a/packages/stream_chat/lib/src/core/api/channel_api.dart +++ b/packages/stream_chat/lib/src/core/api/channel_api.dart @@ -265,11 +265,18 @@ class ChannelApi { /// Removes all messages from the channel Future truncateChannel( String channelId, - String channelType, - ) async { + String channelType, { + Message? message, + bool? skipPush, + DateTime? truncatedAt, + }) async { final response = await _client.post( '${_getChannelUrl(channelId, channelType)}/truncate', - data: {}, + data: { + if (message != null) 'message': message, + if (skipPush != null) 'skip_push': skipPush, + if (truncatedAt != null) 'truncated_at': truncatedAt, + }, ); return EmptyResponse.fromJson(response.data); } diff --git a/packages/stream_chat/lib/src/ws/websocket.dart b/packages/stream_chat/lib/src/ws/websocket.dart index 888ba08b..de5475ce 100644 --- a/packages/stream_chat/lib/src/ws/websocket.dart +++ b/packages/stream_chat/lib/src/ws/websocket.dart @@ -147,12 +147,15 @@ class WebSocket with TimerHelper { } } - Future _buildUri({bool refreshToken = false}) async { + Future _buildUri({ + bool refreshToken = false, + bool includeUserDetails = true, + }) async { final user = _user!; final token = await tokenManager.loadToken(refresh: refreshToken); final params = { 'user_id': user.id, - 'user_details': user, + if (includeUserDetails) 'user_details': user, 'user_token': token.rawValue, 'server_determines_connection_id': true, }; @@ -176,7 +179,10 @@ class WebSocket with TimerHelper { bool _connectRequestInProgress = false; /// Connect the WS using the parameters passed in the constructor - Future connect(User user) async { + Future connect( + User user, { + bool includeUserDetails = false, + }) async { if (_connectRequestInProgress) { throw const StreamWebSocketError(''' You've called connect twice, @@ -191,7 +197,9 @@ class WebSocket with TimerHelper { connectionCompleter = Completer(); try { - final uri = await _buildUri(); + final uri = await _buildUri( + includeUserDetails: includeUserDetails, + ); _initWebSocketChannel(uri); } catch (e, stk) { _onConnectionError(e, stk); @@ -219,7 +227,10 @@ class WebSocket with TimerHelper { setTimer( Duration(milliseconds: delay), () async { - final uri = await _buildUri(refreshToken: refreshToken); + final uri = await _buildUri( + refreshToken: refreshToken, + includeUserDetails: false, + ); try { _initWebSocketChannel(uri); } catch (e, stk) { diff --git a/packages/stream_chat/test/src/fakes.dart b/packages/stream_chat/test/src/fakes.dart index b4089512..409418cd 100644 --- a/packages/stream_chat/test/src/fakes.dart +++ b/packages/stream_chat/test/src/fakes.dart @@ -124,7 +124,10 @@ class FakeWebSocket extends Fake implements WebSocket { Completer? connectionCompleter; @override - Future connect(User user) async { + Future connect( + User user, { + bool? includeUserDetails = true, + }) async { connectionStatus = ConnectionStatus.connecting; final event = Event( type: EventType.healthCheck, @@ -167,7 +170,10 @@ class FakeWebSocketWithConnectionError extends Fake implements WebSocket { Completer? connectionCompleter; @override - Future connect(User user) async { + Future connect( + User user, { + bool? includeUserDetails = true, + }) async { connectionStatus = ConnectionStatus.connecting; const error = StreamWebSocketError('Error Connecting'); connectionCompleter = Completer()..completeError(error); diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 3feb52ab..3ba30484 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -14,10 +14,14 @@ - Added OpenGraph preview support for links in `StreamMessageInput`. - Removed video compression. +## 3.6.0 + 🐞 Fixed - Minor fixes and improvements -[[#892]](https://github.com/GetStream/stream-chat-flutter/issues/892): Fix default `initialAlignment` in `MessageListView`. +- Fix `MessageInputTheme.inputBackgroundColor` color not being used in some widgets of `MessageInput` +- Removed dependency on `visibility_detector` ## 3.5.1 diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart index c77b668c..bc24ed46 100644 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart +++ b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart index f3203ff6..ab3ab00d 100644 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart +++ b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:math'; import 'package:collection/collection.dart' show IterableExtension; -import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 240d318a..1dd27661 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -470,6 +470,7 @@ class MessageInputState extends State { if (widget.editMessage == null) { child = Material( elevation: 8, + color: _messageInputTheme.inputBackgroundColor, child: child, ); } @@ -669,6 +670,7 @@ class MessageInputState extends State { gradient: _focusNode.hasFocus ? _messageInputTheme.activeBorderGradient : _messageInputTheme.idleBorderGradient, + color: _messageInputTheme.inputBackgroundColor, ), child: Padding( padding: const EdgeInsets.all(1.5), diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 7fb96a95..d8c6a569 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -8,7 +8,6 @@ import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positi import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/swipeable.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:visibility_detector/visibility_detector.dart'; /// Widget builder for message /// [defaultMessageWidget] is the default [StreamMessageWidget] configuration @@ -975,26 +974,7 @@ class _StreamMessageListViewState extends State { int index, ) { final messageWidget = buildMessage(message, messages, index); - return VisibilityDetector( - key: ValueKey('visibility: ${message.id}'), - onVisibilityChanged: (visibility) { - final isVisible = visibility.visibleBounds != Rect.zero; - if (isVisible) { - final channel = streamChannel.channel; - if (_upToDate && - channel.config?.readEvents == true && - channel.state!.unreadCount > 0) { - streamChannel.channel.markRead(); - } - } - if (mounted) { - if (_showScrollToBottom.value == isVisible) { - _showScrollToBottom.value = !isVisible; - } - } - }, - child: messageWidget, - ); + return messageWidget; } Widget buildParentMessage( @@ -1316,6 +1296,8 @@ class _StreamMessageListViewState extends State { _scrollController = widget.scrollController ?? ItemScrollController(); _itemPositionListener = widget.itemPositionListener ?? ItemPositionsListener.create(); + _itemPositionListener.itemPositions + .addListener(_handleItemPositionsChanged); _getOnThreadTap(); super.initState(); @@ -1365,6 +1347,34 @@ class _StreamMessageListViewState extends State { super.didChangeDependencies(); } + void _handleItemPositionsChanged() { + final _itemPositions = _itemPositionListener.itemPositions.value.toList(); + final _firstItemIndex = + _itemPositions.indexWhere((element) => element.index == 1); + var _isFirstItemVisible = false; + if (_firstItemIndex != -1) { + final _firstItem = _itemPositions[_firstItemIndex]; + _isFirstItemVisible = + _firstItem.itemLeadingEdge > 0 && _firstItem.itemTrailingEdge < 1; + } + if (_isFirstItemVisible) { + // most recent message is visible + final channel = streamChannel?.channel; + if (channel != null) { + if (_upToDate && + channel.config?.readEvents == true && + channel.state!.unreadCount > 0) { + streamChannel!.channel.markRead(); + } + } + } + if (mounted) { + if (_showScrollToBottom.value == _isFirstItemVisible) { + _showScrollToBottom.value = !_isFirstItemVisible; + } + } + } + void _getOnThreadTap() { if (widget.onThreadTap != null) { _onThreadTap = (Message message) { @@ -1402,6 +1412,8 @@ class _StreamMessageListViewState extends State { streamChannel!.reloadChannel(); } _messageNewListener?.cancel(); + _itemPositionListener.itemPositions + .removeListener(_handleItemPositionsChanged); super.dispose(); } } diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 87246386..931f19d2 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -40,8 +40,7 @@ dependencies: substring_highlight: ^1.0.26 url_launcher: ^6.0.3 video_player: ^2.1.0 - video_thumbnail: ^0.4.3 - visibility_detector: ^0.2.0 + video_thumbnail: ^0.5.0 flutter: assets: diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 1fb6c164..703e3294 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -9,6 +9,10 @@ - Updated `stream_chat` dependency to [`4.0.0-beta.0`](https://pub.dev/packages/stream_chat/changelog). +## 3.6.0 + +- Updated `stream_chat` dependency to [`3.6.0`](https://pub.dev/packages/stream_chat/changelog). + ## 3.5.1 - Updated `stream_chat` dependency to [`3.5.1`](https://pub.dev/packages/stream_chat/changelog).