add stream_chat_v1 example
@@ -0,0 +1,39 @@
|
||||
# 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/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Exceptions to above rules.
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
|
||||
fvm
|
||||
@@ -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: 0b8abb4724aa590dd0f429683339b1e045a1594d
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
@@ -0,0 +1,7 @@
|
||||
# Flutter Chat Example Application
|
||||
|
||||
An example chat application using Flutter and Stream Chat.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Make sure to follow the steps from the [tutorial](https://getstream.io/chat/flutter/tutorial/).
|
||||
@@ -0,0 +1,7 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
@@ -0,0 +1,71 @@
|
||||
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
|
||||
ndkVersion '21.3.6528147'
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.example"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
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"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||
implementation 'com.google.firebase:firebase-messaging:20.1.2'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "1004276287628",
|
||||
"firebase_url": "https://test-a0490.firebaseio.com",
|
||||
"project_id": "test-a0490",
|
||||
"storage_bucket": "test-a0490.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:1004276287628:android:7fff771d5e464c6a5f93a9",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.example"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "1004276287628-43j2uub5rd0968ois7h963i0np373t6k.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyC2ESbd8qWtIUWwGn1L_Dbxzh2W6yfGqiU"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "1004276287628-43j2uub5rd0968ois7h963i0np373t6k.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,39 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.example">
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<application
|
||||
android:name=".Application"
|
||||
android:label="ChatSample"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.example.example
|
||||
|
||||
import com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin
|
||||
import io.flutter.app.FlutterApplication
|
||||
import io.flutter.plugin.common.PluginRegistry
|
||||
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
|
||||
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin
|
||||
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
|
||||
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin
|
||||
import io.flutter.plugins.pathprovider.PathProviderPlugin
|
||||
|
||||
class Application : FlutterApplication(), PluginRegistrantCallback {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FlutterFirebaseMessagingService.setPluginRegistrant(this)
|
||||
}
|
||||
|
||||
override fun registerWith(registry: PluginRegistry?) {
|
||||
PathProviderPlugin.registerWith(registry?.registrarFor(
|
||||
"io.flutter.plugins.pathprovider.PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.registerWith(registry?.registrarFor(
|
||||
"io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin"))
|
||||
FlutterLocalNotificationsPlugin.registerWith(registry?.registrarFor(
|
||||
"com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin"))
|
||||
FirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.example.example
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,32 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.2'
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
@@ -0,0 +1,6 @@
|
||||
#Thu Oct 22 11:03:39 CEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
@@ -0,0 +1,15 @@
|
||||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include ':app'
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 12C3 11.4477 3.44772 11 4 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H4C3.44772 13 3 12.5523 3 12Z" fill="#006CFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2929 7.29289C15.6834 6.90237 16.3166 6.90237 16.7071 7.29289L20.7071 11.2929C21.0976 11.6834 21.0976 12.3166 20.7071 12.7071C20.3166 13.0976 19.6834 13.0976 19.2929 12.7071L15.2929 8.70711C14.9024 8.31658 14.9024 7.68342 15.2929 7.29289Z" fill="#006CFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.7071 11.2929C21.0976 11.6834 21.0976 12.3166 20.7071 12.7071L16.7071 16.7071C16.3166 17.0976 15.6834 17.0976 15.2929 16.7071C14.9024 16.3166 14.9024 15.6834 15.2929 15.2929L19.2929 11.2929C19.6834 10.9024 20.3166 10.9024 20.7071 11.2929Z" fill="#006CFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 908 B |
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="80" height="40" viewBox="0 0 80 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M52.2984 12.8561L77.9046 11.0409C79.5948 10.9211 80.659 12.8259 79.6708 14.2023L61.7582 39.1527C61.3826 39.6759 60.7778 39.9863 60.1336 39.9863H19.9646C19.3214 39.9863 18.7174 39.6769 18.3414 39.1547L0.381197 14.2043C-0.609403 12.8281 0.454797 10.9207 2.1464 11.0409L27.6746 12.8563L38.578 0.666539C39.377 -0.226661 40.777 -0.221461 41.5694 0.677939L52.2984 12.8561ZM57.6608 36.0305L41.0426 29.8199V36.0305H57.6608ZM39.0426 36.0305V29.8199L22.4244 36.0305H39.0426ZM37.125 28.3363L20.146 34.6743L7.6548 17.3111L37.125 28.3363ZM42.9256 28.3363L59.9046 34.6743L72.3958 17.3111L42.9256 28.3363ZM39.0586 26.8871V5.98954L25.1758 21.6611L39.0586 26.8871ZM41.0426 26.8871V5.99214L54.9256 21.6611L41.0426 26.8871ZM21.1046 20.2165L24.2988 16.6273L8.6934 15.5089L21.1046 20.2165ZM58.8298 20.2165L55.6358 16.6273L71.2412 15.5089L58.8298 20.2165Z" fill="#006CFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1004 B |
@@ -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
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
@@ -0,0 +1,6 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
||||
@@ -0,0 +1,180 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.2)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.380.0)
|
||||
aws-sdk-core (3.109.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.39.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.83.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.3)
|
||||
claide (1.0.3)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
declarative (0.0.20)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.6.1)
|
||||
rake (~> 13.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (3.0.0)
|
||||
excon (0.76.0)
|
||||
faraday (1.0.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (1.0.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.0)
|
||||
fastlane (2.162.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.37.0, < 0.39.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-firebase_app_distribution (0.2.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.38.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.9)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.12)
|
||||
google-cloud-core (1.5.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.3.3)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-errors (1.0.1)
|
||||
google-cloud-storage (1.29.1)
|
||||
addressable (~> 2.5)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.33)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (~> 0.9)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (0.13.1)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.14)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
json (2.3.1)
|
||||
jwt (2.2.2)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.10.1)
|
||||
mini_mime (1.0.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.0)
|
||||
os (1.1.1)
|
||||
plist (3.5.0)
|
||||
public_suffix (4.0.6)
|
||||
rake (13.0.1)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
rubyzip (2.3.0)
|
||||
security (0.1.3)
|
||||
signet (0.14.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.18.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
fastlane-plugin-firebase_app_distribution
|
||||
|
||||
BUNDLED WITH
|
||||
2.0.2
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Notifications</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.usernotifications.service</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// NotificationService.swift
|
||||
// Notifications
|
||||
//
|
||||
// Created by Salvatore Giordano on 25/03/2020.
|
||||
// Copyright © 2020 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
|
||||
import UserNotifications
|
||||
//import StreamChatClient
|
||||
|
||||
final class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
self.contentHandler = contentHandler
|
||||
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
||||
|
||||
guard let sharedDefaults = UserDefaults(suiteName: "group.io.getstream.flutter"),
|
||||
let apiKey = sharedDefaults.string(forKey: "KEY_API_KEY"),
|
||||
let userId = sharedDefaults.string(forKey: "KEY_USER_ID"),
|
||||
let token = sharedDefaults.string(forKey: "KEY_TOKEN"),
|
||||
let messageId = bestAttemptContent?.userInfo["message_id"] as? String else {
|
||||
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 ?? "<NoContent>")"
|
||||
// contentHandler(self?.bestAttemptContent ?? request.content)
|
||||
// }
|
||||
// Client.shared.disconnect()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
||||
contentHandler(bestAttemptContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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<Member>()
|
||||
// /// An extra data for the channel.
|
||||
// public let extraData: Codable?
|
||||
//}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.io.getstream.flutter</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,43 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '11.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
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_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
end
|
||||
|
||||
pod 'StreamChatClient'
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,299 @@
|
||||
PODS:
|
||||
- DKImagePickerController/Core (4.3.2):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
- DKImagePickerController/ImageDataManager (4.3.2)
|
||||
- DKImagePickerController/PhotoGallery (4.3.2):
|
||||
- DKImagePickerController/Core
|
||||
- DKPhotoGallery
|
||||
- DKImagePickerController/Resource (4.3.2)
|
||||
- DKPhotoGallery (0.0.17):
|
||||
- DKPhotoGallery/Core (= 0.0.17)
|
||||
- DKPhotoGallery/Model (= 0.0.17)
|
||||
- DKPhotoGallery/Preview (= 0.0.17)
|
||||
- DKPhotoGallery/Resource (= 0.0.17)
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Core (0.0.17):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Preview
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Model (0.0.17):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Preview (0.0.17):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Resource
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Resource (0.0.17):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- esys_flutter_share (0.0.1):
|
||||
- Flutter
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Firebase/CoreOnly (6.33.0):
|
||||
- FirebaseCore (= 6.10.3)
|
||||
- Firebase/Messaging (6.33.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 4.7.0)
|
||||
- firebase_core (0.5.3):
|
||||
- Firebase/CoreOnly (~> 6.33.0)
|
||||
- Flutter
|
||||
- firebase_messaging (7.0.3):
|
||||
- Firebase/CoreOnly (~> 6.33.0)
|
||||
- Firebase/Messaging (~> 6.33.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- FirebaseCore (6.10.3):
|
||||
- FirebaseCoreDiagnostics (~> 1.6)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- FirebaseCoreDiagnostics (1.7.0):
|
||||
- GoogleDataTransport (~> 7.4)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- nanopb (~> 1.30906.0)
|
||||
- FirebaseInstallations (1.7.0):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/UserDefaults (~> 6.7)
|
||||
- PromisesObjC (~> 1.2)
|
||||
- FirebaseInstanceID (4.8.0):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- FirebaseInstallations (~> 1.6)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/UserDefaults (~> 6.7)
|
||||
- FirebaseMessaging (4.7.1):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- FirebaseInstanceID (~> 4.7)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 6.7)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Reachability (~> 6.7)
|
||||
- GoogleUtilities/UserDefaults (~> 6.7)
|
||||
- Protobuf (>= 3.9.2, ~> 3.9)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_apns (0.0.1):
|
||||
- Flutter
|
||||
- flutter_app_badger (0.0.1):
|
||||
- Flutter
|
||||
- flutter_keyboard_visibility (0.0.1):
|
||||
- Flutter
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage (3.3.1):
|
||||
- Flutter
|
||||
- FMDB (2.7.5):
|
||||
- FMDB/standard (= 2.7.5)
|
||||
- FMDB/standard (2.7.5)
|
||||
- GoogleDataTransport (7.5.1):
|
||||
- nanopb (~> 1.30906.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (6.7.2):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network
|
||||
- GoogleUtilities/Environment (6.7.2):
|
||||
- PromisesObjC (~> 1.2)
|
||||
- GoogleUtilities/Logger (6.7.2):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Network (6.7.2):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
- GoogleUtilities/Reachability
|
||||
- "GoogleUtilities/NSData+zlib (6.7.2)"
|
||||
- GoogleUtilities/Reachability (6.7.2):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (6.7.2):
|
||||
- GoogleUtilities/Logger
|
||||
- image_gallery_saver (1.5.0):
|
||||
- Flutter
|
||||
- image_picker (0.0.1):
|
||||
- Flutter
|
||||
- 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
|
||||
- photo_manager (0.0.1):
|
||||
- Flutter
|
||||
- PromisesObjC (1.2.11)
|
||||
- Protobuf (3.13.0)
|
||||
- SDWebImage (5.10.0):
|
||||
- SDWebImage/Core (= 5.10.0)
|
||||
- SDWebImage/Core (5.10.0)
|
||||
- shared_preferences (0.0.1):
|
||||
- Flutter
|
||||
- sqflite (0.0.2):
|
||||
- Flutter
|
||||
- FMDB (>= 2.7.5)
|
||||
- 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.2):
|
||||
- Starscream (~> 4.0)
|
||||
- SwiftyGif (5.3.0)
|
||||
- url_launcher (0.0.1):
|
||||
- Flutter
|
||||
- video_compress (0.3.0):
|
||||
- Flutter
|
||||
- video_player (0.0.1):
|
||||
- Flutter
|
||||
- wakelock (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`)
|
||||
- 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_app_badger (from `.symlinks/plugins/flutter_app_badger/ios`)
|
||||
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
|
||||
- image_picker (from `.symlinks/plugins/image_picker/ios`)
|
||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||
- StreamChatClient
|
||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||
- video_compress (from `.symlinks/plugins/video_compress/ios`)
|
||||
- video_player (from `.symlinks/plugins/video_player/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- Firebase
|
||||
- FirebaseCore
|
||||
- FirebaseCoreDiagnostics
|
||||
- FirebaseInstallations
|
||||
- FirebaseInstanceID
|
||||
- FirebaseMessaging
|
||||
- FMDB
|
||||
- GoogleDataTransport
|
||||
- GoogleUtilities
|
||||
- nanopb
|
||||
- PromisesObjC
|
||||
- Protobuf
|
||||
- SDWebImage
|
||||
- sqlite3
|
||||
- Starscream
|
||||
- StreamChatClient
|
||||
- SwiftyGif
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
esys_flutter_share:
|
||||
:path: ".symlinks/plugins/esys_flutter_share/ios"
|
||||
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:
|
||||
:path: Flutter
|
||||
flutter_apns:
|
||||
:path: ".symlinks/plugins/flutter_apns/ios"
|
||||
flutter_app_badger:
|
||||
:path: ".symlinks/plugins/flutter_app_badger/ios"
|
||||
flutter_keyboard_visibility:
|
||||
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
|
||||
flutter_local_notifications:
|
||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||
flutter_secure_storage:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
image_gallery_saver:
|
||||
:path: ".symlinks/plugins/image_gallery_saver/ios"
|
||||
image_picker:
|
||||
:path: ".symlinks/plugins/image_picker/ios"
|
||||
path_provider:
|
||||
:path: ".symlinks/plugins/path_provider/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
shared_preferences:
|
||||
:path: ".symlinks/plugins/shared_preferences/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"
|
||||
video_compress:
|
||||
:path: ".symlinks/plugins/video_compress/ios"
|
||||
video_player:
|
||||
:path: ".symlinks/plugins/video_player/ios"
|
||||
wakelock:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4
|
||||
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
|
||||
Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
|
||||
firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659
|
||||
firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75
|
||||
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
|
||||
FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
|
||||
FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
|
||||
FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1
|
||||
FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a
|
||||
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
|
||||
flutter_apns: ddc629f26016140bf52165040b0a8e8869f9ce32
|
||||
flutter_app_badger: 65de4d6f0c34a891df49e6cfb8a1c0496426fa68
|
||||
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
||||
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
|
||||
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
|
||||
image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2
|
||||
image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09
|
||||
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
photo_manager: f7c619c2cc8c2adb8d85c63363babac477de9c67
|
||||
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
|
||||
Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
|
||||
SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee
|
||||
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
sqlite3: 8f7d2078ae27778699a622a94b853285793422a2
|
||||
sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f
|
||||
Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
|
||||
StreamChatClient: fc6419fa7ceda9c048188bb7c8e9f07755ced05f
|
||||
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
|
||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||
video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe
|
||||
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
|
||||
wakelock: bfc7955c418d0db797614075aabbc58a39ab5107
|
||||
|
||||
PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184
|
||||
|
||||
COCOAPODS: 1.10.0
|
||||
@@ -0,0 +1,845 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0BC14C50242B5A7A0028DE94 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC14C4F242B5A7A0028DE94 /* NotificationService.swift */; };
|
||||
0BC14C54242B5A7A0028DE94 /* Notifications.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0BC14C4D242B5A7A0028DE94 /* Notifications.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
7DEC2743BD66C91B700A3B97 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BB2E5E4E236267EDF0D8817 /* Pods_Runner.framework */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
0BC14C52242B5A7A0028DE94 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0BC14C4C242B5A7A0028DE94;
|
||||
remoteInfo = Notifications;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
0BC14C55242B5A7A0028DE94 /* Embed App Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
0BC14C54242B5A7A0028DE94 /* Notifications.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0BC14C4D242B5A7A0028DE94 /* Notifications.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Notifications.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0BC14C4F242B5A7A0028DE94 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
0BC14C51242B5A7A0028DE94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0BC14C5A242B5ED90028DE94 /* Notifications.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Notifications.entitlements; sourceTree = "<group>"; };
|
||||
0BC14C5B242B5FF50028DE94 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
2452A9E77396497EB4CF3072 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
68F846A6DB42D92393F5F7E0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7BF51EE28C89025F73A5211F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
8BB2E5E4E236267EDF0D8817 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
0BC14C4A242B5A7A0028DE94 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7DEC2743BD66C91B700A3B97 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0BC14C4E242B5A7A0028DE94 /* Notifications */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0BC14C5A242B5ED90028DE94 /* Notifications.entitlements */,
|
||||
0BC14C4F242B5A7A0028DE94 /* NotificationService.swift */,
|
||||
0BC14C51242B5A7A0028DE94 /* Info.plist */,
|
||||
);
|
||||
path = Notifications;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||
);
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
0BC14C4E242B5A7A0028DE94 /* Notifications */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
CF168B61BAB91958681C7C21 /* Pods */,
|
||||
BC09A38346C8B2CD72199469 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
0BC14C4D242B5A7A0028DE94 /* Notifications.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0BC14C5B242B5FF50028DE94 /* Runner.entitlements */,
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
97C146F11CF9000F007C117D /* Supporting Files */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F11CF9000F007C117D /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BC09A38346C8B2CD72199469 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BB2E5E4E236267EDF0D8817 /* Pods_Runner.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CF168B61BAB91958681C7C21 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2452A9E77396497EB4CF3072 /* Pods-Runner.debug.xcconfig */,
|
||||
7BF51EE28C89025F73A5211F /* Pods-Runner.release.xcconfig */,
|
||||
68F846A6DB42D92393F5F7E0 /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
0BC14C4C242B5A7A0028DE94 /* Notifications */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0BC14C59242B5A7A0028DE94 /* Build configuration list for PBXNativeTarget "Notifications" */;
|
||||
buildPhases = (
|
||||
0BC14C49242B5A7A0028DE94 /* Sources */,
|
||||
0BC14C4A242B5A7A0028DE94 /* Frameworks */,
|
||||
0BC14C4B242B5A7A0028DE94 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Notifications;
|
||||
productName = Notifications;
|
||||
productReference = 0BC14C4D242B5A7A0028DE94 /* Notifications.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
9E02B5C38D6CC2455D9E48E9 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
5702861DACEDB848A3E454E8 /* [CP] Embed Pods Frameworks */,
|
||||
0BC14C55242B5A7A0028DE94 /* Embed App Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
0BC14C53242B5A7A0028DE94 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1140;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = "The Chromium Authors";
|
||||
TargetAttributes = {
|
||||
0BC14C4C242B5A7A0028DE94 = {
|
||||
CreatedOnToolsVersion = 11.4;
|
||||
DevelopmentTeam = EHV7XZLAHA;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
DevelopmentTeam = EHV7XZLAHA;
|
||||
LastSwiftMigration = 1100;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
0BC14C4C242B5A7A0028DE94 /* Notifications */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
0BC14C4B242B5A7A0028DE94 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
5702861DACEDB848A3E454E8 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
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}/esys_flutter_share/esys_flutter_share.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_apns/flutter_apns.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_app_badger/flutter_app_badger.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/image_gallery_saver/image_gallery_saver.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}/photo_manager/photo_manager.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_compress/video_compress.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}/esys_flutter_share.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_app_badger.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}/flutter_secure_storage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_gallery_saver.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}/photo_manager.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_compress.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
9E02B5C38D6CC2455D9E48E9 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
0BC14C49242B5A7A0028DE94 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0BC14C50242B5A7A0028DE94 /* NotificationService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
0BC14C53242B5A7A0028DE94 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 0BC14C4C242B5A7A0028DE94 /* Notifications */;
|
||||
targetProxy = 0BC14C52242B5A7A0028DE94 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C146FB1CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
0BC14C56242B5A7A0028DE94 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = Notifications/Notifications.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EHV7XZLAHA;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = Notifications/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter.Notifications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter.Notifications";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0BC14C57242B5A7A0028DE94 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = Notifications/Notifications.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EHV7XZLAHA;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = Notifications/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter.Notifications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter.Notifications";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0BC14C58242B5A7A0028DE94 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = Notifications/Notifications.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EHV7XZLAHA;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = Notifications/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter.Notifications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter.Notifications";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = EHV7XZLAHA;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = EHV7XZLAHA;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = EHV7XZLAHA;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
0BC14C59242B5A7A0028DE94 /* Build configuration list for PBXNativeTarget "Notifications" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0BC14C56242B5A7A0028DE94 /* Debug */,
|
||||
0BC14C57242B5A7A0028DE94 /* Release */,
|
||||
0BC14C58242B5A7A0028DE94 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147031CF9000F007C117D /* Debug */,
|
||||
97C147041CF9000F007C117D /* Release */,
|
||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147061CF9000F007C117D /* Debug */,
|
||||
97C147071CF9000F007C117D /* Release */,
|
||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1160"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0BC14C4C242B5A7A0028DE94"
|
||||
BuildableName = "Notifications.appex"
|
||||
BlueprintName = "Notifications"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,45 @@
|
||||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
let sharedDefaults = UserDefaults(suiteName: "group.io.getstream.flutter")
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
if let messageQueue = sharedDefaults?.stringArray(forKey: "messageQueue") {
|
||||
UserDefaults.standard.setValue(messageQueue, forKey: "flutter.messageQueue")
|
||||
sharedDefaults?.removeObject(forKey: "messageQueue")
|
||||
}
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
}
|
||||
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
override func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
if let apiKey = UserDefaults.standard.string(forKey: "flutter.KEY_API_KEY") {
|
||||
sharedDefaults?.setValue(apiKey, forKey: "KEY_API_KEY")
|
||||
}
|
||||
|
||||
if let token = UserDefaults.standard.string(forKey: "flutter.KEY_TOKEN") {
|
||||
sharedDefaults?.setValue(token, forKey: "KEY_TOKEN")
|
||||
}
|
||||
|
||||
if let userId = UserDefaults.standard.string(forKey: "flutter.KEY_USER_ID") {
|
||||
sharedDefaults?.setValue(userId, forKey: "KEY_USER_ID")
|
||||
}
|
||||
}
|
||||
|
||||
override func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
if let messageQueue = sharedDefaults?.stringArray(forKey: "messageQueue") {
|
||||
UserDefaults.standard.setValue(messageQueue, forKey: "flutter.messageQueue")
|
||||
sharedDefaults?.removeObject(forKey: "messageQueue")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 572 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 990 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
@@ -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.
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>ChatSample</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppleMusicUsageDescription</key>
|
||||
<string>Used to send message attachments</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Used to send message attachments</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Used to send message attachments</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Used to send message attachments</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.io.getstream.flutter</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,6 @@
|
||||
# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app
|
||||
# apple_id("[[APPLE_ID]]") # Your Apple email address
|
||||
|
||||
|
||||
# For more information about the Appfile, see:
|
||||
# https://docs.fastlane.tools/advanced/#appfile
|
||||
@@ -0,0 +1,46 @@
|
||||
fastlane_version "2.162.0"
|
||||
default_platform :ios
|
||||
|
||||
before_all do
|
||||
if is_ci
|
||||
setup_ci()
|
||||
end
|
||||
end
|
||||
|
||||
desc "Installs all Certs and Profiles necessary for development and ad-hoc"
|
||||
lane :match_me do
|
||||
match(
|
||||
type: "adhoc",
|
||||
app_identifier: [
|
||||
"io.getstream.flutter",
|
||||
"io.getstream.flutter.Notifications"
|
||||
],
|
||||
readonly: is_ci,
|
||||
force_for_new_devices: true
|
||||
)
|
||||
end
|
||||
|
||||
platform :ios do
|
||||
desc "Deploy build to Firebase"
|
||||
lane :deploy_to_firebase do
|
||||
match_me
|
||||
|
||||
gym(
|
||||
workspace: "./Runner.xcworkspace",
|
||||
scheme: "Runner",
|
||||
export_method: "ad-hoc",
|
||||
export_options: "./fastlane/beta_gym_export_options.plist",
|
||||
silent: true,
|
||||
clean: true,
|
||||
include_symbols: true,
|
||||
output_directory: "./dist"
|
||||
)
|
||||
|
||||
message = changelog_from_git_commits(commits_count: 10)
|
||||
|
||||
firebase_app_distribution(
|
||||
app: "1:674907137625:ios:cafb9fb076a453c4d7f348",
|
||||
groups: "ios-stream-testers"
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
git_url("https://github.com/GetStream/ios-certificates")
|
||||
|
||||
storage_mode("git")
|
||||
|
||||
username("salvatore@getstream.io")
|
||||
|
||||
team_id("EHV7XZLAHA")
|
||||
|
||||
# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
|
||||
|
||||
# username("user@fastlane.tools") # Your Apple Developer Portal username
|
||||
|
||||
# For all available options run `fastlane match --help`
|
||||
# Remove the # in the beginning of the line to enable the other options
|
||||
|
||||
# The docs are available on https://docs.fastlane.tools/actions/match
|
||||
@@ -0,0 +1,5 @@
|
||||
# Autogenerated by fastlane
|
||||
#
|
||||
# Ensure this file is checked in to source control!
|
||||
|
||||
gem 'fastlane-plugin-firebase_app_distribution'
|
||||
@@ -0,0 +1,37 @@
|
||||
fastlane documentation
|
||||
================
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
Install _fastlane_ using
|
||||
```
|
||||
[sudo] gem install fastlane -NV
|
||||
```
|
||||
or alternatively using `brew install fastlane`
|
||||
|
||||
# Available Actions
|
||||
### match_me
|
||||
```
|
||||
fastlane match_me
|
||||
```
|
||||
Installs all Certs and Profiles necessary for development and ad-hoc
|
||||
|
||||
----
|
||||
|
||||
## iOS
|
||||
### ios deploy_to_firebase
|
||||
```
|
||||
fastlane ios deploy_to_firebase
|
||||
```
|
||||
Deploy build to Firebase
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
|
||||
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
|
||||
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>iCloudContainerEnvironment</key>
|
||||
<string>Development</string>
|
||||
<key>provisioningProfiles</key>
|
||||
<dict>
|
||||
<key>io.getstream.flutter</key>
|
||||
<string>match AdHoc io.getstream.flutter</string>
|
||||
<key>io.getstream.flutter.Notifications</key>
|
||||
<string>match AdHoc io.getstream.flutter.Notifications</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites>
|
||||
<testsuite name="fastlane.lanes">
|
||||
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: Verifying fastlane version" time="0.000383">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: default_platform" time="0.000196">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: is_ci" time="0.000194">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="3: is_ci" time="0.000177">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="4: match" time="36.832536">
|
||||
|
||||
</testcase>
|
||||
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
@@ -0,0 +1,328 @@
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/stream_version.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'choose_user_page.dart';
|
||||
import 'main.dart';
|
||||
|
||||
class AdvancedOptionsPage extends StatefulWidget {
|
||||
@override
|
||||
_AdvancedOptionsPageState createState() => _AdvancedOptionsPageState();
|
||||
}
|
||||
|
||||
class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
final TextEditingController _apiKeyController = TextEditingController();
|
||||
String _apiKeyError;
|
||||
|
||||
final TextEditingController _userIdController = TextEditingController();
|
||||
String _userIdError;
|
||||
|
||||
final TextEditingController _userTokenController = TextEditingController();
|
||||
String _userTokenError;
|
||||
|
||||
final TextEditingController _usernameController = TextEditingController();
|
||||
|
||||
bool loading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
resizeToAvoidBottomPadding: false,
|
||||
appBar: AppBar(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
brightness: Theme.of(context).brightness,
|
||||
title: Text(
|
||||
'Advanced Options',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold
|
||||
.copyWith(color: StreamChatTheme.of(context).colorTheme.black),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: StreamSvgIcon.left(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
body: Builder(
|
||||
builder: (context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _apiKeyController,
|
||||
onChanged: (_) {
|
||||
if (_apiKeyError != null) {
|
||||
setState(() {
|
||||
_apiKeyError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
setState(() {
|
||||
_apiKeyError = 'Please enter the Chat API Key';
|
||||
});
|
||||
return _apiKeyError;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
errorStyle: TextStyle(height: 0, fontSize: 0),
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _apiKeyError != null
|
||||
? StreamChatTheme.of(context).colorTheme.accentRed
|
||||
: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText:
|
||||
'Chat API Key ${_apiKeyError != null ? ': $_apiKeyError' : ''}',
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: _userIdController,
|
||||
onChanged: (_) {
|
||||
if (_userIdError != null) {
|
||||
setState(() {
|
||||
_userIdError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
setState(() {
|
||||
_userIdError = 'Please enter the User ID';
|
||||
});
|
||||
return _userIdError;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
decoration: InputDecoration(
|
||||
errorStyle: TextStyle(height: 0, fontSize: 0),
|
||||
labelStyle: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
color: _userIdError != null
|
||||
? StreamChatTheme.of(context).colorTheme.accentRed
|
||||
: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText:
|
||||
'User ID ${_userIdError != null ? ': $_userIdError' : ''}',
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
TextFormField(
|
||||
onChanged: (_) {
|
||||
if (_userTokenError != null) {
|
||||
setState(() {
|
||||
_userTokenError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
controller: _userTokenController,
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
setState(() {
|
||||
_userTokenError = 'Please enter the user token';
|
||||
});
|
||||
return _userTokenError;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
decoration: InputDecoration(
|
||||
errorStyle: TextStyle(height: 0, fontSize: 0),
|
||||
labelStyle: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
color: _userTokenError != null
|
||||
? StreamChatTheme.of(context).colorTheme.accentRed
|
||||
: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText:
|
||||
'User Token ${_userTokenError != null ? ': $_userTokenError' : ''}',
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: _usernameController,
|
||||
textInputAction: TextInputAction.done,
|
||||
decoration: InputDecoration(
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText: 'Username (optional)',
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
RaisedButton(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: Colors.white,
|
||||
elevation: 0,
|
||||
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;
|
||||
}
|
||||
if (_formKey.currentState.validate()) {
|
||||
final apiKey = _apiKeyController.text;
|
||||
final userId = _userIdController.text;
|
||||
final userToken = _userTokenController.text;
|
||||
final username = _usernameController.text;
|
||||
|
||||
loading = true;
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
barrierColor:
|
||||
StreamChatTheme.of(context).colorTheme.overlay,
|
||||
builder: (context) => Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
),
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final client = Client(
|
||||
apiKey,
|
||||
logLevel: Level.INFO,
|
||||
)..chatPersistenceClient = chatPersistentClient;
|
||||
|
||||
try {
|
||||
await client.setUser(
|
||||
User(id: userId, extraData: {
|
||||
'name': username,
|
||||
}),
|
||||
userToken,
|
||||
);
|
||||
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
secureStorage.write(
|
||||
key: kStreamApiKey,
|
||||
value: apiKey,
|
||||
);
|
||||
secureStorage.write(
|
||||
key: kStreamUserId,
|
||||
value: userId,
|
||||
);
|
||||
secureStorage.write(
|
||||
key: kStreamToken,
|
||||
value: userToken,
|
||||
);
|
||||
} catch (e) {
|
||||
var errorText = 'Error connecting, retry';
|
||||
if (e is Map) {
|
||||
errorText = e['message'] ?? errorText;
|
||||
}
|
||||
Navigator.pop(context);
|
||||
setState(() {
|
||||
_apiKeyError = errorText;
|
||||
});
|
||||
loading = false;
|
||||
await client.disconnect();
|
||||
return;
|
||||
}
|
||||
loading = false;
|
||||
await Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
Routes.APP,
|
||||
ModalRoute.withName(Routes.APP),
|
||||
arguments: client,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
StreamVersion(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'main.dart';
|
||||
import 'routes/routes.dart';
|
||||
|
||||
/// Detail screen for a 1:1 chat correspondence
|
||||
class ChatInfoScreen extends StatefulWidget {
|
||||
/// User in consideration
|
||||
final User user;
|
||||
|
||||
const ChatInfoScreen({Key key, this.user}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ChatInfoScreenState createState() => _ChatInfoScreenState();
|
||||
}
|
||||
|
||||
class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
body: ListView(
|
||||
children: [
|
||||
_buildUserHeader(),
|
||||
Container(
|
||||
height: 8.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
_buildOptionListTiles(),
|
||||
Container(
|
||||
height: 8.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
if ([
|
||||
'admin',
|
||||
'owner',
|
||||
].contains(channel.state.members
|
||||
.firstWhere((m) => m.userId == channel.client.state.user.id,
|
||||
orElse: () => null)
|
||||
?.role))
|
||||
_buildDeleteListTile(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUserHeader() {
|
||||
return Material(
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
child: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: UserAvatar(
|
||||
user: widget.user,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 72.0,
|
||||
maxHeight: 72.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(36.0),
|
||||
showOnlineStatus: false,
|
||||
),
|
||||
),
|
||||
//SizedBox(height: 4.0),
|
||||
Text(
|
||||
widget.user.name,
|
||||
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 7.0),
|
||||
_buildConnectedTitleState(),
|
||||
SizedBox(height: 15.0),
|
||||
OptionListTile(
|
||||
title: '@${widget.user.id}',
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
trailing: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(
|
||||
widget.user.name,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
fontSize: 16.0),
|
||||
),
|
||||
),
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
top: 21,
|
||||
left: 16,
|
||||
child: InkWell(
|
||||
child: StreamSvgIcon.left(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOptionListTiles() {
|
||||
var channel = StreamChannel.of(context);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// _OptionListTile(
|
||||
// title: 'Notifications',
|
||||
// leading: StreamSvgIcon.Icon_notification(
|
||||
// size: 24.0,
|
||||
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
// ),
|
||||
// trailing: CupertinoSwitch(
|
||||
// value: true,
|
||||
// onChanged: (val) {},
|
||||
// ),
|
||||
// onTap: () {},
|
||||
// ),
|
||||
StreamBuilder<bool>(
|
||||
stream: StreamChannel.of(context).channel.isMutedStream,
|
||||
builder: (context, snapshot) {
|
||||
return OptionListTile(
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
title: 'Mute user',
|
||||
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
||||
child: StreamSvgIcon.mute(
|
||||
size: 24.0,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
trailing: snapshot.data == null
|
||||
? CircularProgressIndicator()
|
||||
: CupertinoSwitch(
|
||||
value: snapshot.data,
|
||||
onChanged: (val) {
|
||||
if (snapshot.data) {
|
||||
channel.channel.unmute();
|
||||
} else {
|
||||
channel.channel.mute();
|
||||
}
|
||||
},
|
||||
),
|
||||
onTap: () {},
|
||||
);
|
||||
}),
|
||||
// _OptionListTile(
|
||||
// title: 'Block User',
|
||||
// leading: StreamSvgIcon.Icon_user_delete(
|
||||
// size: 24.0,
|
||||
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
// ),
|
||||
// trailing: CupertinoSwitch(
|
||||
// value: widget.user.banned,
|
||||
// onChanged: (val) {
|
||||
// if (widget.user.banned) {
|
||||
// channel.channel.shadowBan(widget.user.id, {});
|
||||
// } else {
|
||||
// channel.channel.unbanUser(widget.user.id);
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// onTap: () {},
|
||||
// ),
|
||||
OptionListTile(
|
||||
title: 'Photos & Videos',
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: StreamSvgIcon.pictures(
|
||||
size: 36.0,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
trailing: StreamSvgIcon.right(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
onTap: () {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: MessageSearchBloc(
|
||||
child: ChannelMediaDisplayScreen(
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
paginationParams: PaginationParams(limit: 20),
|
||||
onShowMessage: (m, c) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = m;
|
||||
final channel = client.channel(
|
||||
c.type,
|
||||
id: c.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
OptionListTile(
|
||||
title: 'Files',
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: StreamSvgIcon.files(
|
||||
size: 32.0,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
trailing: StreamSvgIcon.right(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
onTap: () {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: MessageSearchBloc(
|
||||
child: ChannelFileDisplayScreen(
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
paginationParams: PaginationParams(limit: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
OptionListTile(
|
||||
title: 'Shared groups',
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
||||
child: StreamSvgIcon.Icon_group(
|
||||
size: 24.0,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
trailing: StreamSvgIcon.right(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _SharedGroupsScreen(
|
||||
StreamChat.of(context).user, widget.user)));
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeleteListTile() {
|
||||
return OptionListTile(
|
||||
title: 'Delete Conversation',
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
titleTextStyle: StreamChatTheme.of(context).textTheme.body.copyWith(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
||||
child: StreamSvgIcon.delete(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
size: 24.0,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
_showDeleteDialog();
|
||||
},
|
||||
titleColor: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeleteDialog() 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,
|
||||
),
|
||||
);
|
||||
var channel = StreamChannel.of(context).channel;
|
||||
if (res == true) {
|
||||
await channel.delete().then((value) {
|
||||
Navigator.pop(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildConnectedTitleState() {
|
||||
var alternativeWidget;
|
||||
|
||||
final otherMember = widget.user;
|
||||
|
||||
if (otherMember != null) {
|
||||
if (otherMember.online) {
|
||||
alternativeWidget = Text(
|
||||
'Online',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
);
|
||||
} else {
|
||||
alternativeWidget = Text(
|
||||
'Last seen ${Jiffy(otherMember.lastActive).fromNow()}',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.user.online)
|
||||
Material(
|
||||
type: MaterialType.circle,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
width: 24,
|
||||
height: 12,
|
||||
),
|
||||
child: Material(
|
||||
shape: CircleBorder(),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentGreen,
|
||||
),
|
||||
),
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
alternativeWidget,
|
||||
if (widget.user.online)
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SharedGroupsScreen extends StatefulWidget {
|
||||
final User mainUser;
|
||||
final User otherUser;
|
||||
|
||||
_SharedGroupsScreen(this.mainUser, this.otherUser);
|
||||
|
||||
@override
|
||||
__SharedGroupsScreenState createState() => __SharedGroupsScreenState();
|
||||
}
|
||||
|
||||
class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chat = StreamChat.of(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
'Shared Groups',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16.0),
|
||||
),
|
||||
leading: Center(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
child: StreamSvgIcon.left(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
body: FutureBuilder<List<Channel>>(
|
||||
future: chat.client.queryChannels(
|
||||
filter: {
|
||||
r'$and': [
|
||||
{
|
||||
'members': {
|
||||
r'$in': [widget.otherUser.id],
|
||||
},
|
||||
},
|
||||
{
|
||||
'members': {
|
||||
r'$in': [widget.mainUser.id],
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.data.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
StreamSvgIcon.message(
|
||||
size: 136.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
SizedBox(height: 16.0),
|
||||
Text(
|
||||
'No Shared Groups',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0),
|
||||
Text(
|
||||
'Group shared with User will appear here.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data.length,
|
||||
itemBuilder: (context, position) {
|
||||
return StreamChannel(
|
||||
channel: snapshot.data[position],
|
||||
child: _buildListTile(snapshot.data[position]),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListTile(Channel channel) {
|
||||
var extraData = channel.extraData;
|
||||
var members = channel.state.members;
|
||||
|
||||
var textStyle = TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold);
|
||||
|
||||
return Container(
|
||||
height: 64.0,
|
||||
child: LayoutBuilder(builder: (context, constraints) {
|
||||
String title;
|
||||
if (extraData['name'] == null) {
|
||||
final otherMembers = members.where(
|
||||
(member) => member.userId != StreamChat.of(context).user.id);
|
||||
if (otherMembers.isNotEmpty) {
|
||||
final maxWidth = constraints.maxWidth;
|
||||
final maxChars = maxWidth / textStyle.fontSize;
|
||||
var currentChars = 0;
|
||||
final currentMembers = <Member>[];
|
||||
otherMembers.forEach((element) {
|
||||
final newLength = currentChars + element.user.name.length;
|
||||
if (newLength < maxChars) {
|
||||
currentChars = newLength;
|
||||
currentMembers.add(element);
|
||||
}
|
||||
});
|
||||
|
||||
final exceedingMembers =
|
||||
otherMembers.length - currentMembers.length;
|
||||
title =
|
||||
'${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
|
||||
} else {
|
||||
title = 'No title';
|
||||
}
|
||||
} else {
|
||||
title = extraData['name'];
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ChannelImage(
|
||||
channel: channel,
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: 40.0, maxHeight: 40.0),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: textStyle,
|
||||
)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'${channel.memberCount} members',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 1.0,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
typedef ChipBuilder<T> = Widget Function(BuildContext context, T chip);
|
||||
typedef OnChipAdded<T> = void Function(T chip);
|
||||
typedef OnChipRemoved<T> = void Function(T chip);
|
||||
|
||||
class ChipsInputTextField<T> extends StatefulWidget {
|
||||
final TextEditingController controller;
|
||||
final FocusNode focusNode;
|
||||
final ValueChanged<String> onInputChanged;
|
||||
final ChipBuilder<T> chipBuilder;
|
||||
final OnChipAdded<T> onChipAdded;
|
||||
final OnChipRemoved<T> onChipRemoved;
|
||||
final String hint;
|
||||
|
||||
const ChipsInputTextField({
|
||||
Key key,
|
||||
@required this.chipBuilder,
|
||||
@required this.controller,
|
||||
this.onInputChanged,
|
||||
this.focusNode,
|
||||
this.onChipAdded,
|
||||
this.onChipRemoved,
|
||||
this.hint = 'Type a name',
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
ChipInputTextFieldState<T> createState() => ChipInputTextFieldState<T>();
|
||||
}
|
||||
|
||||
class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
|
||||
final _chips = <T>{};
|
||||
bool _pauseItemAddition = false;
|
||||
|
||||
void addItem(T item) {
|
||||
setState(() => _chips.add(item));
|
||||
if (widget.onChipAdded != null) widget.onChipAdded(item);
|
||||
}
|
||||
|
||||
void removeItem(T item) {
|
||||
setState(() {
|
||||
_chips.remove(item);
|
||||
if (_chips.isEmpty) resumeItemAddition();
|
||||
});
|
||||
if (widget.onChipRemoved != null) widget.onChipRemoved(item);
|
||||
}
|
||||
|
||||
void pauseItemAddition() {
|
||||
if (!_pauseItemAddition) {
|
||||
setState(() => _pauseItemAddition = true);
|
||||
}
|
||||
widget.focusNode?.unfocus();
|
||||
}
|
||||
|
||||
void resumeItemAddition() {
|
||||
if (_pauseItemAddition) {
|
||||
setState(() => _pauseItemAddition = false);
|
||||
}
|
||||
widget.focusNode?.requestFocus();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _pauseItemAddition ? resumeItemAddition : null,
|
||||
child: Material(
|
||||
elevation: 1,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: Container(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Text(
|
||||
'TO:',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 4.0,
|
||||
children: _chips.map((item) {
|
||||
return widget.chipBuilder(context, item);
|
||||
}).toList(),
|
||||
),
|
||||
if (!_pauseItemAddition)
|
||||
TextField(
|
||||
controller: widget.controller,
|
||||
onChanged: widget.onInputChanged,
|
||||
focusNode: widget.focusNode,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
disabledBorder: InputBorder.none,
|
||||
contentPadding: const EdgeInsets.only(top: 4.0),
|
||||
hintText: widget.hint,
|
||||
hintStyle: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: IconButton(
|
||||
icon: _chips.isEmpty
|
||||
? StreamSvgIcon.user(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
size: 24,
|
||||
)
|
||||
: StreamSvgIcon.userAdd(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
size: 24,
|
||||
),
|
||||
onPressed: resumeItemAddition,
|
||||
alignment: Alignment.topRight,
|
||||
visualDensity: VisualDensity.compact,
|
||||
padding: const EdgeInsets.all(0),
|
||||
splashRadius: 24,
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
import 'package:example/stream_version.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'routes/routes.dart';
|
||||
|
||||
const kStreamApiKey = 'STREAM_API_KEY';
|
||||
const kStreamUserId = 'STREAM_USER_ID';
|
||||
const kStreamToken = 'STREAM_TOKEN';
|
||||
const kDefaultStreamApiKey = 'kv7mcsxr24p8';
|
||||
|
||||
class ChooseUserPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final users = <String, User>{
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FsdmF0b3JlIn0.pgiJz7sIc7iP29BHKFwe3nLm5-OaR_1l2P-SlgiC9a8':
|
||||
User(
|
||||
id: 'salvatore',
|
||||
extraData: {
|
||||
'name': 'Salvatore Giordano',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-USKK9FFRT-30c415e207a9-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FoaWwifQ.WnIUoB5gR2kcAsFhiDvkiD6zdHXZ-VSU2aQWWkhsvfo':
|
||||
User(
|
||||
id: 'sahil',
|
||||
extraData: {
|
||||
'name': 'Sahil Kumar',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-U01EYU51M89-bbc152b40321-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYmVuIn0.nAz2sNFGQwY7rl2Og2z3TGHUsdpnN53tOsUglJFvLmg':
|
||||
User(
|
||||
id: 'ben',
|
||||
extraData: {
|
||||
'name': 'Ben Golden',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-U01AXAF23MG-f57403a3cb0d-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidGhpZXJyeSJ9.lEq6TrZtHzjoNtf7HHRufUPyGo_pa8vg4_XhEBp4ckY':
|
||||
User(
|
||||
id: 'thierry',
|
||||
extraData: {
|
||||
'name': 'Thierry Schellenbach',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-U02RM6X6D-g28a1278a98e-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidG9tbWFzbyJ9.GLSI0ESshERMo2WjUpysD709NEtn1zmGimUN2an7g9o':
|
||||
User(
|
||||
id: 'tommaso',
|
||||
extraData: {
|
||||
'name': 'Tommaso Barbugli',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-U02U7SJP4-0f65a5997877-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZGV2ZW4ifQ.z3zI4PqJnNhc-1o-VKcmb6BnnQ0oxFNCRHwEulHqcWc':
|
||||
User(
|
||||
id: 'deven',
|
||||
extraData: {
|
||||
'name': 'Deven Joshi',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-U01AM7ELPTL-8a60da32704c-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoibmVldmFzaCJ9.3EdHegTxibrz3A9cTiKmpEyawwcCVB8FXnoFzr4eKvw':
|
||||
User(
|
||||
id: 'neevash',
|
||||
extraData: {
|
||||
'name': 'Neevash Ramdial',
|
||||
'image':
|
||||
'https://ca.slack-edge.com/T02RM6X6B-U01DZ046DS8-b00d321d2880-512',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicWF0ZXN0MSJ9.fnelU7HcP7QoEEsCGteNlF1fppofzNlrnpDQuIgeKCU':
|
||||
User(
|
||||
id: 'qatest1',
|
||||
extraData: {
|
||||
'name': 'QA test 1',
|
||||
},
|
||||
),
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicWF0ZXN0MiJ9.vSCqAEbs2WVmMWsOsa7065Fsjq-rsTih6qsHPynl7XM':
|
||||
User(
|
||||
id: 'qatest2',
|
||||
extraData: {
|
||||
'name': 'QA test 2',
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 34,
|
||||
bottom: 20,
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
'assets/logo.svg',
|
||||
height: 40,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 13.0),
|
||||
child: Text(
|
||||
'Welcome to Stream Chat',
|
||||
style: StreamChatTheme.of(context).textTheme.title,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Select a user to try the Flutter SDK:',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 32),
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, i) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
);
|
||||
},
|
||||
itemCount: users.length + 1,
|
||||
itemBuilder: (context, i) {
|
||||
return [
|
||||
...users.entries.map((entry) {
|
||||
final token = entry.key;
|
||||
final user = entry.value;
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
barrierColor: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.overlay,
|
||||
builder: (context) => Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
),
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
final client = StreamChat.of(context).client;
|
||||
client.apiKey = kDefaultStreamApiKey;
|
||||
await client.setUser(
|
||||
user,
|
||||
token,
|
||||
);
|
||||
|
||||
secureStorage.write(
|
||||
key: kStreamApiKey,
|
||||
value: kDefaultStreamApiKey,
|
||||
);
|
||||
secureStorage.write(
|
||||
key: kStreamUserId,
|
||||
value: user.id,
|
||||
);
|
||||
secureStorage.write(
|
||||
key: kStreamToken,
|
||||
value: token,
|
||||
);
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
Routes.HOME,
|
||||
ModalRoute.withName(Routes.HOME),
|
||||
);
|
||||
},
|
||||
leading: UserAvatar(
|
||||
user: user,
|
||||
constraints: BoxConstraints.tight(
|
||||
Size.fromRadius(20),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
user.name,
|
||||
style:
|
||||
StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
subtitle: Text(
|
||||
'Stream test account',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.grey,
|
||||
),
|
||||
),
|
||||
trailing: StreamSvgIcon.arrow_right(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, Routes.ADVANCED_OPTIONS);
|
||||
},
|
||||
leading: CircleAvatar(
|
||||
child: StreamSvgIcon.settings(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyWhisper,
|
||||
),
|
||||
title: Text(
|
||||
'Advanced Options',
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
subtitle: Text(
|
||||
'Custom settings',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
trailing: SvgPicture.asset(
|
||||
'assets/icon_arrow_right.svg',
|
||||
height: 24,
|
||||
width: 24,
|
||||
clipBehavior: Clip.none,
|
||||
),
|
||||
),
|
||||
][i];
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
StreamVersion(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Fifth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
|
||||
///
|
||||
/// Customizing how messages are rendered is another very common use-case that the SDK supports easily.
|
||||
///
|
||||
/// Replace the built-in message component with your own is done by passing it as a builder function to the [MessageListView] widget.
|
||||
///
|
||||
/// The message builder function will get the usual [BuildContext] argument as well as the [Message] object and its position inside the list.
|
||||
///
|
||||
/// If you look at the code you can see that we use [StreamChat.of] to retrieve the current user so that we can style messages own messages in a different way.
|
||||
///
|
||||
/// Since custom widgets and builders are always children of [StreamChat] or part of a [Channel],
|
||||
/// you can use [StreamChat.of], [StreamChannel.of] and [StreamChatTheme.of] to use the API client directly
|
||||
/// or to retrieve outer scope needed such as messages from the [Channel.state].
|
||||
void main() async {
|
||||
final client = Client(
|
||||
's2dxdhpxd94g',
|
||||
logLevel: Level.INFO,
|
||||
);
|
||||
|
||||
await client.setUser(
|
||||
User(id: 'super-band-9'),
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
|
||||
);
|
||||
|
||||
runApp(MyApp(client));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final Client client;
|
||||
|
||||
MyApp(this.client);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
child: child,
|
||||
client: client,
|
||||
),
|
||||
home: ChannelListPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelListPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
filter: {
|
||||
'members': {
|
||||
'\$in': [StreamChat.of(context).user.id],
|
||||
}
|
||||
},
|
||||
sort: [SortOption('last_message_at')],
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ChannelHeader(),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(
|
||||
messageBuilder: _messageBuilder,
|
||||
),
|
||||
),
|
||||
MessageInput(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _messageBuilder(
|
||||
BuildContext context,
|
||||
MessageDetails details,
|
||||
List<Message> messages,
|
||||
) {
|
||||
final message = details.message;
|
||||
final isCurrentUser = StreamChat.of(context).user.id == message.user.id;
|
||||
final textAlign = isCurrentUser ? TextAlign.right : TextAlign.left;
|
||||
final color = isCurrentUser ? Colors.blueGrey : Colors.blue;
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: color, width: 1),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5.0),
|
||||
),
|
||||
),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
message.text,
|
||||
textAlign: textAlign,
|
||||
),
|
||||
subtitle: Text(
|
||||
message.user.extraData['name'],
|
||||
textAlign: textAlign,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Sixth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
|
||||
///
|
||||
/// The Flutter SDK comes with a fully designed set of widgets which you can customize to fit with your application style and typography.
|
||||
/// Changing the theme of Chat widgets works in a very similar way that [MaterialApp] and [Theme] do.
|
||||
///
|
||||
/// Out of the box all chat widgets use their own default styling, there are two ways to change the styling:
|
||||
///
|
||||
/// 1. Initialize the [StreamChatTheme] from your existing [MaterialApp] style
|
||||
/// 2. Construct a custom theme and provide all the customizations needed
|
||||
///
|
||||
/// First we create a new Material [Theme] and pick [Colors.green] as swatch color. The theme is then passed to [MaterialApp] as usual.
|
||||
///
|
||||
/// Then we create a new [StreamChatTheme] from the green theme we just created.
|
||||
/// After saving the app you will see the UI will update several widgets to match with the new color.
|
||||
///
|
||||
/// We also change the message color posted by the current user.
|
||||
/// You can perform these more granular style changes using [StreamChatTheme.copyWith].
|
||||
void main() async {
|
||||
final client = Client(
|
||||
's2dxdhpxd94g',
|
||||
logLevel: Level.INFO,
|
||||
);
|
||||
|
||||
await client.setUser(
|
||||
User(id: 'super-band-9'),
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
|
||||
);
|
||||
|
||||
runApp(MyApp(client));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final Client client;
|
||||
|
||||
MyApp(this.client);
|
||||
|
||||
@override
|
||||
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: colorTheme.black,
|
||||
messageText: TextStyle(
|
||||
color: colorTheme.white,
|
||||
),
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
return MaterialApp(
|
||||
theme: themeData,
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
child: child,
|
||||
client: client,
|
||||
streamChatThemeData: customTheme,
|
||||
);
|
||||
},
|
||||
home: ChannelListPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelListPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
filter: {
|
||||
'members': {
|
||||
'\$in': [StreamChat.of(context).user.id],
|
||||
}
|
||||
},
|
||||
sort: [SortOption('last_message_at')],
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ChannelHeader(),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(
|
||||
threadBuilder: (_, parentMessage) {
|
||||
return ThreadPage(
|
||||
parent: parentMessage,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
MessageInput(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadPage extends StatelessWidget {
|
||||
final Message parent;
|
||||
|
||||
ThreadPage({
|
||||
Key key,
|
||||
this.parent,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ThreadHeader(
|
||||
parent: parent,
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(
|
||||
parentMessage: parent,
|
||||
),
|
||||
),
|
||||
MessageInput(
|
||||
parentMessage: parent,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Third step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
|
||||
///
|
||||
/// So far you’ve learned how to use the default widgets.
|
||||
/// The library has been designed with composition in mind and to allow all common customizations to be very easy.
|
||||
/// This means that you can change any component in your application by swapping the default widgets with the ones you build yourself.
|
||||
///
|
||||
/// Let’s see how we can make some changes to the SDK’s UI components.
|
||||
/// We start by changing how channel previews are shown in the channel list and include the number of unread messages for each.
|
||||
///
|
||||
/// We're passing a custom widget to [ChannelListView.channelPreviewBuilder], this will override the default [ChannelPreview] and allows you to create one yourself.
|
||||
///
|
||||
/// There are a couple interesting things we do in this widget:
|
||||
///
|
||||
/// - Instead of creating a whole new style for the channel name, we inherit the text style from the parent theme ([StreamChatTheme.of]) and only change the color attribute
|
||||
///
|
||||
/// - We loop over the list of channel messages to search for the first not deleted message ([Channel.state.messages])
|
||||
///
|
||||
/// - We retrieve the count of unread messages from [Channel.state]
|
||||
void main() async {
|
||||
final client = Client(
|
||||
's2dxdhpxd94g',
|
||||
logLevel: Level.INFO,
|
||||
);
|
||||
|
||||
await client.setUser(
|
||||
User(id: 'super-band-9'),
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
|
||||
);
|
||||
|
||||
runApp(MyApp(client));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final Client client;
|
||||
|
||||
MyApp(this.client);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
child: child,
|
||||
client: client,
|
||||
),
|
||||
home: ChannelListPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelListPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
filter: {
|
||||
'members': {
|
||||
'\$in': [StreamChat.of(context).user.id],
|
||||
}
|
||||
},
|
||||
channelPreviewBuilder: _channelPreviewBuilder,
|
||||
sort: [SortOption('last_message_at')],
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _channelPreviewBuilder(BuildContext context, Channel channel) {
|
||||
final lastMessage = channel.state.messages.reversed
|
||||
.firstWhere((message) => !message.isDeleted);
|
||||
|
||||
final subtitle = (lastMessage == null ? "nothing yet" : lastMessage.text);
|
||||
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,
|
||||
),
|
||||
title: ChannelName(
|
||||
textStyle:
|
||||
StreamChatTheme.of(context).channelPreviewTheme.title.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(opacity),
|
||||
),
|
||||
),
|
||||
subtitle: Text(subtitle),
|
||||
trailing: channel.state.unreadCount > 0
|
||||
? CircleAvatar(
|
||||
radius: 10,
|
||||
child: Text(channel.state.unreadCount.toString()),
|
||||
)
|
||||
: SizedBox(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ChannelHeader(),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(),
|
||||
),
|
||||
MessageInput(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Fifth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
|
||||
///
|
||||
/// Customizing how messages are rendered is another very common use-case that the SDK supports easily.
|
||||
///
|
||||
/// Replace the built-in message component with your own is done by passing it as a builder function to the [MessageListView] widget.
|
||||
///
|
||||
/// The message builder function will get the usual [BuildContext] argument as well as the [Message] object and its position inside the list.
|
||||
///
|
||||
/// If you look at the code you can see that we use [StreamChat.of] to retrieve the current user so that we can style messages own messages in a different way.
|
||||
///
|
||||
/// Since custom widgets and builders are always children of [StreamChat] or part of a [Channel],
|
||||
/// you can use [StreamChat.of], [StreamChannel.of] and [StreamChatTheme.of] to use the API client directly
|
||||
/// or to retrieve outer scope needed such as messages from the [Channel.state].
|
||||
void main() async {
|
||||
final client = Client(
|
||||
's2dxdhpxd94g',
|
||||
logLevel: Level.INFO,
|
||||
);
|
||||
|
||||
await client.setUser(
|
||||
User(id: 'super-band-9'),
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
|
||||
);
|
||||
|
||||
runApp(MyApp(client));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final Client client;
|
||||
|
||||
MyApp(this.client);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
child: child,
|
||||
client: client,
|
||||
),
|
||||
home: Container(
|
||||
child: ChannelListPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelListPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
filter: {
|
||||
'members': {
|
||||
'\$in': [StreamChat.of(context).user.id],
|
||||
}
|
||||
},
|
||||
sort: [SortOption('last_message_at')],
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ChannelHeader(),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(
|
||||
messageBuilder: _messageBuilder,
|
||||
),
|
||||
),
|
||||
MessageInput(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _messageBuilder(
|
||||
BuildContext context,
|
||||
MessageDetails details,
|
||||
List<Message> messages,
|
||||
) {
|
||||
final message = details.message;
|
||||
final color = details.isMyMessage ? Colors.red : Colors.blue;
|
||||
if (message.isSystem) {
|
||||
return SizedBox();
|
||||
}
|
||||
return MessageWidget(
|
||||
message: message,
|
||||
messageTheme: details.isMyMessage
|
||||
? StreamChatTheme.of(context).ownMessageTheme
|
||||
: StreamChatTheme.of(context).otherMessageTheme,
|
||||
borderSide: BorderSide(
|
||||
color: color,
|
||||
width: 2,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 4,
|
||||
),
|
||||
attachmentBorderSide: BorderSide(
|
||||
color: color,
|
||||
width: 2,
|
||||
),
|
||||
attachmentPadding: EdgeInsets.all(8),
|
||||
borderRadiusGeometry: BorderRadius.vertical(
|
||||
top: !details.isLastUser ? Radius.circular(16) : Radius.zero,
|
||||
bottom: !details.isNextUser ? Radius.circular(16) : Radius.zero,
|
||||
),
|
||||
showSendingIndicator: false,
|
||||
reverse: false,
|
||||
showUserAvatar:
|
||||
details.isNextUser ? DisplayWidget.hide : DisplayWidget.show,
|
||||
showTimestamp: !details.isNextUser,
|
||||
showUsername: !details.isNextUser,
|
||||
showReactions: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import 'main.dart';
|
||||
import 'routes/routes.dart';
|
||||
|
||||
class GroupChatDetailsScreen extends StatefulWidget {
|
||||
final List<User> selectedUsers;
|
||||
|
||||
const GroupChatDetailsScreen({
|
||||
Key key,
|
||||
@required this.selectedUsers,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_GroupChatDetailsScreenState createState() => _GroupChatDetailsScreenState();
|
||||
}
|
||||
|
||||
class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
final _selectedUsers = <User>[];
|
||||
|
||||
TextEditingController _groupNameController;
|
||||
|
||||
bool _isGroupNameEmpty = true;
|
||||
|
||||
int get _totalUsers => _selectedUsers.length;
|
||||
|
||||
void _groupNameListener() {
|
||||
final name = _groupNameController.text;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isGroupNameEmpty = name.isEmpty;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedUsers.addAll(widget.selectedUsers);
|
||||
_groupNameController = TextEditingController()
|
||||
..addListener(_groupNameListener);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_groupNameController?.removeListener(_groupNameListener);
|
||||
_groupNameController?.clear();
|
||||
_groupNameController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
Navigator.pop(context, _selectedUsers);
|
||||
return false;
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
'Name of Group Chat',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.fromHeight(kToolbarHeight),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'NAME',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _groupNameController,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
disabledBorder: InputBorder.none,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
hintText: 'Choose a group chat name',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
StreamNeumorphicButton(
|
||||
child: IconButton(
|
||||
padding: const EdgeInsets.all(0),
|
||||
icon: StreamSvgIcon.check(
|
||||
size: 24,
|
||||
color: _isGroupNameEmpty
|
||||
? StreamChatTheme.of(context).colorTheme.grey
|
||||
: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
onPressed: _isGroupNameEmpty
|
||||
? null
|
||||
: () async {
|
||||
try {
|
||||
final groupName = _groupNameController.text;
|
||||
final client = StreamChat.of(context).client;
|
||||
final channel = client.channel('messaging',
|
||||
id: Uuid().v4(),
|
||||
extraData: {
|
||||
'members': [
|
||||
client.state.user.id,
|
||||
..._selectedUsers.map((e) => e.id),
|
||||
],
|
||||
'name': groupName,
|
||||
});
|
||||
await channel.watch();
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
ModalRoute.withName(Routes.HOME),
|
||||
arguments: ChannelPageArgs(channel: channel),
|
||||
);
|
||||
} catch (err) {
|
||||
_showErrorAlert();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ConnectionStatusBuilder(
|
||||
statusBuilder: (context, status) {
|
||||
String statusString = '';
|
||||
bool showStatus = true;
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus.connected:
|
||||
statusString = 'Connected';
|
||||
showStatus = false;
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
statusString = 'Reconnecting...';
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
statusString = 'Disconnected';
|
||||
break;
|
||||
}
|
||||
return InfoTile(
|
||||
showMessage: showStatus,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: statusString,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
gradient:
|
||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
'$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: ListView.separated(
|
||||
itemCount: _selectedUsers.length + 1,
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyWhisper,
|
||||
),
|
||||
itemBuilder: (_, index) {
|
||||
if (index == _selectedUsers.length) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyWhisper,
|
||||
);
|
||||
}
|
||||
final user = _selectedUsers[index];
|
||||
return ListTile(
|
||||
key: ObjectKey(user),
|
||||
leading: UserAvatar(
|
||||
user: user,
|
||||
constraints: BoxConstraints.tightFor(
|
||||
width: 40,
|
||||
height: 40,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
user.name,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.clear_rounded,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
splashRadius: 24,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_selectedUsers.remove(user);
|
||||
});
|
||||
if (_selectedUsers.isEmpty) {
|
||||
Navigator.pop(context, _selectedUsers);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showErrorAlert() {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
context: context,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16.0),
|
||||
topRight: Radius.circular(16.0),
|
||||
)),
|
||||
builder: (context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 26.0,
|
||||
),
|
||||
StreamSvgIcon.error(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
size: 24.0,
|
||||
),
|
||||
SizedBox(
|
||||
height: 26.0,
|
||||
),
|
||||
Text(
|
||||
'Something went wrong',
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
SizedBox(
|
||||
height: 7.0,
|
||||
),
|
||||
Text('The operation couldn\'t be completed.'),
|
||||
SizedBox(
|
||||
height: 36.0,
|
||||
),
|
||||
Container(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
||||
height: 1.0,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FlatButton(
|
||||
child: Text(
|
||||
'OK',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:example/chat_info_screen.dart';
|
||||
import 'package:example/choose_user_page.dart';
|
||||
import 'package:example/group_info_screen.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_persistence/stream_chat_persistence.dart';
|
||||
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
||||
|
||||
import 'notifications_service.dart';
|
||||
import 'routes/app_routes.dart';
|
||||
import 'routes/routes.dart';
|
||||
import 'search_text_field.dart';
|
||||
|
||||
final chatPersistentClient = StreamChatPersistenceClient(
|
||||
logLevel: Level.INFO,
|
||||
connectionMode: ConnectionMode.background,
|
||||
);
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
|
||||
final apiKey = await secureStorage.read(key: kStreamApiKey);
|
||||
final userId = await secureStorage.read(key: kStreamUserId);
|
||||
|
||||
final client = Client(
|
||||
apiKey ?? kDefaultStreamApiKey,
|
||||
logLevel: Level.INFO,
|
||||
)..chatPersistenceClient = chatPersistentClient;
|
||||
|
||||
if (userId != null) {
|
||||
final token = await secureStorage.read(key: kStreamToken);
|
||||
await client.setUser(
|
||||
User(id: userId),
|
||||
token,
|
||||
);
|
||||
}
|
||||
|
||||
runApp(MyApp(client));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final Client client;
|
||||
|
||||
MyApp(this.client);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<StreamingSharedPreferences>(
|
||||
future: StreamingSharedPreferences.instance,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return SizedBox();
|
||||
}
|
||||
return PreferenceBuilder<int>(
|
||||
preference: snapshot.data.getInt(
|
||||
'theme',
|
||||
defaultValue: 0,
|
||||
),
|
||||
builder: (context, snapshot) => MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
onBackgroundEventReceived: showLocalNotification,
|
||||
child: Builder(
|
||||
builder: (context) => AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
child: child,
|
||||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarColor:
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
themeMode: {
|
||||
-1: ThemeMode.dark,
|
||||
0: ThemeMode.system,
|
||||
1: ThemeMode.light,
|
||||
}[snapshot],
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
initialRoute:
|
||||
client.state.user == null ? Routes.CHOOSE_USER : Routes.HOME,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
int _currentIndex = 0;
|
||||
|
||||
bool _isSelected(int index) => _currentIndex == index;
|
||||
|
||||
List<BottomNavigationBarItem> get _navBarItems {
|
||||
return <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
StreamSvgIcon.message(
|
||||
color: _isSelected(0)
|
||||
? StreamChatTheme.of(context).colorTheme.black
|
||||
: Colors.grey,
|
||||
),
|
||||
Positioned(
|
||||
top: -3,
|
||||
right: -16,
|
||||
child: UnreadIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: 'Chats',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
StreamSvgIcon.mentions(
|
||||
color: _isSelected(1)
|
||||
? StreamChatTheme.of(context).colorTheme.black
|
||||
: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
label: 'Mentions',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = StreamChat.of(context).user;
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ChannelListHeader(
|
||||
onNewChatButtonTap: () {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
},
|
||||
preNavigationCallback: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
},
|
||||
),
|
||||
drawer: _buildDrawer(context, user),
|
||||
drawerEdgeDragWidth: 50,
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
currentIndex: _currentIndex,
|
||||
items: _navBarItems,
|
||||
selectedLabelStyle: StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||
unselectedLabelStyle:
|
||||
StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
selectedItemColor: StreamChatTheme.of(context).colorTheme.black,
|
||||
unselectedItemColor: Colors.grey,
|
||||
onTap: (index) {
|
||||
setState(() => _currentIndex = index);
|
||||
},
|
||||
),
|
||||
body: IndexedStack(
|
||||
index: _currentIndex,
|
||||
children: [
|
||||
ChannelListPage(),
|
||||
UserMentionPage(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Drawer _buildDrawer(BuildContext context, User user) {
|
||||
return Drawer(
|
||||
child: Container(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: MediaQuery.of(context).viewPadding.top + 8,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 20.0,
|
||||
left: 8,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
UserAvatar(
|
||||
user: user,
|
||||
showOnlineStatus: false,
|
||||
constraints: BoxConstraints.tight(Size.fromRadius(20)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
user.name,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.penWrite(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_CHAT,
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'New direct message',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.contacts(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT,
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'New group',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: ListTile(
|
||||
onTap: () async {
|
||||
Navigator.pop(context);
|
||||
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
await secureStorage.deleteAll();
|
||||
|
||||
StreamChat.of(context).client.disconnect(
|
||||
clearUser: true,
|
||||
);
|
||||
|
||||
await Navigator.pushReplacementNamed(
|
||||
context,
|
||||
Routes.CHOOSE_USER,
|
||||
);
|
||||
},
|
||||
leading: StreamSvgIcon.user(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
title: Text(
|
||||
'Sign out',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: StreamSvgIcon.Icon_moon(
|
||||
size: 24,
|
||||
),
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
onPressed: () async {
|
||||
final sp = await StreamingSharedPreferences.instance;
|
||||
sp.setInt(
|
||||
'theme',
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? 1
|
||||
: -1,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserMentionPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = StreamChat.of(context).user;
|
||||
return MessageSearchBloc(
|
||||
child: MessageSearchListView(
|
||||
filters: {
|
||||
'members': {
|
||||
r'$in': [user.id],
|
||||
},
|
||||
},
|
||||
messageFilters: {
|
||||
'mentioned_users.id': {
|
||||
r'$contains': user.id,
|
||||
},
|
||||
},
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
paginationParams: PaginationParams(limit: 20),
|
||||
showResultCount: false,
|
||||
emptyBuilder: (_, __) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.mentions(
|
||||
size: 96,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyGainsboro,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No mentions exist yet...',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onItemTap: (messageResponse) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel.type,
|
||||
id: messageResponse.channel.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
@override
|
||||
_ChannelListPageState createState() => _ChannelListPageState();
|
||||
}
|
||||
|
||||
class _ChannelListPageState extends State<ChannelListPage> {
|
||||
TextEditingController _controller;
|
||||
|
||||
String _channelQuery = '';
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
Timer _debounce;
|
||||
|
||||
void _channelQueryListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_channelQuery = _controller.text;
|
||||
_isSearchActive = _channelQuery.isNotEmpty;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController()..addListener(_channelQueryListener);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller?.removeListener(_channelQueryListener);
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = StreamChat.of(context).user;
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
if (_isSearchActive) {
|
||||
_controller.clear();
|
||||
setState(() => _isSearchActive = false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: ChannelsBloc(
|
||||
child: MessageSearchBloc(
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder: (_, __) => [
|
||||
SliverToBoxAdapter(
|
||||
child: SearchTextField(
|
||||
controller: _controller,
|
||||
showCloseButton: _isSearchActive,
|
||||
),
|
||||
),
|
||||
],
|
||||
body: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: _isSearchActive
|
||||
? MessageSearchListView(
|
||||
messageQuery: _channelQuery,
|
||||
filters: {
|
||||
'members': {
|
||||
r'$in': [user.id]
|
||||
}
|
||||
},
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
pullToRefresh: false,
|
||||
paginationParams: PaginationParams(limit: 20),
|
||||
emptyBuilder: (_, query) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No results for \"$query\"...',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onItemTap: (messageResponse) async {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel.type,
|
||||
id: messageResponse.channel.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: ChannelListView(
|
||||
onStartChatPressed: () {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
},
|
||||
swipeToAction: true,
|
||||
filter: {
|
||||
'members': {
|
||||
r'$in': [user.id],
|
||||
},
|
||||
},
|
||||
options: {
|
||||
'presence': true,
|
||||
},
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
onViewInfoTap: (channel) {
|
||||
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: ChatInfoScreen(
|
||||
user: channel.state.members.first.user,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: GroupInfoScreen(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelPageArgs {
|
||||
final Channel channel;
|
||||
final Message initialMessage;
|
||||
|
||||
const ChannelPageArgs({
|
||||
this.channel,
|
||||
this.initialMessage,
|
||||
});
|
||||
}
|
||||
|
||||
class ChannelPage extends StatefulWidget {
|
||||
final int initialScrollIndex;
|
||||
final double initialAlignment;
|
||||
final bool highlightInitialMessage;
|
||||
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
this.initialScrollIndex,
|
||||
this.initialAlignment,
|
||||
this.highlightInitialMessage = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ChannelPageState createState() => _ChannelPageState();
|
||||
}
|
||||
|
||||
class _ChannelPageState extends State<ChannelPage> {
|
||||
Message _quotedMessage;
|
||||
FocusNode _focusNode;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_focusNode = FocusNode();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _reply(Message message) {
|
||||
setState(() => _quotedMessage = message);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_focusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ChannelHeader(
|
||||
showTypingIndicator: false,
|
||||
onImageTap: () async {
|
||||
var channel = StreamChannel.of(context).channel;
|
||||
|
||||
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||
final currentUser = StreamChat.of(context).user;
|
||||
final otherUser = channel.state.members.firstWhere(
|
||||
(element) => element.user.id != currentUser.id,
|
||||
orElse: () => null,
|
||||
);
|
||||
if (otherUser != null) {
|
||||
final pop = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
child: ChatInfoScreen(
|
||||
user: otherUser.user,
|
||||
),
|
||||
channel: channel,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (pop == true) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
child: GroupInfoScreen(),
|
||||
channel: channel,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
MessageListView(
|
||||
initialScrollIndex: widget.initialScrollIndex,
|
||||
initialAlignment: widget.initialAlignment,
|
||||
highlightInitialMessage: widget.highlightInitialMessage,
|
||||
onMessageSwiped: _reply,
|
||||
onReplyTap: _reply,
|
||||
threadBuilder: (_, parentMessage) {
|
||||
return ThreadPage(
|
||||
parent: parentMessage,
|
||||
);
|
||||
},
|
||||
onShowMessage: (m, c) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = m;
|
||||
final channel = client.channel(
|
||||
c.type,
|
||||
id: c.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
Navigator.pushReplacementNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow
|
||||
.withOpacity(.9),
|
||||
child: TypingIndicator(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
MessageInput(
|
||||
focusNode: _focusNode,
|
||||
quotedMessage: _quotedMessage,
|
||||
onQuotedMessageCleared: () {
|
||||
setState(() => _quotedMessage = null);
|
||||
_focusNode.unfocus();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadPage extends StatefulWidget {
|
||||
final Message parent;
|
||||
final int initialScrollIndex;
|
||||
final double initialAlignment;
|
||||
|
||||
ThreadPage({
|
||||
Key key,
|
||||
this.parent,
|
||||
this.initialScrollIndex,
|
||||
this.initialAlignment,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ThreadPageState createState() => _ThreadPageState();
|
||||
}
|
||||
|
||||
class _ThreadPageState extends State<ThreadPage> {
|
||||
Message _quotedMessage;
|
||||
FocusNode _focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _reply(Message message) {
|
||||
setState(() => _quotedMessage = message);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_focusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ThreadHeader(
|
||||
parent: widget.parent,
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(
|
||||
parentMessage: widget.parent,
|
||||
initialScrollIndex: widget.initialScrollIndex,
|
||||
initialAlignment: widget.initialAlignment,
|
||||
onMessageSwiped: _reply,
|
||||
onReplyTap: _reply,
|
||||
),
|
||||
),
|
||||
if (widget.parent.type != 'deleted')
|
||||
MessageInput(
|
||||
parentMessage: widget.parent,
|
||||
focusNode: _focusNode,
|
||||
quotedMessage: _quotedMessage,
|
||||
onQuotedMessageCleared: () {
|
||||
setState(() => _quotedMessage = null);
|
||||
_focusNode.unfocus();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Second step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
|
||||
///
|
||||
/// Most chat applications handle more than just one single conversation.
|
||||
/// Apps like Facebook Messenger, Whatsapp and Telegram allows you to have multiple one to one and group conversations.
|
||||
///
|
||||
/// Let’s find out how we can change our application chat screen to display the list of conversations and navigate between them.
|
||||
///
|
||||
/// > Note: the SDK uses Flutter’s [Navigator] to move from one route to another, this allows us to avoid any boiler-plate code.
|
||||
/// > Of course you can take total control of how navigation works by customizing widgets like [Channel] and [ChannelList].
|
||||
///
|
||||
/// If you run the application, you will see that the first screen shows a list of conversations, you can open each by tapping and go back to the list.
|
||||
///
|
||||
/// Every single widget involved in this UI can be customized or swapped with your own.
|
||||
///
|
||||
/// The [ChannelListPage] widget retrieves the list of channels based on a custom query and ordering.
|
||||
/// In this case we are showing the list of channels the current user is a member and we order them based on the time they had a new message.
|
||||
/// [ChannelListView] handles pagination and updates automatically out of the box when new channels are created or when a new message is added to a channel.
|
||||
void main() async {
|
||||
final client = Client(
|
||||
's2dxdhpxd94g',
|
||||
logLevel: Level.INFO,
|
||||
);
|
||||
|
||||
await client.setUser(
|
||||
User(id: 'super-band-9'),
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
|
||||
);
|
||||
|
||||
runApp(MyApp(client));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final Client client;
|
||||
|
||||
MyApp(this.client);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
),
|
||||
home: ChannelListPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelListPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
filter: {
|
||||
// 'members': {
|
||||
// '\$in': [StreamChat.of(context).user.id],
|
||||
// }
|
||||
},
|
||||
sort: [SortOption('last_message_at')],
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelPage extends StatelessWidget {
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ChannelHeader(),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(),
|
||||
),
|
||||
MessageInput(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'chips_input_text_field.dart';
|
||||
import 'main.dart';
|
||||
import 'routes/routes.dart';
|
||||
|
||||
class NewChatScreen extends StatefulWidget {
|
||||
@override
|
||||
_NewChatScreenState createState() => _NewChatScreenState();
|
||||
}
|
||||
|
||||
class _NewChatScreenState extends State<NewChatScreen> {
|
||||
final _chipInputTextFieldStateKey =
|
||||
GlobalKey<ChipInputTextFieldState<User>>();
|
||||
|
||||
TextEditingController _controller;
|
||||
|
||||
ChipInputTextFieldState get _chipInputTextFieldState =>
|
||||
_chipInputTextFieldStateKey.currentState;
|
||||
|
||||
String _userNameQuery = '';
|
||||
|
||||
final _selectedUsers = <User>{};
|
||||
|
||||
final _searchFocusNode = FocusNode();
|
||||
final _messageInputFocusNode = FocusNode();
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
Channel channel;
|
||||
|
||||
Timer _debounce;
|
||||
|
||||
bool _showUserList = true;
|
||||
|
||||
void _userNameListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_userNameQuery = _controller.text;
|
||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
channel = StreamChat.of(context).client.channel('messaging');
|
||||
_controller = TextEditingController()..addListener(_userNameListener);
|
||||
|
||||
_searchFocusNode.addListener(() async {
|
||||
if (_searchFocusNode.hasFocus && !_showUserList) {
|
||||
setState(() {
|
||||
_showUserList = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_messageInputFocusNode.addListener(() async {
|
||||
if (_messageInputFocusNode.hasFocus && _selectedUsers.isNotEmpty) {
|
||||
final chatState = StreamChat.of(context);
|
||||
|
||||
final res = await chatState.client.queryChannels(
|
||||
options: {
|
||||
'state': false,
|
||||
'watch': false,
|
||||
},
|
||||
filter: {
|
||||
'members': [
|
||||
..._selectedUsers.map((e) => e.id),
|
||||
chatState.user.id,
|
||||
],
|
||||
'distinct': true,
|
||||
},
|
||||
messageLimit: 0,
|
||||
paginationParams: PaginationParams(
|
||||
limit: 1,
|
||||
),
|
||||
);
|
||||
|
||||
final _channelExisted = res.length == 1;
|
||||
if (_channelExisted) {
|
||||
channel = res.first;
|
||||
await channel.watch();
|
||||
} else {
|
||||
channel = chatState.client.channel(
|
||||
'messaging',
|
||||
extraData: {
|
||||
'members': [
|
||||
..._selectedUsers.map((e) => e.id),
|
||||
chatState.user.id,
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_showUserList = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchFocusNode.dispose();
|
||||
_messageInputFocusNode.dispose();
|
||||
_controller?.clear();
|
||||
_controller?.removeListener(_userNameListener);
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 0,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
'New Chat',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold
|
||||
.copyWith(color: StreamChatTheme.of(context).colorTheme.black),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: ConnectionStatusBuilder(
|
||||
statusBuilder: (context, status) {
|
||||
String statusString = '';
|
||||
bool showStatus = true;
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus.connected:
|
||||
statusString = 'Connected';
|
||||
showStatus = false;
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
statusString = 'Reconnecting...';
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
statusString = 'Disconnected';
|
||||
break;
|
||||
}
|
||||
return InfoTile(
|
||||
showMessage: showStatus,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: statusString,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ChipsInputTextField<User>(
|
||||
key: _chipInputTextFieldStateKey,
|
||||
controller: _controller,
|
||||
focusNode: _searchFocusNode,
|
||||
chipBuilder: (context, user) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_chipInputTextFieldState.removeItem(user);
|
||||
_searchFocusNode.requestFocus();
|
||||
},
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyGainsboro,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 4, 12, 4),
|
||||
child: Text(
|
||||
user.name,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
foregroundDecoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.overlay,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: UserAvatar(
|
||||
showOnlineStatus: false,
|
||||
user: user,
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
StreamSvgIcon.close(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
onChipAdded: (user) {
|
||||
setState(() => _selectedUsers.add(user));
|
||||
},
|
||||
onChipRemoved: (user) {
|
||||
setState(() => _selectedUsers.remove(user));
|
||||
},
|
||||
),
|
||||
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
||||
Container(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamNeumorphicButton(
|
||||
child: Center(
|
||||
child: StreamSvgIcon.contacts(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'Create a Group',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_showUserList)
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
gradient:
|
||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
_isSearchActive
|
||||
? "Matches for \"$_userNameQuery\""
|
||||
: 'On the platform',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5))),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _showUserList
|
||||
? GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: UsersBloc(
|
||||
child: UserListView(
|
||||
selectedUsers: _selectedUsers,
|
||||
groupAlphabetically:
|
||||
_isSearchActive ? false : true,
|
||||
onUserTap: (user, _) {
|
||||
_controller.clear();
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
_chipInputTextFieldState
|
||||
..addItem(user)
|
||||
..pauseItemAddition();
|
||||
} else {
|
||||
_chipInputTextFieldState.removeItem(user);
|
||||
}
|
||||
},
|
||||
pagination: PaginationParams(
|
||||
limit: 25,
|
||||
),
|
||||
filter: {
|
||||
if (_userNameQuery.isNotEmpty)
|
||||
'name': {
|
||||
r'$autocomplete': _userNameQuery,
|
||||
},
|
||||
'id': {
|
||||
r'$ne': StreamChat.of(context).user.id,
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
SortOption(
|
||||
'name',
|
||||
direction: 1,
|
||||
),
|
||||
],
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics:
|
||||
AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight:
|
||||
viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No user matches these keywords...',
|
||||
style: StreamChatTheme.of(
|
||||
context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme
|
||||
.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: FutureBuilder<bool>(
|
||||
future: channel.initialized,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == true) {
|
||||
return MessageListView();
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: Text(
|
||||
'No chats here yet...',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
MessageInput(
|
||||
focusNode: _messageInputFocusNode,
|
||||
preMessageSending: (message) async {
|
||||
await channel.watch();
|
||||
return message;
|
||||
},
|
||||
onMessageSent: (m) {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
ModalRoute.withName(Routes.HOME),
|
||||
arguments: ChannelPageArgs(channel: channel),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'routes/routes.dart';
|
||||
import 'search_text_field.dart';
|
||||
|
||||
class NewGroupChatScreen extends StatefulWidget {
|
||||
@override
|
||||
_NewGroupChatScreenState createState() => _NewGroupChatScreenState();
|
||||
}
|
||||
|
||||
class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
TextEditingController _controller;
|
||||
|
||||
String _userNameQuery = '';
|
||||
|
||||
final _selectedUsers = <User>{};
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
Timer _debounce;
|
||||
|
||||
void _userNameListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_userNameQuery = _controller.text;
|
||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController()..addListener(_userNameListener);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller?.clear();
|
||||
_controller?.removeListener(_userNameListener);
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
elevation: 1,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
'Add Group Members',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.arrow_right(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
onPressed: () async {
|
||||
final updatedList = await Navigator.pushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT_DETAILS,
|
||||
arguments: _selectedUsers.toList(growable: false),
|
||||
);
|
||||
if (updatedList != null) {
|
||||
setState(() {
|
||||
_selectedUsers
|
||||
..clear()
|
||||
..addAll(updatedList);
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ConnectionStatusBuilder(
|
||||
statusBuilder: (context, status) {
|
||||
String statusString = '';
|
||||
bool showStatus = true;
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus.connected:
|
||||
statusString = 'Connected';
|
||||
showStatus = false;
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
statusString = 'Reconnecting...';
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
statusString = 'Disconnected';
|
||||
break;
|
||||
}
|
||||
return InfoTile(
|
||||
showMessage: showStatus,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: statusString,
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder:
|
||||
(BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: SearchTextField(
|
||||
controller: _controller,
|
||||
),
|
||||
),
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 104,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: _selectedUsers.length,
|
||||
padding: const EdgeInsets.all(8),
|
||||
separatorBuilder: (_, __) => SizedBox(width: 16),
|
||||
itemBuilder: (_, index) {
|
||||
final user = _selectedUsers.elementAt(index);
|
||||
return Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
UserAvatar(
|
||||
onlineIndicatorAlignment:
|
||||
Alignment(0.9, 0.9),
|
||||
user: user,
|
||||
showOnlineStatus: true,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 64,
|
||||
width: 64,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedUsers.contains(user)) {
|
||||
setState(() =>
|
||||
_selectedUsers.remove(user));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow,
|
||||
),
|
||||
),
|
||||
child: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
user.name.split(' ')[0],
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: _HeaderDelegate(
|
||||
height: 30,
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
gradient:
|
||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
_isSearchActive
|
||||
? 'Matches for \"$_userNameQuery\"'
|
||||
: 'On the platform',
|
||||
style: TextStyle(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
body: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: UsersBloc(
|
||||
child: UserListView(
|
||||
selectedUsers: _selectedUsers,
|
||||
pullToRefresh: false,
|
||||
groupAlphabetically: _isSearchActive ? false : true,
|
||||
onUserTap: (user, _) {
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
setState(() {
|
||||
_selectedUsers.add(user);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_selectedUsers.remove(user);
|
||||
});
|
||||
}
|
||||
},
|
||||
pagination: PaginationParams(
|
||||
limit: 25,
|
||||
),
|
||||
filter: {
|
||||
if (_userNameQuery.isNotEmpty)
|
||||
'name': {
|
||||
r'$autocomplete': _userNameQuery,
|
||||
},
|
||||
'id': {
|
||||
r'$ne': StreamChat.of(context).user.id,
|
||||
}
|
||||
},
|
||||
sort: [
|
||||
SortOption(
|
||||
'name',
|
||||
direction: 1,
|
||||
),
|
||||
],
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No user matches these keywords...',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final Widget child;
|
||||
final double height;
|
||||
|
||||
const _HeaderDelegate({
|
||||
@required this.child,
|
||||
@required this.height,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
return Container(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxExtent => height;
|
||||
|
||||
@override
|
||||
double get minExtent => height;
|
||||
|
||||
@override
|
||||
bool shouldRebuild(_HeaderDelegate oldDelegate) => true;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
|
||||
hide Message;
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
void showLocalNotification(Event event) async {
|
||||
if (event.message == null) return;
|
||||
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
final initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('launch_background');
|
||||
final initializationSettingsIOS = IOSInitializationSettings();
|
||||
final initializationSettings = InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsIOS,
|
||||
);
|
||||
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
event.message.id.hashCode,
|
||||
event.message.user.name,
|
||||
event.message.text,
|
||||
NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'message channel',
|
||||
'Message channel',
|
||||
'Channel used for showing messages',
|
||||
priority: Priority.high,
|
||||
importance: Importance.high,
|
||||
),
|
||||
iOS: IOSNotificationDetails(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../choose_user_page.dart';
|
||||
import '../advanced_options_page.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import '../main.dart';
|
||||
import '../group_chat_details_screen.dart';
|
||||
import '../new_group_chat_screen.dart';
|
||||
import '../new_chat_screen.dart';
|
||||
import '../chat_info_screen.dart';
|
||||
import '../group_info_screen.dart';
|
||||
|
||||
class AppRoutes {
|
||||
/// Add entry for new route here
|
||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||
final args = settings.arguments;
|
||||
switch (settings.name) {
|
||||
case Routes.APP:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.APP),
|
||||
builder: (_) {
|
||||
return MyApp(args);
|
||||
});
|
||||
case Routes.HOME:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.HOME),
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
});
|
||||
case Routes.CHOOSE_USER:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.CHOOSE_USER),
|
||||
builder: (_) {
|
||||
return ChooseUserPage();
|
||||
});
|
||||
case Routes.ADVANCED_OPTIONS:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.ADVANCED_OPTIONS),
|
||||
builder: (_) => AdvancedOptionsPage(),
|
||||
);
|
||||
case Routes.CHANNEL_PAGE:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.CHANNEL_PAGE),
|
||||
builder: (_) {
|
||||
final arg = args as ChannelPageArgs;
|
||||
return StreamChannel(
|
||||
channel: arg.channel,
|
||||
initialMessageId: arg.initialMessage?.id,
|
||||
child: ChannelPage(
|
||||
highlightInitialMessage: arg.initialMessage != null,
|
||||
),
|
||||
);
|
||||
});
|
||||
case Routes.NEW_CHAT:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.NEW_CHAT),
|
||||
builder: (_) {
|
||||
return NewChatScreen();
|
||||
});
|
||||
case Routes.NEW_GROUP_CHAT:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT),
|
||||
builder: (_) {
|
||||
return NewGroupChatScreen();
|
||||
});
|
||||
case Routes.NEW_GROUP_CHAT_DETAILS:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT_DETAILS),
|
||||
builder: (_) {
|
||||
return GroupChatDetailsScreen(
|
||||
selectedUsers: args,
|
||||
);
|
||||
});
|
||||
case Routes.CHAT_INFO_SCREEN:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.CHAT_INFO_SCREEN),
|
||||
builder: (_) {
|
||||
return ChatInfoScreen(
|
||||
user: args,
|
||||
);
|
||||
});
|
||||
case Routes.GROUP_INFO_SCREEN:
|
||||
return MaterialPageRoute(
|
||||
settings: const RouteSettings(name: Routes.GROUP_INFO_SCREEN),
|
||||
builder: (_) {
|
||||
return GroupInfoScreen();
|
||||
});
|
||||
// Default case, should not reach here.
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/// Define all the route names here
|
||||
class Routes {
|
||||
static const String APP = '/app';
|
||||
static const String HOME = '/home';
|
||||
static const String CHOOSE_USER = '/choose_user';
|
||||
static const String ADVANCED_OPTIONS = '/advance_options';
|
||||
static const String CHANNEL_PAGE = '/channel_page';
|
||||
static const String NEW_CHAT = '/new_chat';
|
||||
static const String NEW_GROUP_CHAT = '/new_group_chat';
|
||||
static const String NEW_GROUP_CHAT_DETAILS = '/new_group_chat_details';
|
||||
static const String CHAT_INFO_SCREEN = '/chat_info_screen';
|
||||
static const String GROUP_INFO_SCREEN = '/group_info_screen';
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class SearchTextField extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final ValueChanged<String> onChanged;
|
||||
final String hintText;
|
||||
final VoidCallback onTap;
|
||||
final bool showCloseButton;
|
||||
|
||||
const SearchTextField({
|
||||
Key key,
|
||||
@required this.controller,
|
||||
this.onChanged,
|
||||
this.onTap,
|
||||
this.hintText = 'Search',
|
||||
this.showCloseButton = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
border: Border.all(
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
onTap: onTap,
|
||||
controller: controller,
|
||||
onChanged: onChanged,
|
||||
decoration: InputDecoration(
|
||||
prefixText: ' ',
|
||||
prefixIconConstraints: BoxConstraints.tight(Size(40, 24)),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: StreamSvgIcon.search(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: StreamChatTheme.of(context).textTheme.body.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showCloseButton)
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
padding: const EdgeInsets.all(0),
|
||||
icon: StreamSvgIcon.close_small(
|
||||
color: Colors.grey,
|
||||
),
|
||||
splashRadius: 24,
|
||||
onPressed: () {
|
||||
if (controller.text.isNotEmpty) {
|
||||
Future.microtask(
|
||||
() => [
|
||||
controller.clear(),
|
||||
if (onChanged != null) onChanged(''),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||