diff --git a/.github/workflows/scripts/install-tools.sh b/.github/workflows/scripts/install-tools.sh index 087cfecb..b83f55f0 100755 --- a/.github/workflows/scripts/install-tools.sh +++ b/.github/workflows/scripts/install-tools.sh @@ -1,6 +1,6 @@ #!/bin/bash -flutter pub global activate melos +flutter pub global activate melos 0.4.0-dev.2 echo "::add-path::$HOME/.pub-cache/bin" echo "::add-path::$GITHUB_WORKSPACE/_flutter/.pub-cache/bin" echo "::add-path::$GITHUB_WORKSPACE/_flutter/bin/cache/dart-sdk/bin" \ No newline at end of file diff --git a/melos.yaml b/melos.yaml index ea99c335..7e02909f 100644 --- a/melos.yaml +++ b/melos.yaml @@ -55,5 +55,5 @@ dev_dependencies: pedantic: 1.9.2 environment: - sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" \ No newline at end of file + sdk: ">=2.7.0 <3.0.0" + flutter: ">=1.22.4 <2.0.0" \ No newline at end of file diff --git a/packages/dart_client/CHANGELOG.md b/packages/dart_client/CHANGELOG.md index dbab1dcb..5c799a17 100644 --- a/packages/dart_client/CHANGELOG.md +++ b/packages/dart_client/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.24 + +- Create enum for push providers +- Add merge helper functions in `Message` and `ChannelModel` for easier data manipulation + ## 0.2.23+3 - Remove + notation from userAgent diff --git a/packages/dart_client/example/.gitignore b/packages/dart_client/example/.gitignore new file mode 100644 index 00000000..9d532b18 --- /dev/null +++ b/packages/dart_client/example/.gitignore @@ -0,0 +1,41 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/packages/dart_client/example/.metadata b/packages/dart_client/example/.metadata new file mode 100644 index 00000000..182cccaf --- /dev/null +++ b/packages/dart_client/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 78910062997c3a836feee883712c241a5fd22983 + channel: stable + +project_type: app diff --git a/packages/dart_client/example/README.md b/packages/dart_client/example/README.md new file mode 100644 index 00000000..11089159 --- /dev/null +++ b/packages/dart_client/example/README.md @@ -0,0 +1,2 @@ +# Stream Chat Dart Example +Please see `lib/` for example code. \ No newline at end of file diff --git a/packages/dart_client/example/android/.gitignore b/packages/dart_client/example/android/.gitignore new file mode 100644 index 00000000..0a741cb4 --- /dev/null +++ b/packages/dart_client/example/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/packages/dart_client/example/android/app/build.gradle b/packages/dart_client/example/android/app/build.gradle new file mode 100644 index 00000000..3932aa91 --- /dev/null +++ b/packages/dart_client/example/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 29 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.example" + minSdkVersion 16 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/packages/dart_client/example/android/app/src/debug/AndroidManifest.xml b/packages/dart_client/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..c208884f --- /dev/null +++ b/packages/dart_client/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/dart_client/example/android/app/src/main/AndroidManifest.xml b/packages/dart_client/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..55ca830c --- /dev/null +++ b/packages/dart_client/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/dart_client/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/dart_client/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 00000000..e793a000 --- /dev/null +++ b/packages/dart_client/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/packages/dart_client/example/android/app/src/main/res/drawable/launch_background.xml b/packages/dart_client/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/packages/dart_client/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/dart_client/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/dart_client/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..db77bb4b Binary files /dev/null and b/packages/dart_client/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/packages/dart_client/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/dart_client/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..17987b79 Binary files /dev/null and b/packages/dart_client/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/packages/dart_client/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/dart_client/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..09d43914 Binary files /dev/null and b/packages/dart_client/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/packages/dart_client/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/dart_client/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..d5f1c8d3 Binary files /dev/null and b/packages/dart_client/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/packages/dart_client/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/dart_client/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..4d6372ee Binary files /dev/null and b/packages/dart_client/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/packages/dart_client/example/android/app/src/main/res/values/styles.xml b/packages/dart_client/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..1f83a33f --- /dev/null +++ b/packages/dart_client/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/dart_client/example/android/app/src/profile/AndroidManifest.xml b/packages/dart_client/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..c208884f --- /dev/null +++ b/packages/dart_client/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/dart_client/example/android/build.gradle b/packages/dart_client/example/android/build.gradle new file mode 100644 index 00000000..3100ad2d --- /dev/null +++ b/packages/dart_client/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/dart_client/example/android/gradle.properties b/packages/dart_client/example/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/packages/dart_client/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/dart_client/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/dart_client/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..296b146b --- /dev/null +++ b/packages/dart_client/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/dart_client/example/android/settings.gradle b/packages/dart_client/example/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/packages/dart_client/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/dart_client/example/ios/.gitignore b/packages/dart_client/example/ios/.gitignore new file mode 100644 index 00000000..e96ef602 --- /dev/null +++ b/packages/dart_client/example/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/dart_client/example/ios/Flutter/AppFrameworkInfo.plist b/packages/dart_client/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..6b4c0f78 --- /dev/null +++ b/packages/dart_client/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/packages/dart_client/example/ios/Flutter/Debug.xcconfig b/packages/dart_client/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..e8efba11 --- /dev/null +++ b/packages/dart_client/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/dart_client/example/ios/Flutter/Release.xcconfig b/packages/dart_client/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..399e9340 --- /dev/null +++ b/packages/dart_client/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/dart_client/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/dart_client/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..a28140cf --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dart_client/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/dart_client/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/dart_client/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/dart_client/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/dart_client/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/dart_client/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/packages/dart_client/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/dart_client/example/ios/Runner/AppDelegate.swift b/packages/dart_client/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/packages/dart_client/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..dc9ada47 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..28c6bf03 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..f091b6b0 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cde1211 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..d0ef06e7 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..dcdc2306 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..c8f9ed8f Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..75b2d164 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..c4df70d3 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..6a84f41e Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..d0e1f585 Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/dart_client/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/dart_client/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dart_client/example/ios/Runner/Base.lproj/Main.storyboard b/packages/dart_client/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dart_client/example/ios/Runner/Info.plist b/packages/dart_client/example/ios/Runner/Info.plist new file mode 100644 index 00000000..a060db61 --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/dart_client/example/ios/Runner/Runner-Bridging-Header.h b/packages/dart_client/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/packages/dart_client/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/dart_client/example/lib/main.dart b/packages/dart_client/example/lib/main.dart new file mode 100644 index 00000000..8f68c100 --- /dev/null +++ b/packages/dart_client/example/lib/main.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat/stream_chat.dart'; + +Future main() async { + /// Create a new instance of [Client] passing the apikey obtained from your + /// project dashboard. + final client = Client('b67pax5b2wdq'); + + /// Set the current user. In a production scenario, this should be done using + /// a backend to generate a user token using our server SDK. + /// Please see the following for more information: + /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ + await client.setUser( + User( + id: 'cool-shadow-7', + extraData: { + 'image': + 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', + }, + ), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', + ); + + /// Creates a channel using the type `messaging` and `godevs`. + /// Channels are containers for holding messages between different members. To + /// learn more about channels and some of our predefined types, checkout our + /// our channel docs: https://getstream.io/chat/docs/initialize_channel/?language=dart + final channel = client.channel('messaging', id: 'godevs'); + + /// `.watch()` is used to create and listen to the channel for updates. If the + /// channel already exists, it will simply listen for new events. + await channel.watch(); + + runApp( + StreamExample( + client: client, + channel: channel, + ), + ); +} + +/// Example using Stream's Low Level Dart client. +class StreamExample extends StatelessWidget { + /// To initialize this example, an instance of [client] and [channel] is required. + const StreamExample({ + Key key, + @required this.client, + @required this.channel, + }) : super(key: key); + + /// Instance of [Client] we created earlier. This contains information about + /// our application and connection state. + final Client client; + + /// The channel we'd like to observe and participate. + final Channel channel; + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Stream Chat Dart Example', + home: HomeScreen(channel: channel), + ); + } +} + +/// Main screen of our application. The layout is comprised of an [AppBar] +/// containing the channel name and a [MessageView] displaying recent messages. +class HomeScreen extends StatelessWidget { + /// [HomeScreen] is constructed using the [Channel] we defined earlier. + const HomeScreen({Key key, @required this.channel}) : super(key: key); + + /// Channel object containing the [Channel.id] we'd like to observe. + final Channel channel; + + @override + Widget build(BuildContext context) { + final messages = channel.state.channelStateStream; + return Scaffold( + appBar: AppBar( + title: Text('Channel: ${channel.id}'), + ), + body: SafeArea( + child: StreamBuilder( + stream: messages, + builder: ( + BuildContext context, + AsyncSnapshot snapshot, + ) { + if (snapshot.hasData && snapshot.data != null) { + return MessageView( + messages: snapshot.data.messages.reversed.toList(), + channel: channel, + ); + } else if (snapshot.hasError) { + return const Center( + child: Text( + 'There was an error loading messages. Please see logs.', + ), + ); + } + return const Center( + child: SizedBox( + width: 100.0, + height: 100.0, + child: CircularProgressIndicator(), + ), + ); + }, + ), + ), + ); + } +} + +/// UI used to display a list of recent messages and a [TextField] for sending +/// new messages. +class MessageView extends StatefulWidget { + /// Message takes the latest list of messages and the current channel. + const MessageView({ + Key key, + @required this.messages, + @required this.channel, + }) : super(key: key); + + /// List of messages sent in the given channel. + final List messages; + + /// Current channel being observed. + final Channel channel; + + @override + _MessageViewState createState() => _MessageViewState(); +} + +class _MessageViewState extends State { + TextEditingController _controller; + ScrollController _scrollController; + + List get _messages => widget.messages; + + @override + void initState() { + super.initState(); + _controller = TextEditingController(); + _scrollController = ScrollController(); + } + + @override + void dispose() { + _controller.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + /// Convenience method for scrolling the list view when a new message is sent. + void _updateList() { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: _messages.length, + reverse: true, + itemBuilder: (BuildContext context, int index) { + final item = _messages[index]; + if (item.user.id == widget.channel.client.uid) { + return Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(item.text), + ), + ); + } else { + return Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(item.text), + ), + ); + } + }, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _controller, + decoration: const InputDecoration( + hintText: 'Enter your message', + ), + ), + ), + Material( + type: MaterialType.circle, + color: Colors.blue, + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () async { + // We can send a new message by calling `sendMessage` on + // the current channel. After sending a message, the + // TextField is cleared and the list view is scrolled + // to show the new item. + if (_controller.value.text.isNotEmpty) { + await widget.channel.sendMessage( + Message(text: _controller.value.text), + ); + _controller.clear(); + _updateList(); + } + }, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Center( + child: Icon( + Icons.send, + color: Colors.white, + ), + ), + ), + ), + ) + ], + ), + ) + ], + ); + } +} + +/// Helper extension for quickly retrieving the current user id from a [Client]. +extension on Client { + String get uid => state.user.id; +} diff --git a/packages/dart_client/example/pubspec.yaml b/packages/dart_client/example/pubspec.yaml new file mode 100644 index 00000000..2a8b7eb2 --- /dev/null +++ b/packages/dart_client/example/pubspec.yaml @@ -0,0 +1,21 @@ +name: example +description: A new Flutter project. + +publish_to: 'none' +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.0 + stream_chat: + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter +flutter: + uses-material-design: true \ No newline at end of file diff --git a/packages/dart_client/lib/src/api/channel.dart b/packages/dart_client/lib/src/api/channel.dart index abe31984..d8ef1a03 100644 --- a/packages/dart_client/lib/src/api/channel.dart +++ b/packages/dart_client/lib/src/api/channel.dart @@ -1108,30 +1108,7 @@ class ChannelClientState { final oldIndex = newMessages.indexWhere((m) => m.id == message.id); if (oldIndex != -1) { - newMessages[oldIndex] = newMessages[oldIndex].copyWith( - text: message.text, - type: message.type, - attachments: message.attachments, - mentionedUsers: message.mentionedUsers, - reactionCounts: message.reactionCounts, - reactionScores: message.reactionScores, - latestReactions: message.latestReactions, - ownReactions: message.ownReactions, - parentId: message.parentId, - quotedMessageId: message.quotedMessageId, - quotedMessage: message.quotedMessage, - replyCount: message.replyCount, - showInChannel: message.showInChannel, - command: message.command, - silent: message.silent, - createdAt: message.createdAt, - updatedAt: message.updatedAt, - deletedAt: message.deletedAt, - user: message.user, - status: message.status, - threadParticipants: message.threadParticipants, - extraData: message.extraData, - ); + newMessages[oldIndex] = newMessages[oldIndex].merge(message); } else { newMessages.add(message); } @@ -1395,20 +1372,7 @@ class ChannelClientState { _channelState = _channelState.copyWith( messages: newMessages, - channel: _channelState.channel?.copyWith( - lastMessageAt: updatedState.channel?.lastMessageAt, - createdAt: updatedState.channel?.createdAt, - type: updatedState.channel?.type, - extraData: updatedState.channel?.extraData, - updatedAt: updatedState.channel?.updatedAt, - id: updatedState.channel?.id, - createdBy: updatedState.channel?.createdBy, - config: updatedState.channel?.config, - deletedAt: updatedState.channel?.deletedAt, - cid: updatedState.channel?.cid, - frozen: updatedState.channel?.frozen, - memberCount: updatedState.channel?.memberCount, - ), + channel: _channelState.channel?.merge(updatedState.channel), watchers: newWatchers, watcherCount: updatedState.watcherCount, members: newMembers, diff --git a/packages/dart_client/lib/src/client.dart b/packages/dart_client/lib/src/client.dart index c5a77d11..09ea4232 100644 --- a/packages/dart_client/lib/src/client.dart +++ b/packages/dart_client/lib/src/client.dart @@ -46,6 +46,26 @@ const String KEY_TOKEN = 'KEY_TOKEN'; /// The key used to save the apiKey to sharedPreferences const String KEY_API_KEY = 'KEY_API_KEY'; +/// Provider used to send push notifications. +enum PushProvider { + /// Send notifications using Google's Firebase Cloud Messaging + firebase, + + /// Send notifications using Apple's Push Notification service + apn +} + +extension on PushProvider { + /// Returns the string notion for [PushProvider]. + String get name { + if (this == PushProvider.apn) { + return 'apn'; + } else { + return 'firebase'; + } + } +} + /// The official Dart client for Stream Chat, /// a service for building chat applications. /// This library can be used on any Dart project and on both mobile and web apps with Flutter. @@ -1002,10 +1022,10 @@ class Client { } /// Add a device for Push Notifications. - Future addDevice(String id, String pushProvider) async { + Future addDevice(String id, PushProvider pushProvider) async { final response = await post('/devices', data: { 'id': id, - 'push_provider': pushProvider, + 'push_provider': pushProvider.name, }); return decode(response.data, EmptyResponse.fromJson); } diff --git a/packages/dart_client/lib/src/models/channel_model.dart b/packages/dart_client/lib/src/models/channel_model.dart index 10f65e0b..046e29c5 100644 --- a/packages/dart_client/lib/src/models/channel_model.dart +++ b/packages/dart_client/lib/src/models/channel_model.dart @@ -142,4 +142,25 @@ class ChannelModel { extraData: extraData ?? this.extraData, team: team ?? this.team, ); + + /// Returns a new [ChannelModel] that is a combination of this channelModel and the given + /// [other] channelModel. + ChannelModel merge(ChannelModel other) { + if (other == null) return this; + return copyWith( + id: other.id, + type: other.type, + cid: other.cid, + config: other.config, + createdBy: other.createdBy, + frozen: other.frozen, + lastMessageAt: other.lastMessageAt, + createdAt: other.createdAt, + updatedAt: other.updatedAt, + deletedAt: other.deletedAt, + memberCount: other.memberCount, + extraData: other.extraData, + team: other.team, + ); + } } diff --git a/packages/dart_client/lib/src/models/message.dart b/packages/dart_client/lib/src/models/message.dart index 46f2551a..e23eb51a 100644 --- a/packages/dart_client/lib/src/models/message.dart +++ b/packages/dart_client/lib/src/models/message.dart @@ -250,6 +250,38 @@ class Message { deletedAt: deletedAt ?? this.deletedAt, status: status ?? this.status, ); + + /// Returns a new [Message] that is a combination of this message and the given + /// [other] message. + Message merge(Message other) { + if (other == null) return this; + return copyWith( + id: other.id, + text: other.text, + type: other.type, + attachments: other.attachments, + mentionedUsers: other.mentionedUsers, + reactionCounts: other.reactionCounts, + reactionScores: other.reactionScores, + latestReactions: other.latestReactions, + ownReactions: other.ownReactions, + parentId: other.parentId, + quotedMessage: other.quotedMessage, + quotedMessageId: other.quotedMessageId, + replyCount: other.replyCount, + threadParticipants: other.threadParticipants, + showInChannel: other.showInChannel, + command: other.command, + createdAt: other.createdAt, + silent: other.silent, + extraData: other.extraData, + user: other.user, + shadowed: other.shadowed, + updatedAt: other.updatedAt, + deletedAt: other.deletedAt, + status: other.status, + ); + } } /// A translated message diff --git a/packages/dart_client/lib/version.dart b/packages/dart_client/lib/version.dart index 4650f5b2..bc746ab0 100644 --- a/packages/dart_client/lib/version.dart +++ b/packages/dart_client/lib/version.dart @@ -2,4 +2,4 @@ import 'package:stream_chat/src/client.dart'; /// Current package version /// Used in [Client] to build the `x-stream-client` header -const PACKAGE_VERSION = '0.2.23+3'; +const PACKAGE_VERSION = '0.2.24'; diff --git a/packages/dart_client/pubspec.yaml b/packages/dart_client/pubspec.yaml index d045fe22..9fd0d9e0 100644 --- a/packages/dart_client/pubspec.yaml +++ b/packages/dart_client/pubspec.yaml @@ -1,12 +1,12 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 0.2.23+3 +version: 0.2.24 repository: https://github.com/GetStream/stream-chat-dart issue_tracker: https://github.com/GetStream/stream-chat-dart/issues environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: flutter: diff --git a/packages/dart_client/test/src/client_test.dart b/packages/dart_client/test/src/client_test.dart index 738255cc..8f4dc41e 100644 --- a/packages/dart_client/test/src/client_test.dart +++ b/packages/dart_client/test/src/client_test.dart @@ -266,13 +266,17 @@ void main() { when(mockDio.post('/devices', data: { 'id': 'test-id', - 'push_provider': 'test', + 'push_provider': 'firebase', })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.addDevice('test-id', 'test'); + await client.addDevice('test-id', PushProvider.firebase); - verify(mockDio.post('/devices', - data: {'id': 'test-id', 'push_provider': 'test'})).called(1); + verify( + mockDio.post( + '/devices', + data: {'id': 'test-id', 'push_provider': 'firebase'}, + ), + ).called(1); }); test('getDevices', () async { diff --git a/packages/flutter_widgets/example/android/app/build.gradle b/packages/flutter_widgets/example/android/app/build.gradle index c90beca2..c5ff1b9e 100644 --- a/packages/flutter_widgets/example/android/app/build.gradle +++ b/packages/flutter_widgets/example/android/app/build.gradle @@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion 29 - ndkVersion '22.0.7026061' + ndkVersion '21.3.6528147' sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/packages/flutter_widgets/example/ios/fastlane/report.xml b/packages/flutter_widgets/example/ios/fastlane/report.xml index 3a11e14a..0010f637 100644 --- a/packages/flutter_widgets/example/ios/fastlane/report.xml +++ b/packages/flutter_widgets/example/ios/fastlane/report.xml @@ -5,27 +5,27 @@ - + - + - + - + - + diff --git a/packages/flutter_widgets/example/lib/advanced_options_page.dart b/packages/flutter_widgets/example/lib/advanced_options_page.dart index 2da11406..023ce32c 100644 --- a/packages/flutter_widgets/example/lib/advanced_options_page.dart +++ b/packages/flutter_widgets/example/lib/advanced_options_page.dart @@ -222,8 +222,23 @@ class _AdvancedOptionsPageState extends State { ), Spacer(), RaisedButton( + color: Theme.of(context).brightness == Brightness.light + ? StreamChatTheme.of(context).colorTheme.accentBlue + : Colors.white, elevation: 0, - child: Text('Login', style: TextStyle(fontSize: 16)), + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26), + ), + child: Text( + 'Login', + style: TextStyle( + fontSize: 16, + color: Theme.of(context).brightness != Brightness.light + ? StreamChatTheme.of(context).colorTheme.accentBlue + : Colors.white, + ), + ), onPressed: () async { if (loading) { return; diff --git a/packages/flutter_widgets/example/lib/custom_theme.dart b/packages/flutter_widgets/example/lib/custom_theme.dart index d8abcb90..817d2e67 100644 --- a/packages/flutter_widgets/example/lib/custom_theme.dart +++ b/packages/flutter_widgets/example/lib/custom_theme.dart @@ -41,11 +41,12 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { final themeData = ThemeData(primarySwatch: Colors.green); final defaultTheme = StreamChatThemeData.fromTheme(themeData); + final colorTheme = defaultTheme.colorTheme; final customTheme = defaultTheme.merge(StreamChatThemeData( ownMessageTheme: MessageTheme( - messageBackgroundColor: StreamChatTheme.of(context).colorTheme.black, + messageBackgroundColor: colorTheme.black, messageText: TextStyle( - color: StreamChatTheme.of(context).colorTheme.white, + color: colorTheme.white, ), avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(8), diff --git a/packages/flutter_widgets/example/lib/customize_channel_preview.dart b/packages/flutter_widgets/example/lib/customize_channel_preview.dart index 29d22a82..9b7deb64 100644 --- a/packages/flutter_widgets/example/lib/customize_channel_preview.dart +++ b/packages/flutter_widgets/example/lib/customize_channel_preview.dart @@ -80,6 +80,17 @@ class ChannelListPage extends StatelessWidget { final opacity = channel.state.unreadCount > .0 ? 1.0 : 0.5; return ListTile( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => StreamChannel( + child: ChannelPage(), + channel: channel, + ), + ), + ); + }, leading: ChannelImage( channel: channel, ), diff --git a/packages/flutter_widgets/example/lib/customize_message_widget.dart b/packages/flutter_widgets/example/lib/customize_message_widget.dart index 84e65eab..14233dd7 100644 --- a/packages/flutter_widgets/example/lib/customize_message_widget.dart +++ b/packages/flutter_widgets/example/lib/customize_message_widget.dart @@ -97,7 +97,7 @@ class ChannelPage extends StatelessWidget { List messages, ) { final message = details.message; - final color = details.isMyMessage ? Colors.blueGrey : Colors.blue; + final color = details.isMyMessage ? Colors.red : Colors.blue; if (message.isSystem) { return SizedBox(); } @@ -110,7 +110,10 @@ class ChannelPage extends StatelessWidget { color: color, width: 2, ), - padding: const EdgeInsets.all(2), + padding: const EdgeInsets.symmetric( + vertical: 2, + horizontal: 4, + ), attachmentBorderSide: BorderSide( color: color, width: 2, diff --git a/packages/flutter_widgets/example/lib/notifications_service.dart b/packages/flutter_widgets/example/lib/notifications_service.dart index 83f5047a..298ab3ab 100644 --- a/packages/flutter_widgets/example/lib/notifications_service.dart +++ b/packages/flutter_widgets/example/lib/notifications_service.dart @@ -56,7 +56,7 @@ void initNotifications(Client client) { if (connector.token.value != null) { client.addDevice( connector.token.value, - Platform.isAndroid ? 'firebase' : 'apn', + Platform.isAndroid ? PushProvider.firebase : PushProvider.apn, ); } }); diff --git a/packages/flutter_widgets/example/pubspec.yaml b/packages/flutter_widgets/example/pubspec.yaml index f7f941b8..8c11c0ee 100644 --- a/packages/flutter_widgets/example/pubspec.yaml +++ b/packages/flutter_widgets/example/pubspec.yaml @@ -1,6 +1,7 @@ name: example description: A new Flutter project. -version: 1.1.2+2 +publish_to: 'none' +version: 1.1.3+3 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/packages/flutter_widgets/lib/src/channel_list_view.dart b/packages/flutter_widgets/lib/src/channel_list_view.dart index 61a0aabb..0cc7634b 100644 --- a/packages/flutter_widgets/lib/src/channel_list_view.dart +++ b/packages/flutter_widgets/lib/src/channel_list_view.dart @@ -529,123 +529,97 @@ class _ChannelListViewState extends State }; } + final backgroundColor = StreamChatTheme.of(context).colorTheme.whiteSmoke; return StreamChannel( key: ValueKey('CHANNEL-${channel.id}'), channel: channel, child: Builder( builder: (context) { - Widget child; - if (widget.channelPreviewBuilder != null) { - child = Stack( - children: [ - widget.channelPreviewBuilder( - context, - channel, - ), - Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - onTap(channel, widget.channelWidget); - }, - onLongPress: widget.onChannelLongPress != null - ? () { - widget.onChannelLongPress(channel); - } - : null, + return Slidable( + controller: _slideController, + enabled: widget.swipeToAction, + actionPane: SlidableBehindActionPane(), + actionExtentRatio: 0.12, + closeOnScroll: true, + secondaryActions: [ + IconSlideAction( + color: backgroundColor, + icon: Icons.more_horiz, + onTap: () { + showModalBottomSheet( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(32), + topRight: Radius.circular(32), + ), ), - ), - ), - ], - ); - } else { - final backgroundColor = - StreamChatTheme.of(context).colorTheme.whiteSmoke; - child = Slidable( - controller: _slideController, - enabled: widget.swipeToAction, - actionPane: SlidableBehindActionPane(), - actionExtentRatio: 0.12, - closeOnScroll: true, - secondaryActions: [ + context: context, + builder: (context) { + return StreamChannel( + child: ChannelBottomSheet( + onViewInfoTap: () { + widget.onViewInfoTap(channel); + }, + ), + channel: channel, + ); + }, + ); + }, + ), + if ([ + 'admin', + 'owner', + ].contains(channel.state.members + .firstWhere((m) => m.userId == channel.client.state.user.id, + orElse: () => null) + ?.role)) IconSlideAction( color: backgroundColor, - icon: Icons.more_horiz, - onTap: () { - showModalBottomSheet( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(32), - topRight: Radius.circular(32), - ), - ), - context: context, - builder: (context) { - return StreamChannel( - child: ChannelBottomSheet( - onViewInfoTap: () { - widget.onViewInfoTap(channel); - }, - ), - channel: channel, - ); - }, - ); - }, - ), - if ([ - 'admin', - 'owner', - ].contains(channel.state.members - .firstWhere( - (m) => m.userId == channel.client.state.user.id, - orElse: () => null) - ?.role)) - IconSlideAction( - color: backgroundColor, - iconWidget: StreamSvgIcon.delete( - color: StreamChatTheme.of(context).colorTheme.accentRed, - ), - onTap: () async { - final res = await showConfirmationDialog( - context, - title: 'Delete Conversation', - okText: 'DELETE', - question: - 'Are you sure you want to delete this conversation?', - cancelText: 'CANCEL', - icon: StreamSvgIcon.delete( - color: StreamChatTheme.of(context) - .colorTheme - .accentRed, - ), - ); - if (res == true) { - await channel.delete(); - } - }, + iconWidget: StreamSvgIcon.delete( + color: StreamChatTheme.of(context).colorTheme.accentRed, ), - ], - child: Container( - color: StreamChatTheme.of(context).colorTheme.whiteSnow, - child: ChannelPreview( - onLongPress: widget.onChannelLongPress, - channel: channel, - onImageTap: widget.onImageTap != null - ? () { - widget.onImageTap(channel); - } - : null, - onTap: (channel) { - onTap(channel, widget.channelWidget); + onTap: () async { + final res = await showConfirmationDialog( + context, + title: 'Delete Conversation', + okText: 'DELETE', + question: + 'Are you sure you want to delete this conversation?', + cancelText: 'CANCEL', + icon: StreamSvgIcon.delete( + color: + StreamChatTheme.of(context).colorTheme.accentRed, + ), + ); + if (res == true) { + await channel.delete(); + } }, ), - ), - ); - } - return child; + ], + child: Container( + color: StreamChatTheme.of(context).colorTheme.whiteSnow, + child: widget.channelPreviewBuilder != null + ? widget.channelPreviewBuilder( + context, + channel, + ) + : ChannelPreview( + onLongPress: widget.onChannelLongPress, + channel: channel, + onImageTap: widget.onImageTap != null + ? () { + widget.onImageTap(channel); + } + : null, + onTap: (channel) { + onTap(channel, widget.channelWidget); + }, + ), + ), + ); }, ), ); diff --git a/packages/flutter_widgets/lib/src/file_attachment.dart b/packages/flutter_widgets/lib/src/file_attachment.dart index 3fa8c469..dc7fae4b 100644 --- a/packages/flutter_widgets/lib/src/file_attachment.dart +++ b/packages/flutter_widgets/lib/src/file_attachment.dart @@ -41,7 +41,7 @@ class _FileAttachmentState extends State { @override void initState() { super.initState(); - if (MediaUtils.getMimeType(widget.attachment.title).type == 'video') { + if (MediaUtils.getMimeType(widget.attachment.title)?.type == 'video') { if (widget.attachmentType == FileAttachmentType.online) { _controller = VideoPlayerController.network( widget.attachment.assetUrl, @@ -62,7 +62,13 @@ class _FileAttachmentState extends State { child: Container( width: widget.size?.width ?? 100, height: 56.0, - color: StreamChatTheme.of(context).colorTheme.white, + decoration: BoxDecoration( + color: StreamChatTheme.of(context).colorTheme.white, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: StreamChatTheme.of(context).colorTheme.greyWhisper, + ), + ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -122,7 +128,7 @@ class _FileAttachmentState extends State { } Widget _getFileTypeImage() { - if ((MediaUtils.getMimeType(widget.attachment.title).type == 'image')) { + if ((MediaUtils.getMimeType(widget.attachment.title)?.type == 'image')) { switch (widget.attachmentType) { case FileAttachmentType.local: return Image.memory( @@ -159,7 +165,7 @@ class _FileAttachmentState extends State { } } - if ((MediaUtils.getMimeType(widget.attachment.title).type == 'video')) { + if ((MediaUtils.getMimeType(widget.attachment.title)?.type == 'video')) { switch (widget.attachmentType) { case FileAttachmentType.local: return FutureBuilder( diff --git a/packages/flutter_widgets/lib/src/giphy_attachment.dart b/packages/flutter_widgets/lib/src/giphy_attachment.dart index 6abe3df9..0f89bc75 100644 --- a/packages/flutter_widgets/lib/src/giphy_attachment.dart +++ b/packages/flutter_widgets/lib/src/giphy_attachment.dart @@ -68,27 +68,7 @@ class GiphyAttachment extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: GestureDetector( onTap: () async { - var res = await Navigator.push(context, - MaterialPageRoute(builder: (_) { - final channel = StreamChannel.of(context).channel; - - return StreamChannel( - channel: channel, - child: FullScreenMedia( - mediaAttachments: [ - attachment, - ], - userName: message.user.name, - sentAt: message.createdAt, - message: message, - onShowMessage: onShowMessage, - ), - ); - })); - - if (res != null) { - onReturnAction(res); - } + _onImageTap(context); }, child: ClipRRect( borderRadius: BorderRadius.only( @@ -324,6 +304,31 @@ class GiphyAttachment extends StatelessWidget { ); } + void _onImageTap(BuildContext context) async { + var res = await Navigator.push(context, MaterialPageRoute( + builder: (_) { + final channel = StreamChannel.of(context).channel; + + return StreamChannel( + channel: channel, + child: FullScreenMedia( + mediaAttachments: [ + attachment, + ], + userName: message.user.name, + sentAt: message.createdAt, + message: message, + onShowMessage: onShowMessage, + ), + ); + }, + )); + + if (res != null) { + onReturnAction(res); + } + } + Widget _buildSentAttachment(context) { return Container( child: GestureDetector( diff --git a/packages/flutter_widgets/lib/src/message_widget.dart b/packages/flutter_widgets/lib/src/message_widget.dart index eeabaa13..2a15f898 100644 --- a/packages/flutter_widgets/lib/src/message_widget.dart +++ b/packages/flutter_widgets/lib/src/message_widget.dart @@ -159,8 +159,8 @@ class MessageWidget extends StatefulWidget { this.showReactionPickerIndicator = false, this.showUserAvatar = DisplayWidget.show, this.showSendingIndicator = true, - this.showThreadReplyIndicator = true, - this.showInChannelIndicator = true, + this.showThreadReplyIndicator = false, + this.showInChannelIndicator = false, this.onReplyTap, this.onThreadTap, this.showUsername = true, @@ -371,15 +371,24 @@ class _MessageWidgetState extends State { alignment: Alignment.center, transform: Matrix4.rotationY( widget.reverse ? pi : 0), - child: DeletedMessage( - reverse: widget.reverse, - borderRadiusGeometry: widget - .borderRadiusGeometry, - borderSide: - widget.borderSide, - shape: widget.shape, - messageTheme: - widget.messageTheme, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: + widget.showUserAvatar == + DisplayWidget + .gone + ? 0 + : 4.0), + child: DeletedMessage( + reverse: widget.reverse, + borderRadiusGeometry: widget + .borderRadiusGeometry, + borderSide: + widget.borderSide, + shape: widget.shape, + messageTheme: + widget.messageTheme, + ), ), ) : Card( diff --git a/packages/flutter_widgets/lib/src/reaction_bubble.dart b/packages/flutter_widgets/lib/src/reaction_bubble.dart index 74a2ae99..4ddcca93 100644 --- a/packages/flutter_widgets/lib/src/reaction_bubble.dart +++ b/packages/flutter_widgets/lib/src/reaction_bubble.dart @@ -67,7 +67,7 @@ class ReactionBubble extends StatelessWidget { children: [ if (constraints.maxWidth < double.infinity) ...reactions - .take((constraints.maxWidth) ~/ 22) + .take((constraints.maxWidth) ~/ 24) .map((reaction) { return _buildReaction( reactionIcons, diff --git a/packages/flutter_widgets/lib/src/stream_chat_theme.dart b/packages/flutter_widgets/lib/src/stream_chat_theme.dart index 5b5927c8..3945a033 100644 --- a/packages/flutter_widgets/lib/src/stream_chat_theme.dart +++ b/packages/flutter_widgets/lib/src/stream_chat_theme.dart @@ -45,9 +45,6 @@ class StreamChatThemeData { /// The text themes used in the widgets final TextTheme textTheme; - /// The button themes used in the widgets - final ButtonThemeData buttonTheme; - /// The text themes used in the widgets final ColorTheme colorTheme; @@ -78,7 +75,6 @@ class StreamChatThemeData { /// Create a theme from scratch const StreamChatThemeData({ this.textTheme, - this.buttonTheme, this.colorTheme, this.channelPreviewTheme, this.channelTheme, @@ -93,24 +89,20 @@ class StreamChatThemeData { /// Create a theme from a Material [Theme] factory StreamChatThemeData.fromTheme(ThemeData theme) { final defaultTheme = getDefaultTheme(theme); - final customizedTheme = StreamChatThemeData( - primaryIconTheme: theme.primaryIconTheme, - ownMessageTheme: MessageTheme( - replies: TextStyle(color: theme.accentColor), - messageLinks: TextStyle(color: theme.accentColor), + final customizedTheme = StreamChatThemeData.fromColorAndTextTheme( + defaultTheme.colorTheme.copyWith( + accentBlue: theme.accentColor, ), - otherMessageTheme: MessageTheme( - replies: TextStyle(color: theme.accentColor), - messageLinks: TextStyle(color: theme.accentColor), - ), - ); + defaultTheme.textTheme, + ).copyWith( + // primaryIconTheme: theme.primaryIconTheme, + ); return defaultTheme.merge(customizedTheme) ?? customizedTheme; } /// Creates a copy of [StreamChatThemeData] with specified attributes overridden. StreamChatThemeData copyWith({ TextTheme textTheme, - ButtonThemeData buttonTheme, ColorTheme colorTheme, ChannelPreviewTheme channelPreviewTheme, ChannelTheme channelTheme, @@ -123,7 +115,6 @@ class StreamChatThemeData { }) => StreamChatThemeData( textTheme: textTheme ?? this.textTheme, - buttonTheme: buttonTheme ?? this.buttonTheme, colorTheme: colorTheme ?? this.colorTheme, primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme, defaultChannelImage: defaultChannelImage ?? this.defaultChannelImage, @@ -139,7 +130,6 @@ class StreamChatThemeData { if (other == null) return this; return copyWith( textTheme: textTheme?.merge(other.textTheme) ?? other.textTheme, - buttonTheme: other.buttonTheme, colorTheme: colorTheme?.merge(other.colorTheme) ?? other.colorTheme, primaryIconTheme: other.primaryIconTheme, defaultChannelImage: other.defaultChannelImage, @@ -157,26 +147,14 @@ class StreamChatThemeData { ); } - /// Get the default Stream Chat theme - static StreamChatThemeData getDefaultTheme(ThemeData theme) { - final accentColor = Color(0xff006cff); - final isDark = theme.brightness == Brightness.dark; - final textTheme = isDark ? TextTheme.dark() : TextTheme.light(); - final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light(); + static StreamChatThemeData fromColorAndTextTheme( + ColorTheme colorTheme, + TextTheme textTheme, + ) { + final accentColor = colorTheme.accentBlue; return StreamChatThemeData( textTheme: textTheme, colorTheme: colorTheme, - buttonTheme: ButtonThemeData( - height: 48.0, - buttonColor: isDark ? Color(0xffffffff) : Color(0xff006aff), - textTheme: ButtonTextTheme.accent, - colorScheme: theme.colorScheme.copyWith( - secondary: isDark ? Color(0xff005eff) : Color(0xffffffff), - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26), - ), - ), primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)), defaultChannelImage: (context, channel) => SizedBox(), defaultUserImage: (context, user) => Center( @@ -204,7 +182,7 @@ class StreamChatThemeData { ), indicatorIconSize: 16.0), channelTheme: ChannelTheme( - messageInputButtonIconTheme: theme.iconTheme.copyWith( + messageInputButtonIconTheme: IconThemeData( color: accentColor, ), channelHeaderTheme: ChannelHeaderTheme( @@ -291,6 +269,17 @@ class StreamChatThemeData { ], ); } + + /// Get the default Stream Chat theme + static StreamChatThemeData getDefaultTheme(ThemeData theme) { + final isDark = theme.brightness == Brightness.dark; + final textTheme = isDark ? TextTheme.dark() : TextTheme.light(); + final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light(); + return fromColorAndTextTheme( + colorTheme, + textTheme, + ); + } } enum TextThemeType { diff --git a/packages/flutter_widgets/pubspec.yaml b/packages/flutter_widgets/pubspec.yaml index a68082a1..2b82aca2 100644 --- a/packages/flutter_widgets/pubspec.yaml +++ b/packages/flutter_widgets/pubspec.yaml @@ -6,7 +6,7 @@ repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.6.0 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: flutter: @@ -29,7 +29,7 @@ dependencies: image_picker: ^0.6.7+17 flutter_keyboard_visibility: ^4.0.2 mime: ^0.9.7 - stream_chat: ^0.2.23+3 + stream_chat: ^0.2.24 video_compress: ^2.1.1 visibility_detector: ^0.1.5 http_parser: ^3.1.4 diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java b/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java index e30c2bec..334c0856 100644 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java +++ b/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java @@ -2,5 +2,4 @@ package example.example; import io.flutter.embedding.android.FlutterActivity; -public class MainActivity extends FlutterActivity { -} +public class MainActivity extends FlutterActivity {} diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_view_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_view_core.dart index ae1cefdc..41d5d668 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_view_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_view_core.dart @@ -154,12 +154,12 @@ class _UserListViewCoreState extends State temp = users..sort((curr, next) => curr.name.compareTo(next.name)); } final groupedUsers = >{}; - for (var e in temp) { + for (final e in temp) { final alphabet = e.name[0]?.toUpperCase(); groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e]; } final items = []; - for (var key in groupedUsers.keys) { + for (final key in groupedUsers.keys) { items.add(ListHeaderItem(key)); items.addAll(groupedUsers[key].map((e) => ListUserItem(e))); } diff --git a/packages/stream_chat_flutter_core/test/stream_chat_flutter_core_test.dart b/packages/stream_chat_flutter_core/test/stream_chat_flutter_core_test.dart index 3e26a916..ab73b3a2 100644 --- a/packages/stream_chat_flutter_core/test/stream_chat_flutter_core_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_chat_flutter_core_test.dart @@ -1,5 +1 @@ -import 'package:flutter_test/flutter_test.dart'; - -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - void main() {}