diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9f5ca809..ec65f282 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -6,9 +6,6 @@ on:
- master
- feature/new-ui
-env:
- flutter_version: "1.20.2"
-
jobs:
build_and_deploy_ios:
runs-on: [macos-latest]
@@ -28,7 +25,7 @@ jobs:
bundle install
- uses: subosito/flutter-action@v1.3.2
with:
- flutter-version: ${{ env.flutter_version }}
+ channel: 'stable'
- name: Install firebase-tools
run: npm install -g firebase-tools
- name: Flutter setup
@@ -47,25 +44,23 @@ jobs:
build_and_deploy_android:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/checkout@v1
- - uses: actions/setup-java@v1
- with:
- java-version: '12.x'
- - uses: subosito/flutter-action@v1.3.2
- with:
- flutter-version: ${{ env.flutter_version }}
- - run: |
- cd example
- flutter pub get
- - name: Build
- run: |
- cd example
- flutter build apk
- - name: Deploy
- uses: wzieba/Firebase-Distribution-Github-Action@v1.2.1
- with:
- appId: ${{secrets.FIREBASE_ANDROID_APPID}}
- token: ${{secrets.FIREBASE_TOKEN}}
- groups: stream-testers
- file: example/build/app/outputs/apk/release/app-release.apk
+ - uses: actions/checkout@v2
+ - name: Flutter action
+ uses: subosito/flutter-action@v1.3.2
+ with:
+ channel: 'stable'
+ - name: Get dependencies
+ run: flutter pub get
+ - name: Coverage fix
+ run: |
+ file=test/coverage_helper_test.dart
+ echo "// Helper file to make coverage work for all dart files\n" > $file
+ echo "// ignore_for_file: unused_import" >> $file
+ find lib -name '*.dart' | grep -e '[^g]\.dart' | grep -v '_html.dart' | cut -c4- | awk -v package=stream_chat_flutter '{printf "import '\''package:%s%s'\'';\n", package, $1}' >> $file
+ echo "" >> $file
+ echo "void main(){}" >> $file
+ cat $file
+ - name: Run tests
+ run: flutter test --coverage
+ - name: Codecov
+ run: bash <(curl -s https://codecov.io/bash) -c -t ${{ secrets.CODECOV_TOKEN }} -f coverage/lcov.info -F flutter_tool
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc19a470..95b92815 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,45 @@
+## 0.2.8+3
+
+- Add simple example of channel creation in sample app
+- Add back button to the full-screen video view
+- Update llc version
+
+## 0.2.8+2
+
+- Add back button to the full-screen view
+
+## 0.2.8+1
+
+- Update LLC dependency
+- Update file_picker dependency
+
+## 0.2.8
+
+- Update LLC dependency
+
+## 0.2.7+2
+
+- Fix channellistview loading when client is not initialized
+- Update LLC dependency
+
+## 0.2.7
+
+- Update llc dependency
+- Fixed a bug that made the SDK crash if it went to background while not connected
+
+## 0.2.6+1
+
+- Update llc dependency
+
+## 0.2.6
+
+- Add `pullToRefresh` property to `ChannelListView`
+- Add `onLinkTap` to `MessageWidget`
+
+## 0.2.5
+
+- Implement `didUpdateWidget` in `ChannelListView` to react to setState
+
## 0.2.4
- Update llc dependency
diff --git a/README.md b/README.md
index d2bd5dd6..7ca34145 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@
[](https://pub.dartlang.org/packages/stream_chat_flutter)

[](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+[](https://codecov.io/gh/GetStream/stream-chat-flutter)
diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id
new file mode 100644
index 00000000..ab2c77bd
--- /dev/null
+++ b/example/ios/Flutter/.last_build_id
@@ -0,0 +1 @@
+13ecb9b157bb3e3d1ca2efb15c813a88
\ No newline at end of file
diff --git a/example/ios/Notifications/NotificationService.swift b/example/ios/Notifications/NotificationService.swift
index 7d085536..60e44390 100644
--- a/example/ios/Notifications/NotificationService.swift
+++ b/example/ios/Notifications/NotificationService.swift
@@ -7,7 +7,7 @@
//
import UserNotifications
-import StreamChatClient
+//import StreamChatClient
final class NotificationService: UNNotificationServiceExtension {
@@ -26,29 +26,29 @@ final class NotificationService: UNNotificationServiceExtension {
return
}
- Client.config = .init(apiKey: apiKey, logOptions: .error)
- Client.shared.set(user: User(id: userId), token: token) { res in
- guard res.isConnected else {
- return
- }
-
- Client.shared.message(withId: messageId) { [weak self] res in
- if let message = res.value?.message,
- let channel = res.value?.channel {
- let messageWrapper = MessageWrapper(channel: channel, message: message)
- if let encodedData = try? JSONEncoder.stream.encode(messageWrapper),
- let encodedString = String(data: encodedData, encoding: .utf8) {
- let storedMessages = sharedDefaults.stringArray(forKey: "messageQueue") ?? []
- sharedDefaults.setValue(storedMessages + [encodedString], forKey: "messageQueue")
-
- // Modify the notification content here...
- self?.bestAttemptContent?.title = "[modified] \(self?.bestAttemptContent?.title ?? "")"
- contentHandler(self?.bestAttemptContent ?? request.content)
- }
- Client.shared.disconnect()
- }
- }
- }
+// Client.config = .init(apiKey: apiKey, logOptions: .error)
+// Client.shared.set(user: User(id: userId), token: token) { res in
+// guard res.isConnected else {
+// return
+// }
+//
+// Client.shared.message(withId: messageId) { [weak self] res in
+// if let message = res.value?.message,
+// let channel = res.value?.channel {
+// let messageWrapper = MessageWrapper(channel: channel, message: message)
+// if let encodedData = try? JSONEncoder.stream.encode(messageWrapper),
+// let encodedString = String(data: encodedData, encoding: .utf8) {
+// let storedMessages = sharedDefaults.stringArray(forKey: "messageQueue") ?? []
+// sharedDefaults.setValue(storedMessages + [encodedString], forKey: "messageQueue")
+//
+// // Modify the notification content here...
+// self?.bestAttemptContent?.title = "[modified] \(self?.bestAttemptContent?.title ?? "")"
+// contentHandler(self?.bestAttemptContent ?? request.content)
+// }
+// Client.shared.disconnect()
+// }
+// }
+// }
}
override func serviceExtensionTimeWillExpire() {
@@ -58,125 +58,125 @@ final class NotificationService: UNNotificationServiceExtension {
}
}
-public struct MessageWrapper: Encodable {
- private enum CodingKeys: String, CodingKey {
- case id
- case channel
- case type
- case user
- case created = "created_at"
- case updated = "updated_at"
- case text
- case command
- case args
- case attachments
- case parentId = "parent_id"
- case showReplyInChannel = "show_in_channel"
- case mentionedUsers = "mentioned_users"
- }
-
- init(channel: Channel, message: Message) {
- id = message.id
- type = message.type
- user = message.user
- created = message.created
- updated = message.updated
- text = message.text
- command = message.command
- args = message.args
- attachments = message.attachments
- parentId = message.parentId
- showReplyInChannel = message.showReplyInChannel
- mentionedUsers = message.mentionedUsers
- extraData = message.extraData
- self.channel = ChannelWrapper(channel: channel)
- }
-
- /// A message id.
- public let id: String
- /// The channel cid.
- public let channel: ChannelWrapper?
- /// A message type (see `MessageType`).
- public let type: MessageType
- /// A user (see `User`).
- public let user: User
- /// A created date.
- public let created: Date
- /// A updated date.
- public let updated: Date
- /// A text.
- public let text: String
- /// A used command name.
- public let command: String?
- /// A used command args.
- public let args: String?
- /// Attachments (see `Attachment`).
- public let attachments: [Attachment]
- /// A parent message id.
- public let parentId: String?
- /// Check if this reply message needs to show in the channel.
- public let showReplyInChannel: Bool
- /// Mentioned users (see `User`).
- public let mentionedUsers: [User]
- /// An extra data for the message.
- public let extraData: Codable?
-}
-
-public struct ChannelWrapper: Encodable {
- /// Coding keys for the encoding.
- private enum CodingKeys: String, CodingKey {
- case id
- case cid
- case type
- case name
- case imageURL = "image"
- case members
- case lastMessageDate = "last_message_at"
- case createdBy = "created_by"
- case created = "created_at"
- case deleted = "deleted_at"
- case frozen
- }
-
- init(channel: Channel) {
- id = channel.id
- cid = channel.cid
- type = channel.type
- name = channel.name
- imageURL = channel.imageURL
- lastMessageDate = channel.lastMessageDate
- created = channel.created
- deleted = channel.deleted
- createdBy = channel.createdBy
- config = channel.config
- frozen = channel.frozen
- extraData = channel.extraData
- }
-
- /// A channel id.
- public let id: String
- /// A channel type + id.
- public let cid: ChannelId
- /// A channel type.
- public let type: ChannelType
- /// A channel name.
- public let name: String?
- /// An image of the channel.
- public let imageURL: URL?
- /// The last message date.
- public let lastMessageDate: Date?
- /// A channel created date.
- public let created: Date
- /// A channel deleted date.
- public let deleted: Date?
- /// A creator of the channel.
- public let createdBy: User?
- /// A config.
- public let config: Channel.Config
- /// Checks if the channel is frozen.
- public let frozen: Bool
- /// A list of user ids of the channel members.
- public let members = Set()
- /// An extra data for the channel.
- public let extraData: Codable?
-}
+//public struct MessageWrapper: Encodable {
+// private enum CodingKeys: String, CodingKey {
+// case id
+// case channel
+// case type
+// case user
+// case created = "created_at"
+// case updated = "updated_at"
+// case text
+// case command
+// case args
+// case attachments
+// case parentId = "parent_id"
+// case showReplyInChannel = "show_in_channel"
+// case mentionedUsers = "mentioned_users"
+// }
+//
+// init(channel: Channel, message: Message) {
+// id = message.id
+// type = message.type
+// user = message.user
+// created = message.created
+// updated = message.updated
+// text = message.text
+// command = message.command
+// args = message.args
+// attachments = message.attachments
+// parentId = message.parentId
+// showReplyInChannel = message.showReplyInChannel
+// mentionedUsers = message.mentionedUsers
+// extraData = message.extraData
+// self.channel = ChannelWrapper(channel: channel)
+// }
+//
+// /// A message id.
+// public let id: String
+// /// The channel cid.
+// public let channel: ChannelWrapper?
+// /// A message type (see `MessageType`).
+// public let type: MessageType
+// /// A user (see `User`).
+// public let user: User
+// /// A created date.
+// public let created: Date
+// /// A updated date.
+// public let updated: Date
+// /// A text.
+// public let text: String
+// /// A used command name.
+// public let command: String?
+// /// A used command args.
+// public let args: String?
+// /// Attachments (see `Attachment`).
+// public let attachments: [Attachment]
+// /// A parent message id.
+// public let parentId: String?
+// /// Check if this reply message needs to show in the channel.
+// public let showReplyInChannel: Bool
+// /// Mentioned users (see `User`).
+// public let mentionedUsers: [User]
+// /// An extra data for the message.
+// public let extraData: Codable?
+//}
+//
+//public struct ChannelWrapper: Encodable {
+// /// Coding keys for the encoding.
+// private enum CodingKeys: String, CodingKey {
+// case id
+// case cid
+// case type
+// case name
+// case imageURL = "image"
+// case members
+// case lastMessageDate = "last_message_at"
+// case createdBy = "created_by"
+// case created = "created_at"
+// case deleted = "deleted_at"
+// case frozen
+// }
+//
+// init(channel: Channel) {
+// id = channel.id
+// cid = channel.cid
+// type = channel.type
+// name = channel.name
+// imageURL = channel.imageURL
+// lastMessageDate = channel.lastMessageDate
+// created = channel.created
+// deleted = channel.deleted
+// createdBy = channel.createdBy
+// config = channel.config
+// frozen = channel.frozen
+// extraData = channel.extraData
+// }
+//
+// /// A channel id.
+// public let id: String
+// /// A channel type + id.
+// public let cid: ChannelId
+// /// A channel type.
+// public let type: ChannelType
+// /// A channel name.
+// public let name: String?
+// /// An image of the channel.
+// public let imageURL: URL?
+// /// The last message date.
+// public let lastMessageDate: Date?
+// /// A channel created date.
+// public let created: Date
+// /// A channel deleted date.
+// public let deleted: Date?
+// /// A creator of the channel.
+// public let createdBy: User?
+// /// A config.
+// public let config: Channel.Config
+// /// Checks if the channel is frozen.
+// public let frozen: Bool
+// /// A list of user ids of the channel members.
+// public let members = Set()
+// /// An extra data for the channel.
+// public let extraData: Codable?
+//}
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 9e328189..63861e27 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -10,81 +10,34 @@ project 'Runner', {
'Release' => :release,
}
-def parse_KV_file(file, separator='=')
- file_abs_path = File.expand_path(file)
- if !File.exists? file_abs_path
- return [];
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
- generated_key_values = {}
- skip_line_start_symbols = ["#", "/"]
- File.foreach(file_abs_path) do |line|
- next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
- plugin = line.split(pattern=separator)
- if plugin.length == 2
- podname = plugin[0].strip()
- path = plugin[1].strip()
- podpath = File.expand_path("#{path}", file_abs_path)
- generated_key_values[podname] = podpath
- else
- puts "Invalid plugin specification: #{line}"
- end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
end
- generated_key_values
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
target 'Runner' do
use_frameworks!
use_modular_headers!
-
- # Flutter Pod
- copied_flutter_dir = File.join(__dir__, 'Flutter')
- copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
- copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
- unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
- # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
- # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
- # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
-
- generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
- unless File.exist?(generated_xcode_build_settings_path)
- raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
- end
- generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
- cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
-
- unless File.exist?(copied_framework_path)
- FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
- end
- unless File.exist?(copied_podspec_path)
- FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
- end
- end
-
- # Keep pod path relative so it can be checked into Podfile.lock.
- pod 'Flutter', :path => 'Flutter'
- pod 'StreamChatClient', '~> 2.0.0'
- # Plugin Pods
-
- # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
- # referring to absolute paths on developers' machines.
- system('rm -rf .symlinks')
- system('mkdir -p .symlinks/plugins')
- plugin_pods = parse_KV_file('../.flutter-plugins')
- plugin_pods.each do |name, path|
- symlink = File.join('.symlinks', 'plugins', name)
- File.symlink(path, symlink)
- pod name, :path => File.join(symlink, 'ios')
- end
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
-# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
-install! 'cocoapods', :disable_input_output_paths => true
+pod 'StreamChatClient'
post_install do |installer|
installer.pods_project.targets.each do |target|
- target.build_configurations.each do |config|
- config.build_settings['ENABLE_BITCODE'] = 'NO'
- end
+ flutter_additional_ios_build_settings(target)
end
end
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index de801ca0..28b42300 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -33,53 +33,49 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- - Firebase/Core (6.28.1):
+ - Firebase/CoreOnly (6.26.0):
+ - FirebaseCore (= 6.7.2)
+ - Firebase/Messaging (6.26.0):
- Firebase/CoreOnly
- - FirebaseAnalytics (= 6.6.2)
- - Firebase/CoreOnly (6.28.1):
- - FirebaseCore (= 6.9.1)
- - Firebase/Messaging (6.28.1):
- - Firebase/CoreOnly
- - FirebaseMessaging (~> 4.6.0)
- - firebase_messaging (0.0.1):
- - Firebase/Core
- - Firebase/Messaging
+ - FirebaseMessaging (~> 4.4.1)
+ - firebase_core (0.5.0):
+ - Firebase/CoreOnly (~> 6.26.0)
- Flutter
- - FirebaseAnalytics (6.6.2):
- - FirebaseCore (~> 6.8)
- - FirebaseInstallations (~> 1.4)
- - GoogleAppMeasurement (= 6.6.2)
- - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- - GoogleUtilities/MethodSwizzler (~> 6.0)
- - GoogleUtilities/Network (~> 6.0)
- - "GoogleUtilities/NSData+zlib (~> 6.0)"
- - nanopb (~> 1.30905.0)
- - FirebaseCore (6.9.1):
+ - firebase_messaging (7.0.2):
+ - Firebase/CoreOnly (~> 6.26.0)
+ - Firebase/Messaging (~> 6.26.0)
+ - firebase_core
+ - Flutter
+ - FirebaseAnalyticsInterop (1.5.0)
+ - FirebaseCore (6.7.2):
- FirebaseCoreDiagnostics (~> 1.3)
+ - FirebaseCoreDiagnosticsInterop (~> 1.2)
+ - GoogleUtilities/Environment (~> 6.5)
+ - GoogleUtilities/Logger (~> 6.5)
+ - FirebaseCoreDiagnostics (1.7.0):
+ - GoogleDataTransport (~> 7.4)
- GoogleUtilities/Environment (~> 6.7)
- GoogleUtilities/Logger (~> 6.7)
- - FirebaseCoreDiagnostics (1.5.0):
- - GoogleDataTransport (~> 7.0)
- - GoogleUtilities/Environment (~> 6.7)
- - GoogleUtilities/Logger (~> 6.7)
- - nanopb (~> 1.30905.0)
- - FirebaseInstallations (1.5.0):
- - FirebaseCore (~> 6.8)
- - GoogleUtilities/Environment (~> 6.7)
- - GoogleUtilities/UserDefaults (~> 6.7)
+ - nanopb (~> 1.30906.0)
+ - FirebaseCoreDiagnosticsInterop (1.2.0)
+ - FirebaseInstallations (1.3.0):
+ - FirebaseCore (~> 6.6)
+ - GoogleUtilities/Environment (~> 6.6)
+ - GoogleUtilities/UserDefaults (~> 6.6)
- PromisesObjC (~> 1.2)
- - FirebaseInstanceID (4.5.0):
- - FirebaseCore (~> 6.8)
+ - FirebaseInstanceID (4.3.4):
+ - FirebaseCore (~> 6.6)
- FirebaseInstallations (~> 1.0)
- - GoogleUtilities/Environment (~> 6.7)
- - GoogleUtilities/UserDefaults (~> 6.7)
- - FirebaseMessaging (4.6.0):
- - FirebaseCore (~> 6.8)
+ - GoogleUtilities/Environment (~> 6.5)
+ - GoogleUtilities/UserDefaults (~> 6.5)
+ - FirebaseMessaging (4.4.1):
+ - FirebaseAnalyticsInterop (~> 1.5)
+ - FirebaseCore (~> 6.6)
- FirebaseInstanceID (~> 4.3)
- - GoogleUtilities/AppDelegateSwizzler (~> 6.7)
- - GoogleUtilities/Environment (~> 6.7)
- - GoogleUtilities/Reachability (~> 6.7)
- - GoogleUtilities/UserDefaults (~> 6.7)
+ - GoogleUtilities/AppDelegateSwizzler (~> 6.5)
+ - GoogleUtilities/Environment (~> 6.5)
+ - GoogleUtilities/Reachability (~> 6.5)
+ - GoogleUtilities/UserDefaults (~> 6.5)
- Protobuf (>= 3.9.2, ~> 3.9)
- Flutter (1.0.0)
- flutter_apns (0.0.1):
@@ -88,117 +84,92 @@ PODS:
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- - flutter_plugin_android_lifecycle (0.0.1):
- - Flutter
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- - GoogleAppMeasurement (6.6.2):
- - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- - GoogleUtilities/MethodSwizzler (~> 6.0)
- - GoogleUtilities/Network (~> 6.0)
- - "GoogleUtilities/NSData+zlib (~> 6.0)"
- - nanopb (~> 1.30905.0)
- - GoogleDataTransport (7.0.0):
- - nanopb (~> 1.30905.0)
- - GoogleUtilities/AppDelegateSwizzler (6.7.1):
+ - GoogleDataTransport (7.4.0):
+ - nanopb (~> 1.30906.0)
+ - GoogleUtilities/AppDelegateSwizzler (6.7.2):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- - GoogleUtilities/Environment (6.7.1):
+ - GoogleUtilities/Environment (6.7.2):
- PromisesObjC (~> 1.2)
- - GoogleUtilities/Logger (6.7.1):
+ - GoogleUtilities/Logger (6.7.2):
- GoogleUtilities/Environment
- - GoogleUtilities/MethodSwizzler (6.7.1):
- - GoogleUtilities/Logger
- - GoogleUtilities/Network (6.7.1):
+ - GoogleUtilities/Network (6.7.2):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- - "GoogleUtilities/NSData+zlib (6.7.1)"
- - GoogleUtilities/Reachability (6.7.1):
+ - "GoogleUtilities/NSData+zlib (6.7.2)"
+ - GoogleUtilities/Reachability (6.7.2):
- GoogleUtilities/Logger
- - GoogleUtilities/UserDefaults (6.7.1):
+ - GoogleUtilities/UserDefaults (6.7.2):
- GoogleUtilities/Logger
- - GzipSwift (5.1.1)
- image_picker (0.0.1):
- Flutter
- - moor_ffi (0.0.1):
- - Flutter
- - nanopb (1.30905.0):
- - nanopb/decode (= 1.30905.0)
- - nanopb/encode (= 1.30905.0)
- - nanopb/decode (1.30905.0)
- - nanopb/encode (1.30905.0)
+ - nanopb (1.30906.0):
+ - nanopb/decode (= 1.30906.0)
+ - nanopb/encode (= 1.30906.0)
+ - nanopb/decode (1.30906.0)
+ - nanopb/encode (1.30906.0)
- path_provider (0.0.1):
- Flutter
- - path_provider_linux (0.0.1):
- - Flutter
- - path_provider_macos (0.0.1):
- - Flutter
- - PromisesObjC (1.2.9)
- - Protobuf (3.12.0)
- - ReachabilitySwift (5.0.0)
- - SDWebImage (5.8.4):
- - SDWebImage/Core (= 5.8.4)
- - SDWebImage/Core (5.8.4)
+ - PromisesObjC (1.2.10)
+ - Protobuf (3.13.0)
+ - SDWebImage (5.9.2):
+ - SDWebImage/Core (= 5.9.2)
+ - SDWebImage/Core (5.9.2)
- shared_preferences (0.0.1):
- Flutter
- - shared_preferences_linux (0.0.1):
- - Flutter
- - shared_preferences_macos (0.0.1):
- - Flutter
- - shared_preferences_web (0.0.1):
- - Flutter
- sqflite (0.0.1):
- Flutter
- FMDB (~> 2.7.2)
- - Starscream (3.1.1)
- - StreamChatClient (2.0.1):
- - GzipSwift (~> 5.1)
- - ReachabilitySwift (~> 5.0)
- - Starscream (~> 3.1)
+ - sqlite3 (3.32.3):
+ - sqlite3/common (= 3.32.3)
+ - sqlite3/common (3.32.3)
+ - sqlite3/fts5 (3.32.3):
+ - sqlite3/common
+ - sqlite3/json1 (3.32.3):
+ - sqlite3/common
+ - sqlite3/perf-threadsafe (3.32.3):
+ - sqlite3/common
+ - sqlite3/rtree (3.32.3):
+ - sqlite3/common
+ - sqlite3_flutter_libs (0.0.1):
+ - Flutter
+ - sqlite3 (~> 3.32.3)
+ - sqlite3/fts5
+ - sqlite3/json1
+ - sqlite3/perf-threadsafe
+ - sqlite3/rtree
+ - Starscream (4.0.4)
+ - StreamChatClient (2.4.0):
+ - Starscream (~> 4.0)
- SwiftyGif (5.3.0)
- url_launcher (0.0.1):
- Flutter
- - url_launcher_linux (0.0.1):
- - Flutter
- - url_launcher_macos (0.0.1):
- - Flutter
- - url_launcher_web (0.0.1):
- - Flutter
- video_player (0.0.1):
- Flutter
- - video_player_web (0.0.1):
- - Flutter
- wakelock (0.0.1):
- Flutter
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
+ - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_apns (from `.symlinks/plugins/flutter_apns/ios`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`)
- image_picker (from `.symlinks/plugins/image_picker/ios`)
- - moor_ffi (from `.symlinks/plugins/moor_ffi/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`)
- - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- - shared_preferences_linux (from `.symlinks/plugins/shared_preferences_linux/ios`)
- - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`)
- - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- - StreamChatClient (~> 2.0.0)
+ - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
+ - StreamChatClient
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- - url_launcher_linux (from `.symlinks/plugins/url_launcher_linux/ios`)
- - url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`)
- - url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`)
- - video_player_web (from `.symlinks/plugins/video_player_web/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
SPEC REPOS:
@@ -206,22 +177,21 @@ SPEC REPOS:
- DKImagePickerController
- DKPhotoGallery
- Firebase
- - FirebaseAnalytics
+ - FirebaseAnalyticsInterop
- FirebaseCore
- FirebaseCoreDiagnostics
+ - FirebaseCoreDiagnosticsInterop
- FirebaseInstallations
- FirebaseInstanceID
- FirebaseMessaging
- FMDB
- - GoogleAppMeasurement
- GoogleDataTransport
- GoogleUtilities
- - GzipSwift
- nanopb
- PromisesObjC
- Protobuf
- - ReachabilitySwift
- SDWebImage
+ - sqlite3
- Starscream
- StreamChatClient
- SwiftyGif
@@ -229,6 +199,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
+ firebase_core:
+ :path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
@@ -239,40 +211,20 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
- flutter_plugin_android_lifecycle:
- :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios"
image_picker:
:path: ".symlinks/plugins/image_picker/ios"
- moor_ffi:
- :path: ".symlinks/plugins/moor_ffi/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
- path_provider_linux:
- :path: ".symlinks/plugins/path_provider_linux/ios"
- path_provider_macos:
- :path: ".symlinks/plugins/path_provider_macos/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
- shared_preferences_linux:
- :path: ".symlinks/plugins/shared_preferences_linux/ios"
- shared_preferences_macos:
- :path: ".symlinks/plugins/shared_preferences_macos/ios"
- shared_preferences_web:
- :path: ".symlinks/plugins/shared_preferences_web/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
+ sqlite3_flutter_libs:
+ :path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
- url_launcher_linux:
- :path: ".symlinks/plugins/url_launcher_linux/ios"
- url_launcher_macos:
- :path: ".symlinks/plugins/url_launcher_macos/ios"
- url_launcher_web:
- :path: ".symlinks/plugins/url_launcher_web/ios"
video_player:
:path: ".symlinks/plugins/video_player/ios"
- video_player_web:
- :path: ".symlinks/plugins/video_player_web/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
@@ -280,50 +232,40 @@ SPEC CHECKSUMS:
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
- Firebase: ed042590caa0029392257529a8003c25ee82bc18
- firebase_messaging: 21344b3b3a7d9d325d63a70e3750c0c798fe1e03
- FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
- FirebaseCore: 687b8e6a0a4337b898a6326d68254c2f80c143af
- FirebaseCoreDiagnostics: 7535fe695737f8c5b350584292a70b7f8ff0357b
- FirebaseInstallations: 3c520c951305cbf9ca54eb891ff9e6d1fd384881
- FirebaseInstanceID: 358d5cb393d2750a745569ede06827c35aea530b
- FirebaseMessaging: bdd4d573eab37ebee29bad4e7c4b0ef18fa1a952
+ Firebase: 7cf5f9c67f03cb3b606d1d6535286e1080e57eb6
+ firebase_core: 3134fe79d257d430f163b558caf52a10a87efe8a
+ firebase_messaging: 2844c37f9ce87c0904b38fe435223161b1a71528
+ FirebaseAnalyticsInterop: 3f86269c38ae41f47afeb43ebf32a001f58fcdae
+ FirebaseCore: f42e5e5f382cdcf6b617ed737bf6c871a6947b17
+ FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
+ FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850
+ FirebaseInstallations: 6f5f680e65dc374397a483c32d1799ba822a395b
+ FirebaseInstanceID: cef67c4967c7cecb56ea65d8acbb4834825c587b
+ FirebaseMessaging: 29543feb343b09546ab3aa04d008ee8595b43c44
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
flutter_apns: f516b118e423fe7c0a38771180549c4d6cb67c2f
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186
- flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
- GoogleAppMeasurement: 8cd1f289d60e629cf16ab03363b9e89c776b9651
- GoogleDataTransport: 8a40cb194ad242b6f6dfe72c14fe40fc67c4dcd7
- GoogleUtilities: e121a3867449ce16b0e35ddf1797ea7a389ffdf2
- GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
+ GoogleDataTransport: b7f406340a291370045a270c599e53c6fa6ec20f
+ GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09
- moor_ffi: d66c9470c18e9cb333423bbcb493c105c6c774c6
- nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
+ nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
- path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4
- path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
- PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
- Protobuf: 2793fcd0622a00b546c60e7cbbcc493e043e9bb9
- ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
- SDWebImage: cf6922231e95550934da2ada0f20f2becf2ceba9
+ PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151
+ Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
+ SDWebImage: 0b42b8719ab0c5257177d5894306e8a336b21cbb
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
- shared_preferences_linux: afefbfe8d921e207f01ede8b60373d9e3b566b78
- shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087
- shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9
sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0
- Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
- StreamChatClient: 91b0f585e7dc92ade58e657daffafb16d485b2a6
+ sqlite3: 8f7d2078ae27778699a622a94b853285793422a2
+ sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f
+ Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
+ StreamChatClient: 8c83a141e753e45fa096ff56d4b782d59e46f251
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
- url_launcher_linux: ac237cb7a8058736e4aae38bdbcc748a4b394cc0
- url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313
- url_launcher_web: e5527357f037c87560776e36436bf2b0288b965c
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
- video_player_web: da8cadb8274ed4f8dbee8d7171b420dedd437ce7
wakelock: 0d4a70faf8950410735e3f61fb15d517c8a6efc4
-PODFILE CHECKSUM: aaaddd91b568e9a010ff3278737eb06bc017bd6e
+PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184
COCOAPODS: 1.8.4
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 5405840a..db80aaed 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -308,9 +308,60 @@
files = (
);
inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
+ "${BUILT_PRODUCTS_DIR}/Starscream-framework/Starscream.framework",
+ "${BUILT_PRODUCTS_DIR}/StreamChatClient-framework/StreamChatClient.framework",
+ "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
+ "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
+ "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
+ "${PODS_ROOT}/../Flutter/Flutter.framework",
+ "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
+ "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
+ "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework",
+ "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
+ "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
+ "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
+ "${BUILT_PRODUCTS_DIR}/flutter_apns/flutter_apns.framework",
+ "${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework",
+ "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
+ "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework",
+ "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
+ "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
+ "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework",
+ "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
+ "${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework",
+ "${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework",
+ "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
+ "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework",
+ "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/StreamChatClient.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_apns.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_keyboard_visibility.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -489,7 +540,6 @@
};
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -573,7 +623,6 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -629,7 +678,6 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
diff --git a/example/lib/main.dart b/example/lib/main.dart
index a8d4f813..0363d6d3 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -110,6 +110,14 @@ class ChannelListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
+ floatingActionButton: FloatingActionButton(
+ child: Icon(Icons.add),
+ onPressed: () {
+ Navigator.of(context).push(MaterialPageRoute(builder: (context) {
+ return CreateChannelPage();
+ }));
+ },
+ ),
body: ChannelsBloc(
child: ChannelListView(
swipeToAction: true,
@@ -204,3 +212,189 @@ class ThreadPage extends StatelessWidget {
);
}
}
+
+class CreateChannelPage extends StatefulWidget {
+ @override
+ _CreateChannelPageState createState() => _CreateChannelPageState();
+}
+
+class _CreateChannelPageState extends State {
+ final ScrollController _scrollController = ScrollController();
+ Client client;
+ List users = [];
+ List selectedUsers = [];
+ int offset = 0;
+ bool loading = false;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ elevation: 0,
+ backgroundColor: Colors.transparent,
+ title: Text(
+ 'Create a channel',
+ style: Theme.of(context).textTheme.headline6,
+ ),
+ ),
+ floatingActionButton:
+ selectedUsers.isNotEmpty ? _buildFAB(context) : SizedBox(),
+ body: _buildListView(),
+ );
+ }
+
+ ListView _buildListView() {
+ return ListView.builder(
+ controller: _scrollController,
+ itemBuilder: _itemBuilder,
+ itemCount: users.length,
+ );
+ }
+
+ Widget _itemBuilder(context, i) {
+ final user = users[i];
+ return ListTile(
+ onLongPress: () {
+ _selectUser(user);
+ },
+ selected: selectedUsers.contains(user),
+ onTap: () {
+ if (selectedUsers.isNotEmpty) {
+ return _selectUser(user);
+ }
+ _createChannel(context, [user]);
+ },
+ leading: UserAvatar(
+ user: user,
+ ),
+ title: Text(user.name),
+ );
+ }
+
+ Widget _buildFAB(BuildContext context) {
+ return FloatingActionButton(
+ child: Icon(Icons.done),
+ onPressed: () async {
+ String name;
+ if (selectedUsers.length > 1) {
+ name = await _showEnterNameDialog(context);
+ if (name?.isNotEmpty != true) {
+ return;
+ }
+ }
+
+ _createChannel(context, selectedUsers, name);
+ },
+ );
+ }
+
+ Future _showEnterNameDialog(BuildContext context) {
+ final controller = TextEditingController();
+ return showDialog(
+ context: context,
+ builder: (context) => SimpleDialog(
+ contentPadding: const EdgeInsets.all(16),
+ title: Text('Enter a name for the channel'),
+ children: [
+ TextField(
+ controller: controller,
+ decoration: InputDecoration(
+ border: OutlineInputBorder(),
+ ),
+ ),
+ ButtonBar(
+ children: [
+ FlatButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text('Cancel'),
+ ),
+ FlatButton(
+ onPressed: () => Navigator.pop(context, controller.text),
+ child: Text('Ok'),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ Future _createChannel(
+ BuildContext context,
+ List users, [
+ String name,
+ ]) async {
+ final channel = client.channel('messaging', extraData: {
+ 'members': [
+ client.state.user.id,
+ ...users.map((e) => e.id),
+ ],
+ if (name != null) 'name': name,
+ });
+ await channel.watch();
+ Navigator.pushReplacement(
+ context,
+ MaterialPageRoute(
+ builder: (context) {
+ return StreamChannel(
+ child: ChannelPage(),
+ channel: channel,
+ );
+ },
+ ),
+ );
+ }
+
+ void _selectUser(User user) {
+ if (!selectedUsers.contains(user)) {
+ setState(() {
+ selectedUsers.add(user);
+ });
+ } else {
+ setState(() {
+ selectedUsers.remove(user);
+ });
+ }
+ }
+
+ @override
+ void initState() {
+ super.initState();
+
+ client = StreamChat.of(context).client;
+
+ _scrollController.addListener(() async {
+ if (!loading &&
+ _scrollController.offset >=
+ _scrollController.position.maxScrollExtent - 100) {
+ offset += 25;
+ await _queryUsers();
+ }
+ });
+
+ _queryUsers();
+ }
+
+ Future _queryUsers() {
+ loading = true;
+ return client.queryUsers(
+ pagination: PaginationParams(
+ limit: 25,
+ offset: offset,
+ ),
+ sort: [
+ SortOption(
+ 'name',
+ direction: SortOption.ASC,
+ ),
+ ],
+ ).then((value) {
+ setState(() {
+ users = [
+ ...users,
+ ...value.users,
+ ];
+ });
+ }).whenComplete(() => loading = false);
+ }
+}
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 7a09df86..3201f649 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -1,7 +1,7 @@
name: example
description: A new Flutter project.
-version: 1.0.13+15
+version: 1.0.14+16
environment:
sdk: ">=2.2.2 <3.0.0"
diff --git a/lib/src/channel_image.dart b/lib/src/channel_image.dart
index f18f7a0a..c3d76202 100644
--- a/lib/src/channel_image.dart
+++ b/lib/src/channel_image.dart
@@ -78,7 +78,7 @@ class ChannelImage extends StatelessWidget {
String image;
if (snapshot.data?.containsKey('image') == true) {
image = snapshot.data['image'];
- } else if (channel.state.members.length == 2) {
+ } else if (channel.state.members?.length == 2) {
final otherMember = channel.state.members
.firstWhere((member) => member.user.id != streamChat.user.id);
return StreamBuilder(
diff --git a/lib/src/channel_list_view.dart b/lib/src/channel_list_view.dart
index c3f31e56..eaa44398 100644
--- a/lib/src/channel_list_view.dart
+++ b/lib/src/channel_list_view.dart
@@ -66,6 +66,7 @@ class ChannelListView extends StatefulWidget {
this.errorBuilder,
this.onImageTap,
this.swipeToAction = false,
+ this.pullToRefresh = true,
}) : super(key: key);
/// The builder that will be used in case of error
@@ -114,6 +115,9 @@ class ChannelListView extends StatefulWidget {
/// The function called when the image is tapped
final Function(Channel) onImageTap;
+ /// Set it to false to disable the pull-to-refresh widget
+ final bool pullToRefresh;
+
@override
_ChannelListViewState createState() => _ChannelListViewState();
}
@@ -124,105 +128,146 @@ class _ChannelListViewState extends State
@override
Widget build(BuildContext context) {
- final channelsProvider = ChannelsBloc.of(context);
+ final channelsBloc = ChannelsBloc.of(context);
+
+ if (!widget.pullToRefresh) {
+ return _buildListView(channelsBloc);
+ }
return RefreshIndicator(
onRefresh: () async {
- return channelsProvider.queryChannels(
+ return channelsBloc.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
options: widget.options,
);
},
- child: StreamBuilder>(
- stream: channelsProvider.channelsStream,
- builder: (context, snapshot) {
- if (snapshot.hasError) {
- if (snapshot.error is Error) {
- print((snapshot.error as Error).stackTrace);
- }
+ child: _buildListView(channelsBloc),
+ );
+ }
- if (widget.errorBuilder != null) {
- return widget.errorBuilder(snapshot.error);
- }
+ StreamBuilder> _buildListView(
+ ChannelsBlocState channelsBlocState,
+ ) {
+ return StreamBuilder>(
+ stream: channelsBlocState.channelsStream,
+ builder: (context, snapshot) {
+ if (snapshot.hasError) {
+ if (snapshot.error is Error) {
+ print((snapshot.error as Error).stackTrace);
+ }
- var message = snapshot.error.toString();
- if (snapshot.error is DioError) {
- final dioError = snapshot.error as DioError;
- if (dioError.type == DioErrorType.RESPONSE) {
- message = dioError.message;
- } else {
- message = 'Check your connection and retry';
- }
+ if (widget.errorBuilder != null) {
+ return widget.errorBuilder(snapshot.error);
+ }
+
+ var message = snapshot.error.toString();
+ if (snapshot.error is DioError) {
+ final dioError = snapshot.error as DioError;
+ if (dioError.type == DioErrorType.RESPONSE) {
+ message = dioError.message;
+ } else {
+ message = 'Check your connection and retry';
}
- return Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text.rich(
- TextSpan(
- children: [
- WidgetSpan(
- child: Padding(
- padding: const EdgeInsets.only(
- right: 2.0,
- ),
- child: Icon(Icons.error_outline),
+ }
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text.rich(
+ TextSpan(
+ children: [
+ WidgetSpan(
+ child: Padding(
+ padding: const EdgeInsets.only(
+ right: 2.0,
),
+ child: Icon(Icons.error_outline),
),
- TextSpan(text: 'Error loading channels'),
- ],
- ),
- style: Theme.of(context).textTheme.headline6,
+ ),
+ TextSpan(text: 'Error loading channels'),
+ ],
),
- Padding(
- padding: const EdgeInsets.only(
- top: 16.0,
- ),
- child: Text(message),
+ style: Theme.of(context).textTheme.headline6,
+ ),
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 16.0,
),
- FlatButton(
- onPressed: () {
- channelsProvider.queryChannels(
- filter: widget.filter,
- sortOptions: widget.sort,
- paginationParams: widget.pagination,
- options: widget.options,
- );
- },
- child: Text('Retry'),
- ),
- ],
- ),
- );
- }
-
- if (!snapshot.hasData) {
- return Center(
- child: CircularProgressIndicator(),
- );
- }
-
- final channels = snapshot.data;
- return ListView.custom(
- physics: AlwaysScrollableScrollPhysics(),
- controller: _scrollController,
- childrenDelegate: SliverChildBuilderDelegate(
- (context, i) {
- return _itemBuilder(context, i, channels);
- },
- childCount: (channels.length * 2) + 1,
- findChildIndexCallback: (key) {
- final ValueKey valueKey = key;
- final index = channels.indexWhere(
- (channel) => 'CHANNEL-${channel.id}' == valueKey.value);
- return index != -1 ? (index * 2) : null;
- },
+ child: Text(message),
+ ),
+ FlatButton(
+ onPressed: () {
+ channelsBlocState.queryChannels(
+ filter: widget.filter,
+ sortOptions: widget.sort,
+ paginationParams: widget.pagination,
+ options: widget.options,
+ );
+ },
+ child: Text('Retry'),
+ ),
+ ],
),
);
- }),
- );
+ }
+
+ if (!snapshot.hasData) {
+ return LayoutBuilder(
+ builder: (context, viewportConstraints) {
+ return SingleChildScrollView(
+ physics: AlwaysScrollableScrollPhysics(),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: viewportConstraints.maxHeight,
+ ),
+ child: Center(
+ child: CircularProgressIndicator(),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ final channels = snapshot.data;
+
+ if (channels.isEmpty) {
+ return LayoutBuilder(
+ builder: (context, viewportConstraints) {
+ return SingleChildScrollView(
+ physics: AlwaysScrollableScrollPhysics(),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: viewportConstraints.maxHeight,
+ ),
+ child: Center(
+ child: Text('You have no channels currently'),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ return ListView.custom(
+ physics: AlwaysScrollableScrollPhysics(),
+ controller: _scrollController,
+ childrenDelegate: SliverChildBuilderDelegate(
+ (context, i) {
+ return _itemBuilder(context, i, channels);
+ },
+ childCount: (channels.length * 2) + 1,
+ findChildIndexCallback: (key) {
+ final ValueKey valueKey = key;
+ final index = channels.indexWhere(
+ (channel) => 'CHANNEL-${channel.id}' == valueKey.value);
+ return index != -1 ? (index * 2) : null;
+ },
+ ),
+ );
+ });
}
Widget _itemBuilder(context, int i, List channels) {
@@ -430,7 +475,7 @@ class _ChannelListViewState extends State
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination.copyWith(
- offset: channelsProvider.channels.length,
+ offset: channelsProvider.channels?.length ?? 0,
),
options: widget.options,
);
@@ -482,7 +527,7 @@ class _ChannelListViewState extends State
void didUpdateWidget(ChannelListView oldWidget) {
super.didUpdateWidget(oldWidget);
- if (widget.filter != oldWidget.filter ||
+ if (widget.filter?.toString() != oldWidget.filter?.toString() ||
widget.sort != oldWidget.sort ||
widget.pagination != oldWidget.pagination ||
widget.options != oldWidget.options) {
diff --git a/lib/src/channels_bloc.dart b/lib/src/channels_bloc.dart
index e2cf9cd9..df6f641b 100644
--- a/lib/src/channels_bloc.dart
+++ b/lib/src/channels_bloc.dart
@@ -11,7 +11,7 @@ class ChannelsBloc extends StatefulWidget {
/// The widget child
final Widget child;
- /// Set this to false to prevent channels to be brought to the top of the list when a new message arrives
+ /// Set this to true to prevent channels to be brought to the top of the list when a new message arrives
final bool lockChannelsOrder;
/// Instantiate a new ChannelsBloc
@@ -56,8 +56,7 @@ class ChannelsBlocState extends State
final BehaviorSubject _queryChannelsLoadingController =
BehaviorSubject.seeded(false);
- final BehaviorSubject> _channelsController =
- BehaviorSubject.seeded([]);
+ final BehaviorSubject> _channelsController = BehaviorSubject();
/// The stream notifying the state of queryChannel call
Stream get queryChannelsLoading =>
@@ -73,7 +72,10 @@ class ChannelsBlocState extends State
Map options,
bool onlyOffline = false,
}) async {
- if (_queryChannelsLoadingController.value == true) {
+ final client = StreamChat.of(context).client;
+
+ if (client.state?.user == null ||
+ _queryChannelsLoadingController.value == true) {
return;
}
_queryChannelsLoadingController.sink.add(true);
@@ -82,16 +84,15 @@ class ChannelsBlocState extends State
final clear = paginationParams == null ||
paginationParams.offset == null ||
paginationParams.offset == 0;
- final oldChannels = List.from(channels);
- StreamChat.of(context)
- .client
+ final oldChannels = List.from(channels ?? []);
+ client
.queryChannels(
- filter: filter,
- sort: sortOptions,
- options: options,
- paginationParams: paginationParams,
- onlyOffline: onlyOffline,
- )
+ filter: filter,
+ sort: sortOptions,
+ options: options,
+ paginationParams: paginationParams,
+ onlyOffline: onlyOffline,
+ )
.listen((channels) {
if (clear) {
_channelsController.add(channels);
@@ -123,23 +124,28 @@ class ChannelsBlocState extends State
_subscriptions.add(client.on(EventType.messageNew).listen((e) {
final newChannels = List.from(channels ?? []);
final index = newChannels.indexWhere((c) => c.cid == e.cid);
- if (index > 0) {
- final channel = newChannels.removeAt(index);
- newChannels.insert(0, channel);
- _channelsController.add(newChannels);
+ if (index > -1) {
+ if (index > 0) {
+ final channel = newChannels.removeAt(index);
+ newChannels.insert(0, channel);
+ }
} else {
final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid);
if (hiddenIndex > -1) {
newChannels.insert(0, _hiddenChannels[hiddenIndex]);
_hiddenChannels.removeAt(hiddenIndex);
- _channelsController.add(newChannels);
+ } else {
+ if (client.state.channels[e.cid] != null) {
+ newChannels.insert(0, client.state.channels[e.cid]);
+ }
}
}
+ _channelsController.add(newChannels);
}));
}
_subscriptions.add(client.on(EventType.channelHidden).listen((event) async {
- final newChannels = List.from(channels);
+ final newChannels = List.from(channels ?? []);
final channelIndex = newChannels.indexWhere((c) => c.cid == event.cid);
if (channelIndex > -1) {
final channel = newChannels.removeAt(channelIndex);
diff --git a/lib/src/full_screen_image.dart b/lib/src/full_screen_image.dart
index cc6a244a..76953b1a 100644
--- a/lib/src/full_screen_image.dart
+++ b/lib/src/full_screen_image.dart
@@ -15,12 +15,20 @@ class FullScreenImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return PhotoView(
- imageProvider: CachedNetworkImageProvider(url),
- maxScale: PhotoViewComputedScale.covered,
- minScale: PhotoViewComputedScale.contained,
- heroAttributes: PhotoViewHeroAttributes(
- tag: url,
+ return Scaffold(
+ appBar: AppBar(
+ backgroundColor: Colors.black,
+ iconTheme: IconThemeData(
+ color: Colors.white,
+ ),
+ ),
+ body: PhotoView(
+ imageProvider: CachedNetworkImageProvider(url),
+ maxScale: PhotoViewComputedScale.covered,
+ minScale: PhotoViewComputedScale.contained,
+ heroAttributes: PhotoViewHeroAttributes(
+ tag: url,
+ ),
),
);
}
diff --git a/lib/src/full_screen_video.dart b/lib/src/full_screen_video.dart
index 46325b07..98729243 100644
--- a/lib/src/full_screen_video.dart
+++ b/lib/src/full_screen_video.dart
@@ -26,6 +26,12 @@ class _FullScreenVideoState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
+ appBar: AppBar(
+ backgroundColor: Colors.black,
+ iconTheme: IconThemeData(
+ color: Colors.white,
+ ),
+ ),
body: Builder(
key: _scaffoldKey,
builder: (context) {
diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart
index 6358e39e..edc21f96 100644
--- a/lib/src/message_input.dart
+++ b/lib/src/message_input.dart
@@ -182,7 +182,7 @@ class MessageInputState extends State {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
- overflow: Overflow.visible,
+ clipBehavior: Clip.none,
children: [
_buildBorder(context),
Column(
@@ -738,7 +738,10 @@ class MessageInputState extends State {
} else if (fileType == DefaultAttachmentTypes.file) {
type = FileType.any;
}
- file = await FilePicker.getFile(type: type);
+ final res = await FilePicker.platform.pickFiles(type: type);
+ if (res?.files?.isNotEmpty == true) {
+ file = File(res.files.first.path);
+ }
}
setState(() {
@@ -909,9 +912,9 @@ class MessageInputState extends State {
);
}
- return sendingFuture.whenComplete(() {
+ return sendingFuture.then((resp) {
if (widget.onMessageSent != null) {
- widget.onMessageSent(message);
+ widget.onMessageSent(resp.message);
} else {
if (widget.editMessage != null) {
Navigator.pop(context);
diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart
index 14c8e20b..49394aa4 100644
--- a/lib/src/message_list_view.dart
+++ b/lib/src/message_list_view.dart
@@ -104,6 +104,7 @@ class MessageListView extends StatefulWidget {
this.onThreadTap,
this.dateDividerBuilder,
this.scrollPhysics = const AlwaysScrollableScrollPhysics(),
+ this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
}) : super(key: key);
/// Function used to build a custom message widget
@@ -128,6 +129,9 @@ class MessageListView extends StatefulWidget {
/// The ScrollPhysics used by the ListView
final ScrollPhysics scrollPhysics;
+ /// The [ScrollViewKeyboardDismissBehavior] used by the ListView
+ final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
+
@override
_MessageListViewState createState() => _MessageListViewState();
}
@@ -159,6 +163,7 @@ class _MessageListViewState extends State {
child: ListView.custom(
key: Key('messageListView'),
physics: widget.scrollPhysics,
+ keyboardDismissBehavior: widget.keyboardDismissBehavior,
controller: _scrollController,
reverse: true,
childrenDelegate: SliverChildBuilderDelegate(
diff --git a/lib/src/message_text.dart b/lib/src/message_text.dart
index 6ca99f6c..2ee6a6f8 100644
--- a/lib/src/message_text.dart
+++ b/lib/src/message_text.dart
@@ -11,10 +11,12 @@ class MessageText extends StatelessWidget {
@required this.message,
@required this.messageTheme,
this.onMentionTap,
+ this.onLinkTap,
}) : super(key: key);
final Message message;
final void Function(User) onMentionTap;
+ final void Function(String) onLinkTap;
final MessageTheme messageTheme;
@override
@@ -35,7 +37,11 @@ class MessageText extends StatelessWidget {
print('tap on ${mentionedUser.name}');
}
} else {
- launchURL(context, link);
+ if (onLinkTap != null) {
+ onLinkTap(link);
+ } else {
+ launchURL(context, link);
+ }
}
},
styleSheet: MarkdownStyleSheet.fromTheme(
diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart
index 5fea9fa0..841a2b26 100644
--- a/lib/src/message_widget.dart
+++ b/lib/src/message_widget.dart
@@ -101,6 +101,9 @@ class MessageWidget extends StatefulWidget {
/// The function called when tapping on UserAvatar
final void Function(User) onUserAvatarTap;
+ /// The function called when tapping on a link
+ final void Function(String) onLinkTap;
+
final List readList;
/// If true show the users username next to the timestamp of the message
@@ -132,6 +135,7 @@ class MessageWidget extends StatefulWidget {
this.showDeleteMessage = true,
this.showEditMessage = true,
this.onUserAvatarTap,
+ this.onLinkTap,
this.onMessageActions,
this.editMessageInputBuilder,
this.textBuilder,
@@ -814,6 +818,7 @@ class _MessageWidgetState extends State {
return widget.textBuilder != null
? widget.textBuilder(context, widget.message)
: MessageText(
+ onLinkTap: widget.onLinkTap,
message: widget.message,
onMentionTap: widget.onMentionTap,
messageTheme: widget.messageTheme,
diff --git a/lib/src/stream_chat.dart b/lib/src/stream_chat.dart
index ad6ba47c..dfc6095b 100644
--- a/lib/src/stream_chat.dart
+++ b/lib/src/stream_chat.dart
@@ -176,55 +176,58 @@ class StreamChatState extends State with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
- if (state == AppLifecycleState.paused) {
- if (client.showLocalNotification != null) {
- _newMessageSubscription = client
- .on(EventType.messageNew)
- .where((e) => e.user?.id != user.id)
- .where((e) => e.message.silent != true)
- .listen((event) async {
- var channel = client.state.channels[event.cid];
+ if (client.state?.user != null) {
+ if (state == AppLifecycleState.paused) {
+ if (client.showLocalNotification != null) {
+ _newMessageSubscription = client
+ .on(EventType.messageNew)
+ .where((e) => e.user?.id != user.id)
+ .where((e) => e.message.silent != true)
+ .listen((event) async {
+ var channel = client.state.channels[event.cid];
- if (channel == null) {
- channel = client.channel(
- event.type,
- id: event.cid.split(':')[1],
+ if (channel == null) {
+ channel = client.channel(
+ event.type,
+ id: event.cid.split(':')[1],
+ );
+ await channel.query();
+ }
+
+ client.showLocalNotification(
+ event.message,
+ ChannelModel(
+ id: channel.id,
+ createdAt: channel.createdAt,
+ extraData: channel.extraData,
+ type: channel.type,
+ memberCount: channel.memberCount,
+ frozen: channel.frozen,
+ cid: channel.cid,
+ deletedAt: channel.deletedAt,
+ config: channel.config,
+ createdBy: channel.createdBy,
+ updatedAt: channel.updatedAt,
+ lastMessageAt: channel.lastMessageAt,
+ ),
);
- await channel.query();
- }
-
- client.showLocalNotification(
- event.message,
- ChannelModel(
- id: channel.id,
- createdAt: channel.createdAt,
- extraData: channel.extraData,
- type: channel.type,
- memberCount: channel.memberCount,
- frozen: channel.frozen,
- cid: channel.cid,
- deletedAt: channel.deletedAt,
- config: channel.config,
- createdBy: channel.createdBy,
- updatedAt: channel.updatedAt,
- lastMessageAt: channel.lastMessageAt,
- ),
- );
- });
- _disconnectTimer = Timer(client.backgroundKeepAlive, () {
+ });
+ _disconnectTimer = Timer(client.backgroundKeepAlive, () {
+ client.disconnect();
+ });
+ } else {
client.disconnect();
- });
- } else {
- client.disconnect();
- }
- } else if (state == AppLifecycleState.resumed) {
- _newMessageSubscription?.cancel();
- if (_disconnectTimer?.isActive == true) {
- _disconnectTimer.cancel();
- } else {
- if (client.wsConnectionStatus.value == ConnectionStatus.disconnected) {
- NotificationService.handleIosMessageQueue(client);
- client.connect();
+ }
+ } else if (state == AppLifecycleState.resumed) {
+ _newMessageSubscription?.cancel();
+ if (_disconnectTimer?.isActive == true) {
+ _disconnectTimer.cancel();
+ } else {
+ if (client.wsConnectionStatus.value ==
+ ConnectionStatus.disconnected) {
+ NotificationService.handleIosMessageQueue(client);
+ client.connect();
+ }
}
}
}
diff --git a/lib/src/typing_indicator.dart b/lib/src/typing_indicator.dart
index 57427da6..69a16b98 100644
--- a/lib/src/typing_indicator.dart
+++ b/lib/src/typing_indicator.dart
@@ -34,7 +34,7 @@ class TypingIndicator extends StatelessWidget {
builder: (context, snapshot) {
return AnimatedSwitcher(
duration: Duration(milliseconds: 300),
- child: snapshot.data.isNotEmpty
+ child: snapshot.data?.isNotEmpty == true
? Align(
key: Key('typings'),
alignment: alignment,
diff --git a/pubspec.yaml b/pubspec.yaml
index 7012f360..accccade 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
name: stream_chat_flutter
homepage: https://github.com/GetStream/stream-chat-flutter
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
-version: 0.2.4
+version: 0.2.8+3
repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -20,11 +20,11 @@ dependencies:
url_launcher: ^5.4.11
video_player: ^0.10.11+1
chewie: ^0.9.10
- file_picker: ^1.12.0
+ file_picker: ^2.0.0
image_picker: ^0.6.7+2
flutter_keyboard_visibility: ^3.2.1
flutter_svg: ^0.18.0
- stream_chat: ^0.2.3+3
+ stream_chat: ^0.2.5+2
mime: ^0.9.6+3
visibility_detector: ^0.1.5
line_awesome_icons: ^1.0.4+2
@@ -38,3 +38,6 @@ flutter:
dev_dependencies:
pedantic: ^1.9.0
+ flutter_test:
+ sdk: flutter
+ mockito: ^4.1.1
diff --git a/test/src/channel_preview_test.dart b/test/src/channel_preview_test.dart
new file mode 100644
index 00000000..f5e0fbd0
--- /dev/null
+++ b/test/src/channel_preview_test.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+
+class MockClient extends Mock implements Client {}
+
+class MockChannel extends Mock implements Channel {}
+
+class MockChannelState extends Mock implements ChannelClientState {}
+
+void main() {
+ testWidgets(
+ 'it should show basic channel information',
+ (WidgetTester tester) async {
+ final client = MockClient();
+ final channel = MockChannel();
+ final channelState = MockChannelState();
+ final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
+
+ when(channel.lastMessageAt).thenReturn(lastMessageAt);
+ when(channel.state).thenReturn(channelState);
+ when(channel.extraData).thenReturn({
+ 'name': 'test name',
+ });
+ when(channelState.unreadCount).thenReturn(1);
+ when(channelState.lastMessage).thenReturn(Message(
+ text: 'hello',
+ ));
+
+ await tester.pumpWidget(MaterialApp(
+ home: StreamChat(
+ client: client,
+ child: StreamChannel(
+ channel: channel,
+ child: Scaffold(
+ body: ChannelPreview(
+ channel: channel,
+ ),
+ ),
+ ),
+ ),
+ ));
+
+ expect(find.text('22/06/2020'), findsOneWidget);
+ expect(find.text('test name'), findsOneWidget);
+ expect(find.text('1'), findsOneWidget);
+ expect(find.text('hello'), findsOneWidget);
+ expect(find.byType(ChannelImage), findsOneWidget);
+ },
+ );
+}