├── .gitignore ├── .vscode └── settings.json ├── README.md ├── android ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── app │ ├── .classpath │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── wing_chat │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── emoji ├── [NO].png ├── [OK].png ├── [乒乓].png ├── [亲亲].png ├── [便便].png ├── [偷笑].png ├── [傲慢].png ├── [再见].png ├── [冷汗].png ├── [凋谢].png ├── [刀].png ├── [勾引].png ├── [发呆].png ├── [发怒].png ├── [可怜].png ├── [右哼哼].png ├── [吐].png ├── [吓].png ├── [呲牙].png ├── [咒骂].png ├── [咖啡].png ├── [哈欠].png ├── [啤酒].png ├── [嘘].png ├── [嘴唇].png ├── [困].png ├── [坏笑].png ├── [大哭].png ├── [太阳].png ├── [奋斗].png ├── [委屈].png ├── [害羞].png ├── [尴尬].png ├── [左哼哼].png ├── [差劲].png ├── [弱].png ├── [强].png ├── [得意].png ├── [微笑].png ├── [心碎].png ├── [快哭了].png ├── [悠闲].png ├── [惊恐].png ├── [惊讶].png ├── [愉快].png ├── [憨笑].png ├── [抓狂].png ├── [抠鼻].png ├── [抱拳].png ├── [拥抱].png ├── [拳头].png ├── [握手].png ├── [撇嘴].png ├── [擦汗].png ├── [敲打].png ├── [晕].png ├── [月亮].png ├── [流汗].png ├── [流泪].png ├── [炸弹].png ├── [爱你].png ├── [爱心].png ├── [猪头].png ├── [玫瑰].png ├── [瓢虫].png ├── [疑问].png ├── [疯了].png ├── [白眼].png ├── [睡].png ├── [礼物].png ├── [篮球].png ├── [糗大了].png ├── [胜利].png ├── [色].png ├── [菜刀].png ├── [蛋糕].png ├── [衰].png ├── [西瓜].png ├── [调皮].png ├── [足球].png ├── [鄙视].png ├── [酷].png ├── [闪电].png ├── [闭嘴].png ├── [阴险].png ├── [难过].png ├── [饥饿].png ├── [饭].png ├── [骷髅].png └── [鼓掌].png ├── images ├── badge.png ├── chat_emoji.png ├── chat_funcs.png ├── dog.jpg ├── icon_friends_add.png ├── icon_right.png ├── tabbar_chat.png ├── tabbar_chat_hl.png ├── tabbar_discover.png ├── tabbar_discover_hl.png ├── tabbar_friends.png ├── tabbar_friends_hl.png ├── tabbar_mine.png ├── tabbar_mine_hl.png ├── 五角星b.png ├── 五角星w.png ├── 公众号.png ├── 发起群聊.png ├── 圆加.png ├── 小程序.png ├── 微信 支付.png ├── 微信卡包.png ├── 微信收藏.png ├── 微信相册.png ├── 微信表情.png ├── 微信设置.png ├── 扫一扫.png ├── 扫一扫1.png ├── 扫一扫2.png ├── 搜一搜 2.png ├── 搜一搜.png ├── 搜一搜2.png ├── 摇一摇.png ├── 收付款.png ├── 放大镜b.png ├── 放大镜w.png ├── 新的朋友.png ├── 朋友圈.png ├── 标星.png ├── 标签.png ├── 气泡.png ├── 添加朋友.png ├── 游戏.png ├── 游戏2.png ├── 相机.png ├── 看一看icon.png ├── 群聊.png ├── 购物.png └── 附近的人icon.png ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── flutter_export_environment.sh ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── main.dart ├── provider │ ├── group_chat_provider.dart │ ├── group_list_provider.dart │ ├── jmessage_manager_provider.dart │ ├── root_scene_provider.dart │ └── single_chat_provider.dart ├── tools │ ├── app_screen.dart │ ├── app_toast.dart │ ├── emoji_text.dart │ ├── expanded_viewport.dart │ ├── sp_util.dart │ └── special_text_span_builder.dart └── views │ ├── chat │ ├── group_chat_scene.dart │ └── single_chat_scene.dart │ ├── discover │ ├── discover_cell.dart │ ├── discover_child_page.dart │ └── discover_scene.dart │ ├── friends │ ├── friends_data.dart │ ├── friends_page_cell.dart │ ├── friends_scene.dart │ └── index_bar.dart │ ├── login_scene.dart │ ├── message │ └── message_list_scene.dart │ ├── mine │ └── mine_page.dart │ └── root_scene.dart ├── pubspec.yaml ├── show_images ├── 会话列表.jpg ├── 群聊.jpg └── 表情.jpg └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 73 | pubspec.lock 74 | android/key.properties 75 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "interactive" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wing_chat 2 | 极光IM插件开发的即时通讯demo,有聊天界面,包含有文字、图片、表情,可帮助初学者更快切入im开发。demo功能还不完善,期待大家提出宝贵意见!如果可以希望大家给个star 3 | 4 | ![会话列表](https://github.com/ShellPeng/wing_chat/blob/master/show_images/会话列表.jpg) 5 | ![群聊](https://github.com/ShellPeng/wing_chat/blob/master/show_images/群聊.jpg) 6 | ![表情](https://github.com/ShellPeng/wing_chat/blob/master/show_images/表情.jpg) 7 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | // def keystorePropertiesFile = rootProject.file("key.properties") 29 | // def keystoreProperties = new Properties() 30 | // keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 31 | 32 | android { 33 | compileSdkVersion 28 34 | 35 | sourceSets { 36 | main.java.srcDirs += 'src/main/kotlin' 37 | } 38 | 39 | lintOptions { 40 | disable 'InvalidPackage' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.wing_chat" 46 | minSdkVersion 16 47 | targetSdkVersion 28 48 | versionCode flutterVersionCode.toInteger() 49 | versionName flutterVersionName 50 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 51 | 52 | ndk { 53 | //选择要添加的对应cpu类型的.so库。 54 | abiFilters 'armeabi', 'armeabi-v7a','arm64-v8a' 55 | } 56 | 57 | manifestPlaceholders = [ 58 | JPUSH_PKGNAME : applicationId, 59 | JPUSH_APPKEY : "65e831cf441e2df70442995a", //JPush上注册的包名对应的appkey. 60 | JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可. 61 | ] 62 | } 63 | 64 | buildTypes { 65 | release { 66 | // TODO: Add your own signing config for the release build. 67 | // Signing with the debug keys for now, so `flutter run --release` works. 68 | signingConfig signingConfigs.debug 69 | } 70 | } 71 | } 72 | 73 | flutter { 74 | source '../..' 75 | } 76 | 77 | dependencies { 78 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 79 | testImplementation 'junit:junit:4.12' 80 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 81 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 82 | } 83 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/wing_chat/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.wing_chat 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.app.FlutterActivity 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | GeneratedPluginRegistrant.registerWith(this) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.2.71' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /emoji/[NO].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[NO].png -------------------------------------------------------------------------------- /emoji/[OK].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[OK].png -------------------------------------------------------------------------------- /emoji/[乒乓].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[乒乓].png -------------------------------------------------------------------------------- /emoji/[亲亲].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[亲亲].png -------------------------------------------------------------------------------- /emoji/[便便].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[便便].png -------------------------------------------------------------------------------- /emoji/[偷笑].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[偷笑].png -------------------------------------------------------------------------------- /emoji/[傲慢].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[傲慢].png -------------------------------------------------------------------------------- /emoji/[再见].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[再见].png -------------------------------------------------------------------------------- /emoji/[冷汗].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[冷汗].png -------------------------------------------------------------------------------- /emoji/[凋谢].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[凋谢].png -------------------------------------------------------------------------------- /emoji/[刀].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[刀].png -------------------------------------------------------------------------------- /emoji/[勾引].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[勾引].png -------------------------------------------------------------------------------- /emoji/[发呆].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[发呆].png -------------------------------------------------------------------------------- /emoji/[发怒].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[发怒].png -------------------------------------------------------------------------------- /emoji/[可怜].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[可怜].png -------------------------------------------------------------------------------- /emoji/[右哼哼].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[右哼哼].png -------------------------------------------------------------------------------- /emoji/[吐].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[吐].png -------------------------------------------------------------------------------- /emoji/[吓].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[吓].png -------------------------------------------------------------------------------- /emoji/[呲牙].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[呲牙].png -------------------------------------------------------------------------------- /emoji/[咒骂].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[咒骂].png -------------------------------------------------------------------------------- /emoji/[咖啡].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[咖啡].png -------------------------------------------------------------------------------- /emoji/[哈欠].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[哈欠].png -------------------------------------------------------------------------------- /emoji/[啤酒].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[啤酒].png -------------------------------------------------------------------------------- /emoji/[嘘].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[嘘].png -------------------------------------------------------------------------------- /emoji/[嘴唇].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[嘴唇].png -------------------------------------------------------------------------------- /emoji/[困].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[困].png -------------------------------------------------------------------------------- /emoji/[坏笑].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[坏笑].png -------------------------------------------------------------------------------- /emoji/[大哭].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[大哭].png -------------------------------------------------------------------------------- /emoji/[太阳].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[太阳].png -------------------------------------------------------------------------------- /emoji/[奋斗].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[奋斗].png -------------------------------------------------------------------------------- /emoji/[委屈].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[委屈].png -------------------------------------------------------------------------------- /emoji/[害羞].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[害羞].png -------------------------------------------------------------------------------- /emoji/[尴尬].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[尴尬].png -------------------------------------------------------------------------------- /emoji/[左哼哼].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[左哼哼].png -------------------------------------------------------------------------------- /emoji/[差劲].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[差劲].png -------------------------------------------------------------------------------- /emoji/[弱].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[弱].png -------------------------------------------------------------------------------- /emoji/[强].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[强].png -------------------------------------------------------------------------------- /emoji/[得意].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[得意].png -------------------------------------------------------------------------------- /emoji/[微笑].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[微笑].png -------------------------------------------------------------------------------- /emoji/[心碎].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[心碎].png -------------------------------------------------------------------------------- /emoji/[快哭了].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[快哭了].png -------------------------------------------------------------------------------- /emoji/[悠闲].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[悠闲].png -------------------------------------------------------------------------------- /emoji/[惊恐].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[惊恐].png -------------------------------------------------------------------------------- /emoji/[惊讶].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[惊讶].png -------------------------------------------------------------------------------- /emoji/[愉快].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[愉快].png -------------------------------------------------------------------------------- /emoji/[憨笑].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[憨笑].png -------------------------------------------------------------------------------- /emoji/[抓狂].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[抓狂].png -------------------------------------------------------------------------------- /emoji/[抠鼻].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[抠鼻].png -------------------------------------------------------------------------------- /emoji/[抱拳].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[抱拳].png -------------------------------------------------------------------------------- /emoji/[拥抱].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[拥抱].png -------------------------------------------------------------------------------- /emoji/[拳头].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[拳头].png -------------------------------------------------------------------------------- /emoji/[握手].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[握手].png -------------------------------------------------------------------------------- /emoji/[撇嘴].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[撇嘴].png -------------------------------------------------------------------------------- /emoji/[擦汗].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[擦汗].png -------------------------------------------------------------------------------- /emoji/[敲打].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[敲打].png -------------------------------------------------------------------------------- /emoji/[晕].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[晕].png -------------------------------------------------------------------------------- /emoji/[月亮].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[月亮].png -------------------------------------------------------------------------------- /emoji/[流汗].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[流汗].png -------------------------------------------------------------------------------- /emoji/[流泪].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[流泪].png -------------------------------------------------------------------------------- /emoji/[炸弹].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[炸弹].png -------------------------------------------------------------------------------- /emoji/[爱你].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[爱你].png -------------------------------------------------------------------------------- /emoji/[爱心].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[爱心].png -------------------------------------------------------------------------------- /emoji/[猪头].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[猪头].png -------------------------------------------------------------------------------- /emoji/[玫瑰].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[玫瑰].png -------------------------------------------------------------------------------- /emoji/[瓢虫].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[瓢虫].png -------------------------------------------------------------------------------- /emoji/[疑问].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[疑问].png -------------------------------------------------------------------------------- /emoji/[疯了].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[疯了].png -------------------------------------------------------------------------------- /emoji/[白眼].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[白眼].png -------------------------------------------------------------------------------- /emoji/[睡].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[睡].png -------------------------------------------------------------------------------- /emoji/[礼物].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[礼物].png -------------------------------------------------------------------------------- /emoji/[篮球].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[篮球].png -------------------------------------------------------------------------------- /emoji/[糗大了].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[糗大了].png -------------------------------------------------------------------------------- /emoji/[胜利].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[胜利].png -------------------------------------------------------------------------------- /emoji/[色].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[色].png -------------------------------------------------------------------------------- /emoji/[菜刀].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[菜刀].png -------------------------------------------------------------------------------- /emoji/[蛋糕].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[蛋糕].png -------------------------------------------------------------------------------- /emoji/[衰].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[衰].png -------------------------------------------------------------------------------- /emoji/[西瓜].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[西瓜].png -------------------------------------------------------------------------------- /emoji/[调皮].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[调皮].png -------------------------------------------------------------------------------- /emoji/[足球].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[足球].png -------------------------------------------------------------------------------- /emoji/[鄙视].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[鄙视].png -------------------------------------------------------------------------------- /emoji/[酷].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[酷].png -------------------------------------------------------------------------------- /emoji/[闪电].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[闪电].png -------------------------------------------------------------------------------- /emoji/[闭嘴].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[闭嘴].png -------------------------------------------------------------------------------- /emoji/[阴险].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[阴险].png -------------------------------------------------------------------------------- /emoji/[难过].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[难过].png -------------------------------------------------------------------------------- /emoji/[饥饿].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[饥饿].png -------------------------------------------------------------------------------- /emoji/[饭].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[饭].png -------------------------------------------------------------------------------- /emoji/[骷髅].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[骷髅].png -------------------------------------------------------------------------------- /emoji/[鼓掌].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/emoji/[鼓掌].png -------------------------------------------------------------------------------- /images/badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/badge.png -------------------------------------------------------------------------------- /images/chat_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/chat_emoji.png -------------------------------------------------------------------------------- /images/chat_funcs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/chat_funcs.png -------------------------------------------------------------------------------- /images/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/dog.jpg -------------------------------------------------------------------------------- /images/icon_friends_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/icon_friends_add.png -------------------------------------------------------------------------------- /images/icon_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/icon_right.png -------------------------------------------------------------------------------- /images/tabbar_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_chat.png -------------------------------------------------------------------------------- /images/tabbar_chat_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_chat_hl.png -------------------------------------------------------------------------------- /images/tabbar_discover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_discover.png -------------------------------------------------------------------------------- /images/tabbar_discover_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_discover_hl.png -------------------------------------------------------------------------------- /images/tabbar_friends.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_friends.png -------------------------------------------------------------------------------- /images/tabbar_friends_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_friends_hl.png -------------------------------------------------------------------------------- /images/tabbar_mine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_mine.png -------------------------------------------------------------------------------- /images/tabbar_mine_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/tabbar_mine_hl.png -------------------------------------------------------------------------------- /images/五角星b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/五角星b.png -------------------------------------------------------------------------------- /images/五角星w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/五角星w.png -------------------------------------------------------------------------------- /images/公众号.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/公众号.png -------------------------------------------------------------------------------- /images/发起群聊.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/发起群聊.png -------------------------------------------------------------------------------- /images/圆加.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/圆加.png -------------------------------------------------------------------------------- /images/小程序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/小程序.png -------------------------------------------------------------------------------- /images/微信 支付.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/微信 支付.png -------------------------------------------------------------------------------- /images/微信卡包.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/微信卡包.png -------------------------------------------------------------------------------- /images/微信收藏.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/微信收藏.png -------------------------------------------------------------------------------- /images/微信相册.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/微信相册.png -------------------------------------------------------------------------------- /images/微信表情.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/微信表情.png -------------------------------------------------------------------------------- /images/微信设置.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/微信设置.png -------------------------------------------------------------------------------- /images/扫一扫.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/扫一扫.png -------------------------------------------------------------------------------- /images/扫一扫1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/扫一扫1.png -------------------------------------------------------------------------------- /images/扫一扫2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/扫一扫2.png -------------------------------------------------------------------------------- /images/搜一搜 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/搜一搜 2.png -------------------------------------------------------------------------------- /images/搜一搜.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/搜一搜.png -------------------------------------------------------------------------------- /images/搜一搜2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/搜一搜2.png -------------------------------------------------------------------------------- /images/摇一摇.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/摇一摇.png -------------------------------------------------------------------------------- /images/收付款.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/收付款.png -------------------------------------------------------------------------------- /images/放大镜b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/放大镜b.png -------------------------------------------------------------------------------- /images/放大镜w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/放大镜w.png -------------------------------------------------------------------------------- /images/新的朋友.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/新的朋友.png -------------------------------------------------------------------------------- /images/朋友圈.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/朋友圈.png -------------------------------------------------------------------------------- /images/标星.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/标星.png -------------------------------------------------------------------------------- /images/标签.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/标签.png -------------------------------------------------------------------------------- /images/气泡.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/气泡.png -------------------------------------------------------------------------------- /images/添加朋友.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/添加朋友.png -------------------------------------------------------------------------------- /images/游戏.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/游戏.png -------------------------------------------------------------------------------- /images/游戏2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/游戏2.png -------------------------------------------------------------------------------- /images/相机.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/相机.png -------------------------------------------------------------------------------- /images/看一看icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/看一看icon.png -------------------------------------------------------------------------------- /images/群聊.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/群聊.png -------------------------------------------------------------------------------- /images/购物.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/购物.png -------------------------------------------------------------------------------- /images/附近的人icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/images/附近的人icon.png -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/shell/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/shell/Desktop/FlutterProject/wing_chat" 5 | export "FLUTTER_TARGET=/Users/shell/Desktop/FlutterProject/wing_chat/lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "FLUTTER_FRAMEWORK_DIR=/Users/shell/flutter/bin/cache/artifacts/engine/ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | export "TRACK_WIDGET_CREATION=true" 12 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Using a CDN with CocoaPods 1.7.2 or later can save a lot of time on pod installation, but it's experimental rather than the default. 2 | # source 'https://cdn.cocoapods.org/' 3 | 4 | # Uncomment this line to define a global platform for your project 5 | # platform :ios, '9.0' 6 | 7 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 8 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 9 | 10 | project 'Runner', { 11 | 'Debug' => :debug, 12 | 'Profile' => :release, 13 | 'Release' => :release, 14 | } 15 | 16 | def parse_KV_file(file, separator='=') 17 | file_abs_path = File.expand_path(file) 18 | if !File.exists? file_abs_path 19 | return []; 20 | end 21 | pods_ary = [] 22 | skip_line_start_symbols = ["#", "/"] 23 | File.foreach(file_abs_path) { |line| 24 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 25 | plugin = line.split(pattern=separator) 26 | if plugin.length == 2 27 | podname = plugin[0].strip() 28 | path = plugin[1].strip() 29 | podpath = File.expand_path("#{path}", file_abs_path) 30 | pods_ary.push({:name => podname, :path => podpath}); 31 | else 32 | puts "Invalid plugin specification: #{line}" 33 | end 34 | } 35 | return pods_ary 36 | end 37 | 38 | target 'Runner' do 39 | use_frameworks! 40 | 41 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 42 | # referring to absolute paths on developers' machines. 43 | system('rm -rf .symlinks') 44 | system('mkdir -p .symlinks/plugins') 45 | 46 | # Flutter Pods 47 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 48 | if generated_xcode_build_settings.empty? 49 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first." 50 | end 51 | generated_xcode_build_settings.map { |p| 52 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 53 | symlink = File.join('.symlinks', 'flutter') 54 | File.symlink(File.dirname(p[:path]), symlink) 55 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 56 | end 57 | } 58 | 59 | # Plugin Pods 60 | plugin_pods = parse_KV_file('../.flutter-plugins') 61 | plugin_pods.map { |p| 62 | symlink = File.join('.symlinks', 'plugins', p[:name]) 63 | File.symlink(p[:path], symlink) 64 | pod p[:name], :path => File.join(symlink, 'ios') 65 | } 66 | end 67 | 68 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 69 | install! 'cocoapods', :disable_input_output_paths => true 70 | 71 | post_install do |installer| 72 | installer.pods_project.targets.each do |target| 73 | target.build_configurations.each do |config| 74 | config.build_settings['ENABLE_BITCODE'] = 'NO' 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - fluttertoast (0.0.2): 4 | - Flutter 5 | - image_picker (0.0.1): 6 | - Flutter 7 | - JCore (2.1.1) 8 | - JMessage (3.9.1): 9 | - JCore (< 3.0.0, >= 2.0.0) 10 | - jmessage_flutter (0.0.1): 11 | - Flutter 12 | - JMessage 13 | - path_provider (0.0.1): 14 | - Flutter 15 | - shared_preferences (0.0.1): 16 | - Flutter 17 | 18 | DEPENDENCIES: 19 | - Flutter (from `.symlinks/flutter/ios`) 20 | - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) 21 | - image_picker (from `.symlinks/plugins/image_picker/ios`) 22 | - jmessage_flutter (from `.symlinks/plugins/jmessage_flutter/ios`) 23 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 24 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 25 | 26 | SPEC REPOS: 27 | https://github.com/CocoaPods/Specs.git: 28 | - JCore 29 | - JMessage 30 | 31 | EXTERNAL SOURCES: 32 | Flutter: 33 | :path: ".symlinks/flutter/ios" 34 | fluttertoast: 35 | :path: ".symlinks/plugins/fluttertoast/ios" 36 | image_picker: 37 | :path: ".symlinks/plugins/image_picker/ios" 38 | jmessage_flutter: 39 | :path: ".symlinks/plugins/jmessage_flutter/ios" 40 | path_provider: 41 | :path: ".symlinks/plugins/path_provider/ios" 42 | shared_preferences: 43 | :path: ".symlinks/plugins/shared_preferences/ios" 44 | 45 | SPEC CHECKSUMS: 46 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 47 | fluttertoast: b644586ef3b16f67fae9a1f8754cef6b2d6b634b 48 | image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f 49 | JCore: 4072327557f0b83cc22dd4041f2f839740c0077b 50 | JMessage: 43dfcd5348573b2f4f24357b12e9b19076322606 51 | jmessage_flutter: eb7c42cb2444efae6aa14c2083dc84929ca3c80a 52 | path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d 53 | shared_preferences: 430726339841afefe5142b9c1f50cb6bd7793e01 54 | 55 | PODFILE CHECKSUM: 10ae9c18d12c9ffc2275c9a159a3b1e281990db0 56 | 57 | COCOAPODS: 1.8.4 58 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 7 | CFBundleDisplayName 8 | 星光呓语 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | wing_chat 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | NSCameraUsageDescription 33 | WingChat想使用您的相机,用于拍摄照片 34 | NSPhotoLibraryAddUsageDescription 35 | WingChat想访问您的相册,用于保存照片 36 | NSPhotoLibraryUsageDescription 37 | WingChat想使用您的相册,用于获取相册照片 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIMainStoryboardFile 41 | Main 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UIViewControllerBasedStatusBarAppearance 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:wing_chat/provider/jmessage_manager_provider.dart'; 5 | import 'package:wing_chat/views/login_scene.dart'; 6 | import 'tools/sp_util.dart'; 7 | 8 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 9 | 10 | void main(){ 11 | SystemChrome.setPreferredOrientations( 12 | [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]) 13 | .then((_) async { 14 | /// 等待sp初始化完成后再运行app。 15 | /// sp初始化时间 release模式下30ms左右,debug模式下100多ms。 16 | await SpUtil.getInstance(); 17 | runApp(MultiProvider(providers: [ 18 | ChangeNotifierProvider( 19 | create: (_) => JMessageManagerProvider()..initPushMessage()) 20 | ], child: MaterialApp( 21 | // supportedLocales: [Locale('zh', ''), Locale('en', '')], 22 | title: '星光呓语', 23 | debugShowCheckedModeBanner: false, 24 | theme: ThemeData(primaryColor: Colors.white), 25 | home: LoginPage(), 26 | ))); 27 | 28 | // if (Platform.isAndroid) { 29 | SystemChrome.setSystemUIOverlayStyle( 30 | SystemUiOverlayStyle(statusBarColor: Colors.transparent)); 31 | // } else { 32 | }); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /lib/provider/group_chat_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | import 'package:extended_image/extended_image.dart'; 4 | import 'package:extended_text/extended_text.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:image_picker/image_picker.dart'; 7 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 8 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 9 | import 'package:wing_chat/tools/special_text_span_builder.dart'; 10 | import 'jmessage_manager_provider.dart'; 11 | 12 | class GroupChatProvider with ChangeNotifier { 13 | bool isLoading = false; 14 | bool _talkFOT = false; 15 | bool _emojiOpen = false; 16 | double _keyboardHeight = 275.0; 17 | 18 | BuildContext context; 19 | FocusNode fsNode = FocusNode(); 20 | TextEditingController textInputController = TextEditingController(); 21 | ScrollController scrollController = ScrollController(); 22 | RefreshController controller = RefreshController(); 23 | PPSpecialTextSpanBuilder mySpecialTextSpanBuilder = 24 | PPSpecialTextSpanBuilder(type: BuilderType.extendedText); 25 | 26 | /// 聊天内容列表 27 | List _chatWidgetChildren = []; 28 | 29 | /// 聊天内容缓存列表 30 | List _chatHistory = []; 31 | 32 | // UserInfoModel mySelf = UserInfoModel.fromJson(SpUtil.getObject("UserInfo")); 33 | 34 | JMGroup kGroup; 35 | JMGroupInfo kGroupInfo; 36 | JMConversationInfo kGroupConver; 37 | 38 | bool get talkFOT => _talkFOT; 39 | bool get emojiOpen => _emojiOpen; 40 | double get keyboardHeight => _keyboardHeight; 41 | 42 | /// 聊天内容列表 43 | List get chatWidgetChildren => _chatWidgetChildren; 44 | 45 | /// 总消息数 46 | int totalMsgCount = 0; 47 | double extraHeight = 0; 48 | 49 | set talkFOT(value) { 50 | _talkFOT = value; 51 | notifyListeners(); 52 | } 53 | 54 | set emojiOpen(value) { 55 | fsNode.unfocus(); 56 | _emojiOpen = value; 57 | notifyListeners(); 58 | } 59 | 60 | set keyboardHeight(value) { 61 | _keyboardHeight = max(_keyboardHeight, value); 62 | // notifyListeners(); 63 | } 64 | 65 | reloadInputView() { 66 | notifyListeners(); 67 | } 68 | 69 | List emojiArray = [ 70 | '[微笑]', 71 | '[撇嘴]', 72 | '[色]', 73 | '[发呆]', 74 | '[得意]', 75 | '[流泪]', 76 | '[害羞]', 77 | '[闭嘴]', 78 | '[睡]', 79 | '[大哭]', 80 | '[尴尬]', 81 | '[发怒]', 82 | '[调皮]', 83 | '[呲牙]', 84 | '[惊讶]', 85 | '[难过]', 86 | '[酷]', 87 | '[冷汗]', 88 | '[抓狂]', 89 | '[吐]', 90 | '[偷笑]', 91 | '[愉快]', 92 | '[白眼]', 93 | '[傲慢]', 94 | '[饥饿]', 95 | '[困]', 96 | '[惊恐]', 97 | '[流汗]', 98 | '[憨笑]', 99 | '[悠闲]', 100 | '[奋斗]', 101 | '[咒骂]', 102 | '[疑问]', 103 | '[嘘]', 104 | '[晕]', 105 | '[疯了]', 106 | '[衰]', 107 | '[骷髅]', 108 | '[敲打]', 109 | '[再见]', 110 | '[擦汗]', 111 | '[抠鼻]', 112 | '[鼓掌]', 113 | '[糗大了]', 114 | '[坏笑]', 115 | '[左哼哼]', 116 | '[右哼哼]', 117 | '[哈欠]', 118 | '[鄙视]', 119 | '[委屈]', 120 | '[快哭了]', 121 | '[阴险]', 122 | '[亲亲]', 123 | '[吓]', 124 | '[可怜]', 125 | '[菜刀]', 126 | '[西瓜]', 127 | '[啤酒]', 128 | '[篮球]', 129 | '[乒乓]', 130 | '[咖啡]', 131 | '[饭]', 132 | '[猪头]', 133 | '[玫瑰]', 134 | '[凋谢]', 135 | '[嘴唇]', 136 | '[爱心]', 137 | '[心碎]', 138 | '[蛋糕]', 139 | '[闪电]', 140 | '[炸弹]', 141 | '[刀]', 142 | '[足球]', 143 | '[瓢虫]', 144 | '[便便]', 145 | '[月亮]', 146 | '[太阳]', 147 | '[礼物]', 148 | '[拥抱]', 149 | '[强]', 150 | '[弱]', 151 | '[握手]', 152 | '[胜利]', 153 | '[抱拳]', 154 | '[勾引]', 155 | '[拳头]', 156 | '[差劲]', 157 | '[爱你]', 158 | '[NO]', 159 | '[OK]', 160 | ]; 161 | 162 | /// 初始化数据 163 | initConversation() { 164 | kGroup = JMGroup.fromJson( 165 | {'type': JMGroupType.private, 'groupId': kGroupInfo.id}); 166 | _createGroupConversation(); 167 | } 168 | 169 | /// 创建会话 170 | _createGroupConversation() async { 171 | kGroupConver = await JMessage.createConversation(target: kGroup); 172 | 173 | JMessage.resetUnreadMessageCount(target: kGroup); 174 | 175 | await JMessage.enterConversation(target: kGroup); 176 | JMessage.addReceiveMessageListener((msg) async { 177 | print('消息监听++++++++++++++++++++++++++++++++++++++++'); 178 | isLoading = false; 179 | if (kGroupConver.isMyMessage(msg)) { 180 | _parseMessage(msg, false); 181 | } 182 | }); 183 | getHistoryMessages(); 184 | } 185 | 186 | /// 获取历史消息 187 | getHistoryMessages() async { 188 | isLoading = true; 189 | var _historyList = await kGroupConver.getHistoryMessages( 190 | from: totalMsgCount, limit: 20, isDescend: true); 191 | if (_historyList == null) { 192 | // PPToast.show('没有更多历史消息'); 193 | controller.loadNoData(); 194 | } else { 195 | totalMsgCount += _historyList.length; 196 | _historyList.forEach((message) { 197 | _parseMessage(message, true); 198 | }); 199 | } 200 | } 201 | 202 | /// 解析消息发送方 203 | JMUserInfo _parseOptUser(message, bool isHistory) { 204 | JMUserInfo optUser; 205 | 206 | if (message.target is JMGroupInfo) { 207 | optUser = message.from; 208 | } else if (message.target is JMUserInfo) { 209 | optUser = message.target; 210 | } else {} 211 | 212 | return optUser; 213 | } 214 | 215 | /// 消息解析 216 | _insertMsgToHistory(PPMessageModel messageModel, bool isHistory) { 217 | if (isHistory) { 218 | _chatHistory.insert(0, messageModel); 219 | } else { 220 | _chatHistory.add(messageModel); 221 | } 222 | _refreshMessageList(isHistory); 223 | } 224 | 225 | /// 消息解析 226 | _parseMessage(msg, bool isHistory) { 227 | if (msg is JMTextMessage) { 228 | JMTextMessage textMessage = JMTextMessage.fromJson(msg.toJson()); 229 | JMUserInfo user = _parseOptUser(msg, isHistory); 230 | 231 | PPMessageModel msgModel = PPMessageModel() 232 | ..nickname = user.nickname 233 | ..userId = user.username 234 | ..avatar = user.extras['avatar'] 235 | ..text = textMessage.text 236 | ..message = textMessage 237 | ..messageType = JMMessageType.text; 238 | 239 | _insertMsgToHistory(msgModel, isHistory); 240 | } else if (msg is JMImageMessage) { 241 | JMImageMessage imageMessage = JMImageMessage.fromJson(msg.toJson()); 242 | JMUserInfo user = _parseOptUser(msg, isHistory); 243 | 244 | File imagefile = File('${imageMessage.thumbPath}'); 245 | 246 | PPMessageModel msgModel = PPMessageModel() 247 | ..nickname = user.nickname 248 | ..userId = user.username 249 | ..avatar = user.extras['avatar'] 250 | ..imageFile = imagefile 251 | ..message = imageMessage 252 | ..messageType = JMMessageType.image; 253 | 254 | _insertMsgToHistory(msgModel, isHistory); 255 | } 256 | } 257 | 258 | sendTextMessage(content) { 259 | kGroupConver.sendTextMessage(text: content).then((msg) { 260 | if (content != '' && content != null) { 261 | // _sendMessageContent(content, 'text'); 262 | _parseMessage(msg, false); 263 | totalMsgCount += 1; 264 | textInputController.clear(); 265 | notifyListeners(); 266 | } 267 | }).catchError((error) {}); 268 | } 269 | 270 | _refreshMessageList(bool isHistory) { 271 | List widgetList = []; 272 | for (var i = 0; i < _chatHistory.length; i++) { 273 | widgetList.add(_layoutMessageCell(_chatHistory[i])); 274 | } 275 | // _chatWidgetChildren = widgetList; 276 | _chatWidgetChildren = widgetList.reversed.toList(); 277 | controller.loadComplete(); 278 | notifyListeners(); 279 | JMessage.resetUnreadMessageCount(target: kGroup); 280 | 281 | if (!isHistory) { 282 | scrollController.animateTo( 283 | 0.0, 284 | curve: Curves.linear, 285 | duration: const Duration(milliseconds: 300), 286 | ); 287 | } 288 | } 289 | 290 | void sendImageMessage() async { 291 | var image = await ImagePicker.pickImage(source: ImageSource.gallery); 292 | if (image != null) { 293 | kGroupConver.sendImageMessage(path: image.path).then((msg) async { 294 | _parseMessage(msg, false); 295 | totalMsgCount += 1; 296 | }).catchError((error) {}); 297 | } 298 | } 299 | 300 | /// 根据消息类型加载消息内容 301 | _messageContentType(PPMessageModel msgModel, String otherId) { 302 | switch (msgModel.messageType) { 303 | case JMMessageType.image: 304 | return InkWell( 305 | onTap: () { 306 | JMessage 307 | .downloadOriginalImage( 308 | messageId: Platform.isAndroid 309 | ? msgModel.message.serverMessageId 310 | : msgModel.message.id, 311 | target: kGroup) 312 | .then((result) { 313 | if (result.isNotEmpty) { 314 | showDialog( 315 | context: context, 316 | builder: (context) { 317 | return GestureDetector( 318 | onTap: (){ 319 | Navigator.pop(context); 320 | }, 321 | child: Container( 322 | width: double.infinity, 323 | height: double.infinity, 324 | color: Colors.black, 325 | child: ExtendedImage.file( 326 | File(result['filePath']), fit: BoxFit.contain, 327 | //enableLoadState: false, 328 | mode: ExtendedImageMode.gesture, 329 | onDoubleTap: (tap){ 330 | Navigator.pop(context); 331 | }, 332 | initGestureConfigHandler: (state) { 333 | return GestureConfig( 334 | minScale: 0.9, 335 | animationMinScale: 0.7, 336 | maxScale: 3.0, 337 | animationMaxScale: 3.5, 338 | speed: 1.0, 339 | inertialSpeed: 100.0, 340 | initialScale: 1.0, 341 | inPageView: false); 342 | }, 343 | ), 344 | ), 345 | ); 346 | }); 347 | } 348 | }).catchError((error) { 349 | print("下载出错:${error.toString()}"); 350 | }); 351 | }, 352 | child: Image.file( 353 | msgModel.imageFile, 354 | height: 200 * 9 / 16.0, 355 | fit: BoxFit.fitWidth, 356 | ), 357 | ); 358 | break; 359 | case JMMessageType.text: 360 | return ExtendedText( 361 | msgModel.text, 362 | style: TextStyle( 363 | color: msgModel.message.isSend ? Colors.white : Colors.black), 364 | specialTextSpanBuilder: mySpecialTextSpanBuilder, 365 | ); 366 | break; 367 | default: 368 | break; 369 | } 370 | } 371 | 372 | _layoutMessageCell(PPMessageModel msgModel) { 373 | List widgetList = []; 374 | var msgPadding; 375 | var msgMargin; 376 | var msgColor; 377 | 378 | if (msgModel.message.isSend) { 379 | msgMargin = EdgeInsets.only(right: 10, top: 10); 380 | msgColor = Colors.lightBlue; 381 | } else { 382 | msgMargin = EdgeInsets.only(left: 10, top: 10); 383 | msgColor = Colors.white; 384 | } 385 | 386 | if (msgModel.messageType == JMMessageType.image) { 387 | msgPadding = EdgeInsets.all(0); 388 | msgColor = Colors.transparent; 389 | } else { 390 | msgPadding = EdgeInsets.all(10.0); 391 | } 392 | 393 | Widget userAvatar = CircleAvatar( 394 | radius: 25, 395 | backgroundImage: NetworkImage('${msgModel.avatar}'), 396 | ); 397 | 398 | Widget msgContent = Container( 399 | margin: msgMargin, 400 | padding: msgPadding, 401 | decoration: BoxDecoration( 402 | color: msgColor, borderRadius: BorderRadius.circular(10.0)), 403 | child: LimitedBox( 404 | maxWidth: MediaQuery.of(context).size.width - 120.0, 405 | child: _messageContentType(msgModel, msgModel.userId), 406 | )); 407 | 408 | if (msgModel.message.isSend) { 409 | widgetList = [msgContent, userAvatar]; 410 | } else { 411 | widgetList = [userAvatar, msgContent]; 412 | } 413 | 414 | return Container( 415 | width: MediaQuery.of(context).size.width - 120.0, 416 | margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20), 417 | child: Row( 418 | mainAxisAlignment: msgModel.message.isSend 419 | ? MainAxisAlignment.end 420 | : MainAxisAlignment.start, 421 | crossAxisAlignment: CrossAxisAlignment.start, 422 | children: widgetList)); 423 | } 424 | 425 | @override 426 | void dispose() { 427 | super.dispose(); 428 | controller.dispose(); 429 | JMessage.exitConversation(target: kGroup); 430 | JMessage.removeReceiveMessageListener((message) {}); 431 | } 432 | } 433 | 434 | class PPMessageModel { 435 | String nickname; 436 | String userId; 437 | String avatar; 438 | String text; 439 | File imageFile; 440 | JMNormalMessage message; 441 | JMMessageType messageType; 442 | 443 | PPMessageModel( 444 | {this.nickname, 445 | this.userId, 446 | this.avatar, 447 | this.text, 448 | this.imageFile, 449 | this.message, 450 | this.messageType}); 451 | } 452 | -------------------------------------------------------------------------------- /lib/provider/group_list_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 3 | import 'package:wing_chat/tools/sp_util.dart'; 4 | 5 | import 'jmessage_manager_provider.dart'; 6 | 7 | 8 | class GroupListProvider with ChangeNotifier { 9 | List _groupList = []; 10 | 11 | List get groupList => _groupList; 12 | 13 | // List _groupIdArray = []; 14 | // List get groupIdArray => _groupIdArray; 15 | 16 | int _unreadCount = 0; 17 | 18 | int get unreadCount => _unreadCount; 19 | 20 | 21 | set unreadCount(value){ 22 | _unreadCount = value; 23 | notifyListeners(); 24 | } 25 | 26 | 27 | // set groupIdArray(value){ 28 | // _groupIdArray = value; 29 | // getConversationList(); 30 | // } 31 | 32 | loginStatusCheck() async { 33 | // var userModel = UserInfoModel.fromJson(SpUtil.getObject("UserInfo")); 34 | JMessage 35 | .login(username: SpUtil.getString('jUserName'), password: SpUtil.getString('jPassword')) 36 | .then((userInfo) { 37 | print(userInfo); 38 | receiveMessageListener(); 39 | receiveApplyJoinGroupApprovalListener(); 40 | getConversationList(); 41 | // getMyGroup(); 42 | }).catchError((error) { 43 | // PPToast.show('用户群聊登录失败!'); 44 | print(error); 45 | }); 46 | } 47 | 48 | receiveMessageListener() async { 49 | JMessage.addReceiveMessageListener((msg) { 50 | if (msg is JMEventMessage) { 51 | // getMyGroup(); 52 | }else{ 53 | getConversationList(); 54 | } 55 | }); 56 | } 57 | /// 监听入群申请 58 | receiveApplyJoinGroupApprovalListener() async { 59 | JMessage.addReceiveApplyJoinGroupApprovalListener((event){ 60 | JMessage.processApplyJoinGroup(events: [event.eventId],isAgree: true,reason: '同意', isRespondInviter: false).then((result){ 61 | 62 | }).catchError((error){ 63 | 64 | }); 65 | }); 66 | } 67 | 68 | // getMyGroup() { 69 | // jmessage.getGroupIds().then((groupIds) { 70 | // groupIdArray = groupIds; 71 | // }).catchError((error) {}); 72 | // } 73 | 74 | getConversationList() async { 75 | print("************************"); 76 | 77 | JMessage.getConversations().then((allConversations) { 78 | print("************************"); 79 | print(allConversations); 80 | 81 | _groupList = allConversations; 82 | if (_groupList == null) { 83 | _groupList = []; 84 | } else { 85 | int unreadNum = 0; 86 | _groupList.removeWhere((conversation) { 87 | unreadNum += conversation.unreadCount; 88 | return false; 89 | // if (conversation.conversationType == JMConversationType.group) { 90 | // var groupInfo = JMGroupInfo.fromJson(conversation.target.toJson()); 91 | // if (_groupIdArray.contains(groupInfo.id)) { 92 | // unreadNum += conversation.unreadCount; 93 | // return false; 94 | // } else { 95 | // jmessage.deleteConversation( 96 | // target: JMGroup.fromJson( 97 | // {'type': JMGroupType.private, 'groupId': groupInfo.id})); 98 | // return true; 99 | // } 100 | // } else { 101 | // return true; 102 | // } 103 | }); 104 | unreadCount = unreadNum; 105 | notifyListeners(); 106 | } 107 | }).catchError((error) { 108 | print(error); 109 | loginStatusCheck(); 110 | }); 111 | notifyListeners(); 112 | 113 | } 114 | 115 | @override 116 | void dispose() { 117 | super.dispose(); 118 | // jmessage.removeReceiveMessageListener((message){}); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/provider/jmessage_manager_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 4 | import 'package:platform/platform.dart'; 5 | 6 | MethodChannel channel = MethodChannel('jmessage_flutter'); 7 | JmessageFlutter JMessage = JmessageFlutter(); 8 | 9 | typedef LoginCallback = void Function(bool isSuccess); 10 | 11 | class JMessageManagerProvider with ChangeNotifier { 12 | // final JPush jpush = JPush(); 13 | 14 | initPushMessage() async { 15 | // jpush.getRegistrationID().then((rid) {}); 16 | 17 | // jpush.setup( 18 | // appKey: "", 19 | // channel: "dev", 20 | // production: false, 21 | // debug: true, 22 | // ); 23 | 24 | // jpush.applyPushAuthority(NotificationSettingsIOS(sound: true, alert: true, badge: true)); 25 | 26 | // try { 27 | // jpush.addEventHandler( 28 | // onReceiveNotification: (Map message) async { 29 | // print("flutter onReceiveNotification: $message"); 30 | // }, 31 | // onOpenNotification: (Map message) async { 32 | // print("flutter onOpenNotification: $message"); 33 | // }, 34 | // onReceiveMessage: (Map message) async { 35 | // print("flutter onReceiveMessage: $message"); 36 | // }, 37 | // ); 38 | // } on PlatformException { 39 | // // platformVersion = 'Failed to get platform version.'; 40 | // } 41 | 42 | JMessage.setDebugMode(enable: true); 43 | JMessage.init( 44 | isOpenMessageRoaming: true, appkey: '65e831cf441e2df70442995a',channel: 'test'); 45 | JMessage.applyPushAuthority( 46 | JMNotificationSettingsIOS(sound: true, alert: true, badge: true)); 47 | } 48 | 49 | juserLogin(LoginCallback callback, String jUserName, String jUserPWD) async { 50 | // await jmessage.login( 51 | // username: jUserName, password: jUserPWD); 52 | // return loginResult(callback); 53 | JMessage.login(username: jUserName, password: jUserPWD).then((jUser) { 54 | callback(jUser != null); 55 | }).catchError((error) {}); 56 | } 57 | 58 | loginResult(LoginCallback callback) async { 59 | JMUserInfo user = await JMessage.getMyInfo(); 60 | print(user.toJson()); 61 | return callback(user != null); 62 | } 63 | 64 | loginStatusChanged() async { 65 | JMessage.addLoginStateChangedListener((JMLoginStateChangedType type) { 66 | print('flutter receive event receive login state change $type'); 67 | switch (type) { 68 | case JMLoginStateChangedType.user_login_status_unexpected: 69 | break; 70 | case JMLoginStateChangedType.user_disabled: 71 | break; 72 | case JMLoginStateChangedType.user_logout: 73 | break; 74 | default: 75 | } 76 | }); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/provider/root_scene_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RootSceneProvider with ChangeNotifier { 4 | int _currentIndex = 0; 5 | int get currentIndex => _currentIndex; 6 | 7 | set currentIndex(value) { 8 | _currentIndex = value; 9 | notifyListeners(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/provider/single_chat_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | import 'package:extended_image/extended_image.dart'; 4 | import 'package:extended_text/extended_text.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:image_picker/image_picker.dart'; 7 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 8 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 9 | import 'package:wing_chat/tools/special_text_span_builder.dart'; 10 | import 'jmessage_manager_provider.dart'; 11 | 12 | class SingleChatProvider with ChangeNotifier { 13 | bool isLoading = false; 14 | bool _talkFOT = false; 15 | bool _emojiOpen = false; 16 | double _keyboardHeight = 275.0; 17 | 18 | BuildContext context; 19 | FocusNode fsNode = FocusNode(); 20 | TextEditingController textInputController = TextEditingController(); 21 | ScrollController scrollController = ScrollController(); 22 | RefreshController controller = RefreshController(); 23 | PPSpecialTextSpanBuilder mySpecialTextSpanBuilder = 24 | PPSpecialTextSpanBuilder(type: BuilderType.extendedText); 25 | 26 | /// 聊天内容列表 27 | List _chatWidgetChildren = []; 28 | 29 | /// 聊天内容缓存列表 30 | List _chatHistory = []; 31 | 32 | // UserInfoModel mySelf = UserInfoModel.fromJson(SpUtil.getObject("UserInfo")); 33 | 34 | JMSingle jmSingle; 35 | JMUserInfo jmUserInfo; 36 | JMConversationInfo jSingleConver; 37 | 38 | bool get talkFOT => _talkFOT; 39 | bool get emojiOpen => _emojiOpen; 40 | double get keyboardHeight => _keyboardHeight; 41 | 42 | /// 聊天内容列表 43 | List get chatWidgetChildren => _chatWidgetChildren; 44 | 45 | /// 总消息数 46 | int totalMsgCount = 0; 47 | double extraHeight = 0; 48 | 49 | set talkFOT(value) { 50 | _talkFOT = value; 51 | notifyListeners(); 52 | } 53 | 54 | set emojiOpen(value) { 55 | fsNode.unfocus(); 56 | _emojiOpen = value; 57 | notifyListeners(); 58 | } 59 | 60 | set keyboardHeight(value) { 61 | _keyboardHeight = max(_keyboardHeight, value); 62 | // notifyListeners(); 63 | } 64 | 65 | reloadInputView() { 66 | notifyListeners(); 67 | } 68 | 69 | List emojiArray = [ 70 | '[微笑]', 71 | '[撇嘴]', 72 | '[色]', 73 | '[发呆]', 74 | '[得意]', 75 | '[流泪]', 76 | '[害羞]', 77 | '[闭嘴]', 78 | '[睡]', 79 | '[大哭]', 80 | '[尴尬]', 81 | '[发怒]', 82 | '[调皮]', 83 | '[呲牙]', 84 | '[惊讶]', 85 | '[难过]', 86 | '[酷]', 87 | '[冷汗]', 88 | '[抓狂]', 89 | '[吐]', 90 | '[偷笑]', 91 | '[愉快]', 92 | '[白眼]', 93 | '[傲慢]', 94 | '[饥饿]', 95 | '[困]', 96 | '[惊恐]', 97 | '[流汗]', 98 | '[憨笑]', 99 | '[悠闲]', 100 | '[奋斗]', 101 | '[咒骂]', 102 | '[疑问]', 103 | '[嘘]', 104 | '[晕]', 105 | '[疯了]', 106 | '[衰]', 107 | '[骷髅]', 108 | '[敲打]', 109 | '[再见]', 110 | '[擦汗]', 111 | '[抠鼻]', 112 | '[鼓掌]', 113 | '[糗大了]', 114 | '[坏笑]', 115 | '[左哼哼]', 116 | '[右哼哼]', 117 | '[哈欠]', 118 | '[鄙视]', 119 | '[委屈]', 120 | '[快哭了]', 121 | '[阴险]', 122 | '[亲亲]', 123 | '[吓]', 124 | '[可怜]', 125 | '[菜刀]', 126 | '[西瓜]', 127 | '[啤酒]', 128 | '[篮球]', 129 | '[乒乓]', 130 | '[咖啡]', 131 | '[饭]', 132 | '[猪头]', 133 | '[玫瑰]', 134 | '[凋谢]', 135 | '[嘴唇]', 136 | '[爱心]', 137 | '[心碎]', 138 | '[蛋糕]', 139 | '[闪电]', 140 | '[炸弹]', 141 | '[刀]', 142 | '[足球]', 143 | '[瓢虫]', 144 | '[便便]', 145 | '[月亮]', 146 | '[太阳]', 147 | '[礼物]', 148 | '[拥抱]', 149 | '[强]', 150 | '[弱]', 151 | '[握手]', 152 | '[胜利]', 153 | '[抱拳]', 154 | '[勾引]', 155 | '[拳头]', 156 | '[差劲]', 157 | '[爱你]', 158 | '[NO]', 159 | '[OK]', 160 | ]; 161 | 162 | /// 初始化数据 163 | initConversation() { 164 | jmSingle = JMSingle.fromJson({ 165 | "username": jmUserInfo.username, 166 | "appKey": '65e831cf441e2df70442995a' 167 | }); 168 | _createGroupConversation(); 169 | } 170 | 171 | /// 创建会话 172 | _createGroupConversation() async { 173 | jSingleConver = await JMessage.createConversation(target: jmSingle); 174 | 175 | JMessage.resetUnreadMessageCount(target: jmSingle); 176 | 177 | await JMessage.enterConversation(target: jmSingle); 178 | JMessage.addReceiveMessageListener((msg) async { 179 | print('消息监听++++++++++++++++++++++++++++++++++++++++'); 180 | isLoading = false; 181 | if (jSingleConver.isMyMessage(msg)) { 182 | _parseMessage(msg, false); 183 | } 184 | }); 185 | getHistoryMessages(); 186 | } 187 | 188 | /// 获取历史消息 189 | getHistoryMessages() async { 190 | isLoading = true; 191 | var _historyList = await jSingleConver.getHistoryMessages( 192 | from: totalMsgCount, limit: 20, isDescend: true); 193 | if (_historyList == null) { 194 | // PPToast.show('没有更多历史消息'); 195 | controller.loadNoData(); 196 | } else { 197 | totalMsgCount += _historyList.length; 198 | _historyList.forEach((message) { 199 | _parseMessage(message, true); 200 | }); 201 | } 202 | } 203 | 204 | /// 解析消息发送方 205 | JMUserInfo _parseOptUser(message, bool isHistory) { 206 | JMUserInfo optUser; 207 | 208 | if (message.target is JMGroupInfo) { 209 | optUser = message.from; 210 | } else if (message.target is JMUserInfo) { 211 | optUser = message.target; 212 | } else {} 213 | 214 | return optUser; 215 | } 216 | 217 | /// 消息解析 218 | _insertMsgToHistory(PPMessageModel messageModel, bool isHistory) { 219 | if (isHistory) { 220 | _chatHistory.insert(0, messageModel); 221 | } else { 222 | _chatHistory.add(messageModel); 223 | } 224 | _refreshMessageList(isHistory); 225 | } 226 | 227 | /// 消息解析 228 | _parseMessage(msg, bool isHistory) { 229 | if (msg is JMTextMessage) { 230 | JMTextMessage textMessage = JMTextMessage.fromJson(msg.toJson()); 231 | JMUserInfo user = _parseOptUser(msg, isHistory); 232 | 233 | PPMessageModel msgModel = PPMessageModel() 234 | ..nickname = user.nickname 235 | ..userId = user.username 236 | ..avatar = user.extras['avatar'] 237 | ..text = textMessage.text 238 | ..message = textMessage 239 | ..messageType = JMMessageType.text; 240 | 241 | _insertMsgToHistory(msgModel, isHistory); 242 | } else if (msg is JMImageMessage) { 243 | JMImageMessage imageMessage = JMImageMessage.fromJson(msg.toJson()); 244 | JMUserInfo user = _parseOptUser(msg, isHistory); 245 | 246 | File imagefile = File('${imageMessage.thumbPath}'); 247 | 248 | PPMessageModel msgModel = PPMessageModel() 249 | ..nickname = user.nickname 250 | ..userId = user.username 251 | ..avatar = user.extras['avatar'] 252 | ..imageFile = imagefile 253 | ..message = imageMessage 254 | ..messageType = JMMessageType.image; 255 | 256 | _insertMsgToHistory(msgModel, isHistory); 257 | } 258 | } 259 | 260 | sendTextMessage(content) { 261 | jSingleConver.sendTextMessage(text: content).then((msg) { 262 | if (content != '' && content != null) { 263 | // _sendMessageContent(content, 'text'); 264 | _parseMessage(msg, false); 265 | totalMsgCount += 1; 266 | textInputController.clear(); 267 | notifyListeners(); 268 | } 269 | }).catchError((error) {}); 270 | } 271 | 272 | _refreshMessageList(bool isHistory) { 273 | List widgetList = []; 274 | for (var i = 0; i < _chatHistory.length; i++) { 275 | widgetList.add(_layoutMessageCell(_chatHistory[i])); 276 | } 277 | // _chatWidgetChildren = widgetList; 278 | _chatWidgetChildren = widgetList.reversed.toList(); 279 | controller.loadComplete(); 280 | notifyListeners(); 281 | JMessage.resetUnreadMessageCount(target: jmSingle); 282 | 283 | if (!isHistory) { 284 | scrollController.animateTo( 285 | 0.0, 286 | curve: Curves.linear, 287 | duration: const Duration(milliseconds: 300), 288 | ); 289 | } 290 | } 291 | 292 | void sendImageMessage() async { 293 | var image = await ImagePicker.pickImage(source: ImageSource.gallery); 294 | if (image != null) { 295 | jSingleConver.sendImageMessage(path: image.path).then((msg) async { 296 | _parseMessage(msg, false); 297 | totalMsgCount += 1; 298 | }).catchError((error) {}); 299 | } 300 | } 301 | 302 | /// 根据消息类型加载消息内容 303 | _messageContentType(PPMessageModel msgModel, String otherId) { 304 | switch (msgModel.messageType) { 305 | case JMMessageType.image: 306 | return InkWell( 307 | onTap: () { 308 | JMessage 309 | .downloadOriginalImage( 310 | messageId: Platform.isAndroid 311 | ? msgModel.message.serverMessageId 312 | : msgModel.message.id, 313 | target: jmSingle) 314 | .then((result) { 315 | if (result.isNotEmpty) { 316 | showDialog( 317 | context: context, 318 | builder: (context) { 319 | return GestureDetector( 320 | onTap: (){ 321 | Navigator.pop(context); 322 | }, 323 | child: Container( 324 | width: double.infinity, 325 | height: double.infinity, 326 | color: Colors.black, 327 | child: ExtendedImage.file( 328 | File(result['filePath']), fit: BoxFit.contain, 329 | //enableLoadState: false, 330 | mode: ExtendedImageMode.gesture, 331 | onDoubleTap: (tap){ 332 | Navigator.pop(context); 333 | }, 334 | initGestureConfigHandler: (state) { 335 | return GestureConfig( 336 | minScale: 0.9, 337 | animationMinScale: 0.7, 338 | maxScale: 3.0, 339 | animationMaxScale: 3.5, 340 | speed: 1.0, 341 | inertialSpeed: 100.0, 342 | initialScale: 1.0, 343 | inPageView: false); 344 | }, 345 | ), 346 | ), 347 | ); 348 | }); 349 | } 350 | }).catchError((error) { 351 | print("下载出错:${error.toString()}"); 352 | }); 353 | }, 354 | child: Image.file( 355 | msgModel.imageFile, 356 | height: 200 * 9 / 16.0, 357 | fit: BoxFit.fitWidth, 358 | ), 359 | ); 360 | break; 361 | case JMMessageType.text: 362 | return ExtendedText( 363 | msgModel.text, 364 | style: TextStyle( 365 | color: msgModel.message.isSend ? Colors.white : Colors.black), 366 | specialTextSpanBuilder: mySpecialTextSpanBuilder, 367 | ); 368 | break; 369 | default: 370 | break; 371 | } 372 | } 373 | 374 | _layoutMessageCell(PPMessageModel msgModel) { 375 | List widgetList = []; 376 | var msgPadding; 377 | var msgMargin; 378 | var msgColor; 379 | 380 | if (msgModel.message.isSend) { 381 | msgMargin = EdgeInsets.only(right: 10, top: 10); 382 | msgColor = Colors.lightBlue; 383 | } else { 384 | msgMargin = EdgeInsets.only(left: 10, top: 10); 385 | msgColor = Colors.white; 386 | } 387 | 388 | if (msgModel.messageType == JMMessageType.image) { 389 | msgPadding = EdgeInsets.all(0); 390 | msgColor = Colors.transparent; 391 | } else { 392 | msgPadding = EdgeInsets.all(10.0); 393 | } 394 | 395 | Widget userAvatar = CircleAvatar( 396 | radius: 25, 397 | backgroundImage: NetworkImage('${msgModel.avatar}'), 398 | ); 399 | 400 | Widget msgContent = Container( 401 | margin: msgMargin, 402 | padding: msgPadding, 403 | decoration: BoxDecoration( 404 | color: msgColor, borderRadius: BorderRadius.circular(10.0)), 405 | child: LimitedBox( 406 | maxWidth: MediaQuery.of(context).size.width - 120.0, 407 | child: _messageContentType(msgModel, msgModel.userId), 408 | )); 409 | 410 | if (msgModel.message.isSend) { 411 | widgetList = [msgContent, userAvatar]; 412 | } else { 413 | widgetList = [userAvatar, msgContent]; 414 | } 415 | 416 | return Container( 417 | width: MediaQuery.of(context).size.width - 120.0, 418 | margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20), 419 | child: Row( 420 | mainAxisAlignment: msgModel.message.isSend 421 | ? MainAxisAlignment.end 422 | : MainAxisAlignment.start, 423 | crossAxisAlignment: CrossAxisAlignment.start, 424 | children: widgetList)); 425 | } 426 | 427 | @override 428 | void dispose() { 429 | super.dispose(); 430 | controller.dispose(); 431 | JMessage.exitConversation(target: jmSingle); 432 | JMessage.removeReceiveMessageListener((message) {}); 433 | } 434 | } 435 | 436 | class PPMessageModel { 437 | String nickname; 438 | String userId; 439 | String avatar; 440 | String text; 441 | File imageFile; 442 | JMNormalMessage message; 443 | JMMessageType messageType; 444 | 445 | PPMessageModel( 446 | {this.nickname, 447 | this.userId, 448 | this.avatar, 449 | this.text, 450 | this.imageFile, 451 | this.message, 452 | this.messageType}); 453 | } 454 | -------------------------------------------------------------------------------- /lib/tools/app_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'dart:ui' as ui show window; 4 | 5 | class Screen { 6 | static double get width { 7 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 8 | return mediaQuery.size.width; 9 | } 10 | 11 | static double get height { 12 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 13 | return mediaQuery.size.height; 14 | } 15 | 16 | static double get scale { 17 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 18 | return mediaQuery.devicePixelRatio; 19 | } 20 | 21 | static double get textScaleFactor { 22 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 23 | return mediaQuery.textScaleFactor; 24 | } 25 | 26 | static double get navigationBarHeight { 27 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 28 | return mediaQuery.padding.top + kToolbarHeight; 29 | } 30 | 31 | static double get topSafeHeight { 32 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 33 | return mediaQuery.padding.top; 34 | } 35 | 36 | static double get bottomSafeHeight { 37 | MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window); 38 | return mediaQuery.padding.bottom; 39 | } 40 | 41 | static updateStatusBarStyle(SystemUiOverlayStyle style) { 42 | SystemChrome.setSystemUIOverlayStyle(style); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/tools/app_toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | class AppToast { 5 | static show(String msg) { 6 | Fluttertoast.showToast(msg: msg,gravity: ToastGravity.CENTER,backgroundColor: Color.fromRGBO(231, 236, 242, 1),textColor: Colors.black); 7 | } 8 | } -------------------------------------------------------------------------------- /lib/tools/emoji_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:extended_text/extended_text.dart'; 3 | 4 | ///emoji/image text 5 | class EmojiText extends SpecialText { 6 | static const String flag = "["; 7 | final int start; 8 | EmojiText(TextStyle textStyle, {this.start}) 9 | : super(EmojiText.flag, "]", textStyle); 10 | 11 | @override 12 | InlineSpan finishText() { 13 | // TODO: implement finishText 14 | var key = toString(); 15 | if (EmojiUitl.instance.emojiMap.containsKey(key)) { 16 | //fontsize id define image height 17 | //size = 30.0/26.0 * fontSize 18 | final double size = 20.0; 19 | 20 | ///fontSize 26 and text height =30.0 21 | //final double fontSize = 26.0; 22 | 23 | return ImageSpan(AssetImage(EmojiUitl.instance.emojiMap[key]), 24 | actualText: key, 25 | imageWidth: size, 26 | imageHeight: size, 27 | start: start, 28 | fit: BoxFit.fill, 29 | margin: EdgeInsets.only(left: 2.0, right: 2.0)); 30 | } 31 | 32 | return TextSpan(text: toString(), style: textStyle); 33 | } 34 | } 35 | 36 | class EmojiUitl { 37 | final Map _emojiMap = new Map(); 38 | 39 | Map get emojiMap => _emojiMap; 40 | List emojiArray = [ 41 | '[微笑]', '[撇嘴]', '[色]', '[发呆]', '[得意]', '[流泪]', '[害羞]', 42 | '[闭嘴]', '[睡]', '[大哭]', '[尴尬]', '[发怒]', '[调皮]', '[呲牙]', 43 | '[惊讶]','[难过]','[酷]','[冷汗]','[抓狂]','[吐]','[偷笑]', 44 | '[愉快]','[白眼]','[傲慢]','[饥饿]','[困]','[惊恐]','[流汗]', 45 | '[憨笑]','[悠闲]','[奋斗]','[咒骂]','[疑问]','[嘘]','[晕]', 46 | '[疯了]','[衰]','[骷髅]','[敲打]','[再见]','[擦汗]','[抠鼻]', 47 | '[鼓掌]','[糗大了]','[坏笑]','[左哼哼]','[右哼哼]','[哈欠]','[鄙视]', 48 | '[委屈]','[快哭了]','[阴险]','[亲亲]','[吓]','[可怜]','[菜刀]', 49 | '[西瓜]','[啤酒]','[篮球]','[乒乓]','[咖啡]','[饭]','[猪头]', 50 | '[玫瑰]','[凋谢]','[嘴唇]','[爱心]','[心碎]','[蛋糕]','[闪电]', 51 | '[炸弹]','[刀]','[足球]','[瓢虫]','[便便]','[月亮]','[太阳]', 52 | '[礼物]','[拥抱]','[强]','[弱]','[握手]','[胜利]','[抱拳]', 53 | '[勾引]','[拳头]','[差劲]','[爱你]','[NO]','[OK]', 54 | ]; 55 | 56 | final String _emojiFilePath = "emoji"; 57 | 58 | static EmojiUitl _instance; 59 | static EmojiUitl get instance { 60 | if (_instance == null) _instance = new EmojiUitl._(); 61 | return _instance; 62 | } 63 | 64 | EmojiUitl._() { 65 | for (var emojiName in emojiArray) { 66 | _emojiMap["$emojiName"] = "$_emojiFilePath/$emojiName.png"; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/tools/expanded_viewport.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Jpeng 3 | * Email: peng8350@gmail.com 4 | * Time: 2019-07-11 12:23 5 | */ 6 | import 'package:flutter/cupertino.dart'; 7 | import 'package:flutter/rendering.dart'; 8 | import 'package:flutter/widgets.dart'; 9 | import 'dart:math' as math; 10 | 11 | /* 12 | aim to implements expand all the free empty place when viewport is not full 13 | ,but this can not correction offset,due to _minScrollExtent,_maxScrollExtent private in RenderViewport 14 | ,no idea how to do. without doing this,chat list (top when not full && reverse = true) can not be done. 15 | in my plugin similar issue:#127,# 118 16 | in flutter similar issue:#12650,#33399,#17444 17 | */ 18 | 19 | class ExpandedViewport extends Viewport { 20 | ExpandedViewport({ 21 | Key key, 22 | AxisDirection axisDirection = AxisDirection.down, 23 | AxisDirection crossAxisDirection, 24 | double anchor = 0.0, 25 | ScrollPosition offset, 26 | Key center, 27 | double cacheExtent, 28 | List slivers = const [], 29 | }) : super( 30 | key: key, 31 | slivers: slivers, 32 | axisDirection: axisDirection, 33 | crossAxisDirection: crossAxisDirection, 34 | anchor: anchor, 35 | offset: offset, 36 | center: center, 37 | cacheExtent: cacheExtent); 38 | 39 | @override 40 | RenderViewport createRenderObject(BuildContext context) { 41 | // TODO: implement createRenderObject 42 | return _RenderExpandedViewport( 43 | axisDirection: axisDirection, 44 | crossAxisDirection: crossAxisDirection ?? 45 | Viewport.getDefaultCrossAxisDirection(context, axisDirection), 46 | anchor: anchor, 47 | offset: offset, 48 | cacheExtent: cacheExtent, 49 | ); 50 | } 51 | } 52 | 53 | class _RenderExpandedViewport extends RenderViewport { 54 | _RenderExpandedViewport({ 55 | AxisDirection axisDirection = AxisDirection.down, 56 | @required AxisDirection crossAxisDirection, 57 | @required ViewportOffset offset, 58 | double anchor = 0.0, 59 | List children, 60 | RenderSliver center, 61 | double cacheExtent, 62 | }) : super( 63 | axisDirection: axisDirection, 64 | crossAxisDirection: crossAxisDirection, 65 | offset: offset, 66 | anchor: anchor, 67 | children: children, 68 | center: center, 69 | cacheExtent: cacheExtent); 70 | 71 | @override 72 | void performLayout() { 73 | // TODO: implement performLayout 74 | super.performLayout(); 75 | RenderSliver expand; 76 | RenderSliver p = firstChild; 77 | double totalLayoutExtent = 0; 78 | double BehindExtent = 0.0, FrontExtent = 0.0; 79 | while (p != null) { 80 | totalLayoutExtent += p.geometry.scrollExtent; 81 | if (p is _RenderExpanded) { 82 | expand = p; 83 | FrontExtent = totalLayoutExtent; 84 | } 85 | 86 | p = childAfter(p); 87 | } 88 | double count = 0; 89 | BehindExtent = totalLayoutExtent - FrontExtent; 90 | if (expand != null && size.height > totalLayoutExtent) { 91 | _attemptLayout(expand, size.height, size.width, 92 | offset.pixels - FrontExtent - (size.height - totalLayoutExtent)); 93 | } 94 | } 95 | 96 | // _minScrollExtent private in super,no setter method 97 | double _attemptLayout(RenderSliver expandPosition, double mainAxisExtent, 98 | double crossAxisExtent, double correctedOffset) { 99 | assert(!mainAxisExtent.isNaN); 100 | assert(mainAxisExtent >= 0.0); 101 | assert(crossAxisExtent.isFinite); 102 | assert(crossAxisExtent >= 0.0); 103 | assert(correctedOffset.isFinite); 104 | 105 | // centerOffset is the offset from the leading edge of the RenderViewport 106 | // to the zero scroll offset (the line between the forward slivers and the 107 | // reverse slivers). 108 | final double centerOffset = mainAxisExtent * anchor - correctedOffset; 109 | final double reverseDirectionRemainingPaintExtent = 110 | centerOffset.clamp(0.0, mainAxisExtent); 111 | 112 | final double forwardDirectionRemainingPaintExtent = 113 | (mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent); 114 | final double fullCacheExtent = mainAxisExtent + 2 * cacheExtent; 115 | final double centerCacheOffset = centerOffset + cacheExtent; 116 | final double reverseDirectionRemainingCacheExtent = 117 | centerCacheOffset.clamp(0.0, fullCacheExtent); 118 | final double forwardDirectionRemainingCacheExtent = 119 | (fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent); 120 | 121 | final RenderSliver leadingNegativeChild = childBefore(center); 122 | // positive scroll offsets 123 | return layoutChildSequence( 124 | child: expandPosition, 125 | scrollOffset: math.max(0.0, -centerOffset), 126 | overlap: 127 | leadingNegativeChild == null ? math.min(0.0, -centerOffset) : 0.0, 128 | layoutOffset: centerOffset >= mainAxisExtent 129 | ? centerOffset 130 | : reverseDirectionRemainingPaintExtent, 131 | remainingPaintExtent: forwardDirectionRemainingPaintExtent, 132 | mainAxisExtent: mainAxisExtent, 133 | crossAxisExtent: crossAxisExtent, 134 | growthDirection: GrowthDirection.forward, 135 | advance: childAfter, 136 | remainingCacheExtent: forwardDirectionRemainingCacheExtent, 137 | cacheOrigin: centerOffset.clamp(-cacheExtent, 0.0), 138 | ); 139 | } 140 | } 141 | 142 | //tag 143 | class SliverExpanded extends SingleChildRenderObjectWidget { 144 | SliverExpanded() : super(child: Container()); 145 | 146 | @override 147 | RenderSliver createRenderObject(BuildContext context) { 148 | // TODO: implement createRenderObject 149 | return _RenderExpanded(); 150 | } 151 | } 152 | 153 | class _RenderExpanded extends RenderSliver 154 | with RenderObjectWithChildMixin { 155 | @override 156 | void performLayout() { 157 | // TODO: implement performLayout 158 | geometry = SliverGeometry.zero; 159 | } 160 | } -------------------------------------------------------------------------------- /lib/tools/sp_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | import 'package:synchronized/synchronized.dart'; 6 | 7 | 8 | /// SharedPreferences Util. 9 | class SpUtil { 10 | static SpUtil _singleton; 11 | static SharedPreferences _prefs; 12 | static Lock _lock = Lock(); 13 | 14 | static Future getInstance() async { 15 | if (_singleton == null) { 16 | await _lock.synchronized(() async { 17 | if (_singleton == null) { 18 | // keep local instance till it is fully initialized. 19 | // 保持本地实例直到完全初始化。 20 | var singleton = SpUtil._(); 21 | await singleton._init(); 22 | _singleton = singleton; 23 | } 24 | }); 25 | } 26 | return _singleton; 27 | } 28 | 29 | SpUtil._(); 30 | 31 | Future _init() async { 32 | _prefs = await SharedPreferences.getInstance(); 33 | } 34 | 35 | /// put object. 36 | static Future putObject(String key, Object value) { 37 | if (_prefs == null) return null; 38 | return _prefs.setString(key, value == null ? "" : json.encode(value)); 39 | } 40 | 41 | /// get obj. 42 | static T getObj(String key, T f(Map v), {T defValue}) { 43 | Map map = getObject(key); 44 | return map == null ? defValue : f(map); 45 | } 46 | 47 | /// get object. 48 | static Map getObject(String key) { 49 | if (_prefs == null) return null; 50 | String _data = _prefs.getString(key); 51 | return (_data == null || _data.isEmpty) ? null : json.decode(_data); 52 | } 53 | 54 | /// put object list. 55 | static Future putObjectList(String key, List list) { 56 | if (_prefs == null) return null; 57 | List _dataList = list?.map((value) { 58 | return json.encode(value); 59 | })?.toList(); 60 | return _prefs.setStringList(key, _dataList); 61 | } 62 | 63 | /// get obj list. 64 | static List getObjList(String key, T f(Map v), 65 | {List defValue = const []}) { 66 | List dataList = getObjectList(key); 67 | List list = dataList?.map((value) { 68 | return f(value); 69 | })?.toList(); 70 | return list ?? defValue; 71 | } 72 | 73 | /// get object list. 74 | static List getObjectList(String key) { 75 | if (_prefs == null) return null; 76 | List dataLis = _prefs.getStringList(key); 77 | return dataLis?.map((value) { 78 | Map _dataMap = json.decode(value); 79 | return _dataMap; 80 | })?.toList(); 81 | } 82 | 83 | /// get string. 84 | static String getString(String key, {String defValue = ''}) { 85 | if (_prefs == null) return defValue; 86 | return _prefs.getString(key) ?? defValue; 87 | } 88 | 89 | /// put string. 90 | static Future putString(String key, String value) { 91 | if (_prefs == null) return null; 92 | return _prefs.setString(key, value); 93 | } 94 | 95 | /// get bool. 96 | static bool getBool(String key, {bool defValue = false}) { 97 | if (_prefs == null) return defValue; 98 | return _prefs.getBool(key) ?? defValue; 99 | } 100 | 101 | /// put bool. 102 | static Future putBool(String key, bool value) { 103 | if (_prefs == null) return null; 104 | return _prefs.setBool(key, value); 105 | } 106 | 107 | /// get int. 108 | static int getInt(String key, {int defValue = 0}) { 109 | if (_prefs == null) return defValue; 110 | return _prefs.getInt(key) ?? defValue; 111 | } 112 | 113 | /// put int. 114 | static Future putInt(String key, int value) { 115 | if (_prefs == null) return null; 116 | return _prefs.setInt(key, value); 117 | } 118 | 119 | /// get double. 120 | static double getDouble(String key, {double defValue = 0.0}) { 121 | if (_prefs == null) return defValue; 122 | return _prefs.getDouble(key) ?? defValue; 123 | } 124 | 125 | /// put double. 126 | static Future putDouble(String key, double value) { 127 | if (_prefs == null) return null; 128 | return _prefs.setDouble(key, value); 129 | } 130 | 131 | /// get string list. 132 | static List getStringList(String key, 133 | {List defValue = const []}) { 134 | if (_prefs == null) return defValue; 135 | return _prefs.getStringList(key) ?? defValue; 136 | } 137 | 138 | /// put string list. 139 | static Future putStringList(String key, List value) { 140 | if (_prefs == null) return null; 141 | return _prefs.setStringList(key, value); 142 | } 143 | 144 | /// get dynamic. 145 | static dynamic getDynamic(String key, {Object defValue}) { 146 | if (_prefs == null) return defValue; 147 | return _prefs.get(key) ?? defValue; 148 | } 149 | 150 | /// have key. 151 | static bool haveKey(String key) { 152 | if (_prefs == null) return null; 153 | return _prefs.getKeys().contains(key); 154 | } 155 | 156 | /// get keys. 157 | static Set getKeys() { 158 | if (_prefs == null) return null; 159 | return _prefs.getKeys(); 160 | } 161 | 162 | /// remove. 163 | static Future remove(String key) { 164 | if (_prefs == null) return null; 165 | return _prefs.remove(key); 166 | } 167 | 168 | /// clear. 169 | static Future clear() { 170 | if (_prefs == null) return null; 171 | return _prefs.clear(); 172 | } 173 | 174 | ///Sp is initialized. 175 | static bool isInitialized() { 176 | return _prefs != null; 177 | } 178 | } -------------------------------------------------------------------------------- /lib/tools/special_text_span_builder.dart: -------------------------------------------------------------------------------- 1 | import 'emoji_text.dart'; 2 | import 'package:extended_text_library/extended_text_library.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class PPSpecialTextSpanBuilder extends SpecialTextSpanBuilder { 6 | final BuilderType type; 7 | PPSpecialTextSpanBuilder({this.type: BuilderType.extendedText}); 8 | 9 | @override 10 | SpecialText createSpecialText(String flag, 11 | {TextStyle textStyle, SpecialTextGestureTapCallback onTap, int index}) { 12 | if (flag == null || flag == "") return null; 13 | // TODO: implement createSpecialText 14 | 15 | ///index is end index of start flag, so text start index should be index-(flag.length-1) 16 | if (isStart(flag, EmojiText.flag)) { 17 | return EmojiText(textStyle, start: index - (EmojiText.flag.length - 1)); 18 | } 19 | return null; 20 | } 21 | } 22 | 23 | enum BuilderType { extendedText, extendedTextField } 24 | -------------------------------------------------------------------------------- /lib/views/chat/group_chat_scene.dart: -------------------------------------------------------------------------------- 1 | import 'package:extended_text_field/extended_text_field.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 5 | 6 | import 'package:provider/provider.dart'; 7 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 8 | import 'package:wing_chat/provider/group_chat_provider.dart'; 9 | import 'package:wing_chat/tools/expanded_viewport.dart'; 10 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 11 | 12 | class GroupChatScene extends StatelessWidget { 13 | final JMGroupInfo groupInfo; 14 | 15 | const GroupChatScene({Key key, this.groupInfo}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return MultiProvider( 20 | providers: [ 21 | ChangeNotifierProvider( 22 | create: (context) => GroupChatProvider() 23 | ..context = context 24 | ..kGroupInfo = groupInfo 25 | ..initConversation()) 26 | ], 27 | child: Consumer(builder: (_, provider, widget) { 28 | return Scaffold( 29 | appBar: AppBar(title: Text('${groupInfo.name}')), 30 | backgroundColor: Colors.white, 31 | body: GestureDetector( 32 | behavior: HitTestBehavior.translucent, 33 | onTap: () {// 触摸收起键盘 34 | FocusScope.of(context).requestFocus(FocusNode()); 35 | provider.emojiOpen = false; 36 | }, 37 | child: SafeArea( 38 | child: GroupChatList(), 39 | ), 40 | ) 41 | ); 42 | })); 43 | } 44 | } 45 | 46 | class GroupChatList extends StatelessWidget { 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | 51 | return Consumer(builder: (_, provider, widget) { 52 | 53 | provider.keyboardHeight = MediaQuery.of(context).viewInsets.bottom; 54 | return Container( 55 | color: ThemeColor, 56 | child: Column( 57 | children: [ 58 | Expanded( 59 | child: SmartRefresher( 60 | controller: provider.controller, 61 | enablePullDown: false, 62 | enablePullUp: true, 63 | onLoading: () async { 64 | provider.getHistoryMessages(); 65 | }, 66 | footer: CustomFooter( 67 | loadStyle: LoadStyle.HideAlways, 68 | builder: (context, mode) { 69 | if (mode == LoadStatus.loading) { 70 | return Container( 71 | height: 60.0, 72 | child: Container( 73 | height: 20.0, 74 | width: 20.0, 75 | child: CupertinoActivityIndicator(), 76 | ), 77 | ); 78 | } else 79 | return Container(); 80 | }), 81 | child: Scrollable( 82 | controller: provider.scrollController, 83 | axisDirection: AxisDirection.up, 84 | viewportBuilder: (context, offset) { 85 | return ExpandedViewport( 86 | offset: offset, 87 | axisDirection: AxisDirection.up, 88 | slivers: [ 89 | SliverExpanded(), 90 | SliverList( 91 | delegate: SliverChildBuilderDelegate( 92 | (c, i) => provider.chatWidgetChildren[i], 93 | childCount: provider.chatWidgetChildren.length), 94 | ) 95 | ], 96 | ); 97 | }, 98 | ), 99 | ), 100 | ), 101 | GroupChatInput() 102 | ], 103 | ), 104 | ); 105 | }); 106 | } 107 | } 108 | 109 | class GroupChatInput extends StatelessWidget { 110 | Widget build(BuildContext context) { 111 | return Consumer(builder: (_, provider, widget) { 112 | 113 | return Container( 114 | color: Colors.white, 115 | alignment: Alignment.bottomCenter, 116 | child: Column( 117 | children: [ 118 | Offstage( 119 | offstage: provider.talkFOT, 120 | child: Row( 121 | children: [ 122 | Container( 123 | width: 20.0, 124 | ), 125 | Expanded( 126 | child: Container( 127 | alignment: Alignment.center, 128 | decoration: BoxDecoration( 129 | color: ThemeColor, 130 | borderRadius: BorderRadius.circular(25)), 131 | child: ExtendedTextField( 132 | specialTextSpanBuilder: 133 | provider.mySpecialTextSpanBuilder, 134 | controller: provider.textInputController, 135 | focusNode: provider.fsNode, 136 | // autofocus: true, 137 | maxLines: null, 138 | textInputAction: TextInputAction.send, 139 | decoration: InputDecoration( 140 | hintText: '请输入内容...', 141 | contentPadding: EdgeInsets.symmetric( 142 | horizontal: 10, vertical: 8), 143 | border: InputBorder.none, 144 | hintStyle: 145 | TextStyle(color: Colors.grey[400]), 146 | ), 147 | onChanged: (value) { 148 | provider.reloadInputView(); 149 | }, 150 | onSubmitted: (value) { 151 | provider.sendTextMessage(value); 152 | }, 153 | onTap: () { 154 | provider.emojiOpen = false; 155 | }, 156 | ), 157 | ), 158 | ), 159 | IconButton( 160 | icon: Image.asset( 161 | 'images/chat_emoji.png', 162 | height: 24, 163 | width: 24, 164 | ), 165 | onPressed: () { 166 | provider.emojiOpen = !provider.emojiOpen; 167 | }, 168 | ), 169 | IconButton( 170 | icon: 171 | provider.textInputController.text.trim().length > 0 || 172 | provider.emojiOpen 173 | ? Text('发送') 174 | : Image.asset( 175 | 'images/chat_funcs.png', 176 | height: 24, 177 | width: 24, 178 | ), 179 | onPressed: () { 180 | if (provider.textInputController.text.trim().length > 181 | 0 || 182 | provider.emojiOpen) { 183 | provider.sendTextMessage( 184 | provider.textInputController.text); 185 | // provider.emojiOpen = false; 186 | } else { 187 | provider.sendImageMessage(); 188 | } 189 | }, 190 | ) 191 | ], 192 | ), 193 | ), 194 | Offstage( 195 | // 表情选择 196 | offstage: !provider.emojiOpen, 197 | child: Container( 198 | height: provider.keyboardHeight, 199 | padding: 200 | EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 0), 201 | child: GridView.count( 202 | shrinkWrap: true, 203 | crossAxisCount: 7, 204 | children: provider.emojiArray.map((emojiName) { 205 | return IconButton( 206 | icon: Image.asset('emoji/$emojiName.png'), 207 | onPressed: () { 208 | provider.textInputController.text += emojiName; 209 | }, 210 | ); 211 | }).toList(), 212 | mainAxisSpacing: 1, 213 | crossAxisSpacing: 1, 214 | childAspectRatio: 1.0, 215 | ), 216 | )) 217 | ], 218 | )); 219 | }); 220 | } 221 | } 222 | 223 | -------------------------------------------------------------------------------- /lib/views/chat/single_chat_scene.dart: -------------------------------------------------------------------------------- 1 | import 'package:extended_text_field/extended_text_field.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 5 | 6 | import 'package:provider/provider.dart'; 7 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 8 | import 'package:wing_chat/provider/single_chat_provider.dart'; 9 | import 'package:wing_chat/tools/expanded_viewport.dart'; 10 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 11 | 12 | class SingleChatScene extends StatelessWidget { 13 | final JMUserInfo userInfo; 14 | 15 | const SingleChatScene({Key key, this.userInfo}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return MultiProvider( 20 | providers: [ 21 | ChangeNotifierProvider( 22 | create: (context) => SingleChatProvider() 23 | ..context = context 24 | ..jmUserInfo = userInfo 25 | ..initConversation()) 26 | ], 27 | child: Consumer(builder: (_, provider, widget) { 28 | return Scaffold( 29 | appBar: AppBar(title: Text('${userInfo.username}')), 30 | backgroundColor: Colors.white, 31 | body: GestureDetector( 32 | behavior: HitTestBehavior.translucent, 33 | onTap: () {// 触摸收起键盘 34 | FocusScope.of(context).requestFocus(FocusNode()); 35 | provider.emojiOpen = false; 36 | }, 37 | child: SafeArea( 38 | child: GroupChatList(), 39 | ), 40 | ) 41 | ); 42 | })); 43 | } 44 | } 45 | 46 | class GroupChatList extends StatelessWidget { 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | 51 | return Consumer(builder: (_, provider, widget) { 52 | 53 | provider.keyboardHeight = MediaQuery.of(context).viewInsets.bottom; 54 | return Container( 55 | color: ThemeColor, 56 | child: Column( 57 | children: [ 58 | Expanded( 59 | child: SmartRefresher( 60 | controller: provider.controller, 61 | enablePullDown: false, 62 | enablePullUp: true, 63 | onLoading: () async { 64 | provider.getHistoryMessages(); 65 | }, 66 | footer: CustomFooter( 67 | loadStyle: LoadStyle.HideAlways, 68 | builder: (context, mode) { 69 | if (mode == LoadStatus.loading) { 70 | return Container( 71 | height: 60.0, 72 | child: Container( 73 | height: 20.0, 74 | width: 20.0, 75 | child: CupertinoActivityIndicator(), 76 | ), 77 | ); 78 | } else 79 | return Container(); 80 | }), 81 | child: Scrollable( 82 | controller: provider.scrollController, 83 | axisDirection: AxisDirection.up, 84 | viewportBuilder: (context, offset) { 85 | return ExpandedViewport( 86 | offset: offset, 87 | axisDirection: AxisDirection.up, 88 | slivers: [ 89 | SliverExpanded(), 90 | SliverList( 91 | delegate: SliverChildBuilderDelegate( 92 | (c, i) => provider.chatWidgetChildren[i], 93 | childCount: provider.chatWidgetChildren.length), 94 | ) 95 | ], 96 | ); 97 | }, 98 | ), 99 | ), 100 | ), 101 | GroupChatInput() 102 | ], 103 | ), 104 | ); 105 | }); 106 | } 107 | } 108 | 109 | class GroupChatInput extends StatelessWidget { 110 | Widget build(BuildContext context) { 111 | return Consumer(builder: (_, provider, widget) { 112 | 113 | return Container( 114 | color: Colors.white, 115 | alignment: Alignment.bottomCenter, 116 | child: Column( 117 | children: [ 118 | Offstage( 119 | offstage: provider.talkFOT, 120 | child: Row( 121 | children: [ 122 | Container( 123 | width: 20.0, 124 | ), 125 | Expanded( 126 | child: Container( 127 | alignment: Alignment.center, 128 | decoration: BoxDecoration( 129 | color: ThemeColor, 130 | borderRadius: BorderRadius.circular(25)), 131 | child: ExtendedTextField( 132 | specialTextSpanBuilder: 133 | provider.mySpecialTextSpanBuilder, 134 | controller: provider.textInputController, 135 | focusNode: provider.fsNode, 136 | // autofocus: true, 137 | maxLines: null, 138 | textInputAction: TextInputAction.send, 139 | decoration: InputDecoration( 140 | hintText: '请输入内容...', 141 | contentPadding: EdgeInsets.symmetric( 142 | horizontal: 10, vertical: 8), 143 | border: InputBorder.none, 144 | hintStyle: 145 | TextStyle(color: Colors.grey[400]), 146 | ), 147 | onChanged: (value) { 148 | provider.reloadInputView(); 149 | }, 150 | onSubmitted: (value) { 151 | provider.sendTextMessage(value); 152 | }, 153 | onTap: () { 154 | provider.emojiOpen = false; 155 | }, 156 | ), 157 | ), 158 | ), 159 | IconButton( 160 | icon: Image.asset( 161 | 'images/chat_emoji.png', 162 | height: 24, 163 | width: 24, 164 | ), 165 | onPressed: () { 166 | provider.emojiOpen = !provider.emojiOpen; 167 | }, 168 | ), 169 | IconButton( 170 | icon: 171 | provider.textInputController.text.trim().length > 0 || 172 | provider.emojiOpen 173 | ? Text('发送') 174 | : Image.asset( 175 | 'images/chat_funcs.png', 176 | height: 24, 177 | width: 24, 178 | ), 179 | onPressed: () { 180 | if (provider.textInputController.text.trim().length > 181 | 0 || 182 | provider.emojiOpen) { 183 | provider.sendTextMessage( 184 | provider.textInputController.text); 185 | // provider.emojiOpen = false; 186 | } else { 187 | provider.sendImageMessage(); 188 | } 189 | }, 190 | ) 191 | ], 192 | ), 193 | ), 194 | Offstage( 195 | // 表情选择 196 | offstage: !provider.emojiOpen, 197 | child: Container( 198 | height: provider.keyboardHeight, 199 | padding: 200 | EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 0), 201 | child: GridView.count( 202 | shrinkWrap: true, 203 | crossAxisCount: 7, 204 | children: provider.emojiArray.map((emojiName) { 205 | return IconButton( 206 | icon: Image.asset('emoji/$emojiName.png'), 207 | onPressed: () { 208 | provider.textInputController.text += emojiName; 209 | }, 210 | ); 211 | }).toList(), 212 | mainAxisSpacing: 1, 213 | crossAxisSpacing: 1, 214 | childAspectRatio: 1.0, 215 | ), 216 | )) 217 | ], 218 | )); 219 | }); 220 | } 221 | } 222 | 223 | -------------------------------------------------------------------------------- /lib/views/discover/discover_cell.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'discover_child_page.dart'; 3 | 4 | 5 | class DiscoverCell extends StatefulWidget { 6 | @required 7 | final String title; 8 | @required 9 | final String imageName; 10 | final String subTitle; 11 | final String subImageName; 12 | 13 | const DiscoverCell({ 14 | Key key, 15 | this.title, 16 | this.subTitle, 17 | this.imageName, 18 | this.subImageName 19 | }):assert(title != null, 'title不能为空'), 20 | assert(imageName != null, 'imageName不能为空'); 21 | @override 22 | _DiscoverCellState createState() => _DiscoverCellState(); 23 | } 24 | 25 | class _DiscoverCellState extends State { 26 | @override 27 | Widget build(BuildContext context){ 28 | return GestureDetector( 29 | onTap: (){ 30 | Navigator.of(context).push(MaterialPageRoute( 31 | builder: (BuildContext context) => DiscoverChildPage( 32 | title: widget.title, 33 | ))); 34 | }, 35 | onTapCancel: (){ 36 | print("取消了点击"); 37 | }, 38 | child:Container( 39 | color: Colors.white, 40 | height: 54, 41 | width: 400, 42 | padding: EdgeInsets.all(10), 43 | child: Row( 44 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 45 | children: [ 46 | Container( 47 | child: Row( 48 | mainAxisAlignment: MainAxisAlignment.start, 49 | children: [ 50 | Image( 51 | image: AssetImage(widget.imageName), 52 | width: 26, 53 | ), 54 | SizedBox(width: 15,), 55 | Text(widget.title,style: TextStyle(fontSize: 16.0),) 56 | ], 57 | ), 58 | ), 59 | Container( 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.end, 62 | children: [ 63 | Text( 64 | widget.subTitle!=null?widget.subTitle:'', 65 | style: TextStyle(color: Colors.grey,fontSize: 16.0), 66 | ), 67 | SizedBox(width: 10,), 68 | //红点 69 | widget.subImageName != null ? Container( 70 | child: Image(image: AssetImage(widget.subImageName)), 71 | width: 15, 72 | margin: EdgeInsets.only(right: 10), 73 | ) :Container(), 74 | Image(image:AssetImage("images/icon_right.png"),width: 15) 75 | ], 76 | ), 77 | ), 78 | ], 79 | ), 80 | ) 81 | ); 82 | } 83 | } -------------------------------------------------------------------------------- /lib/views/discover/discover_child_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 3 | class DiscoverChildPage extends StatefulWidget { 4 | final String title; 5 | const DiscoverChildPage({ 6 | Key key, 7 | this.title 8 | }):super(key:key); 9 | @override 10 | _DiscoverChildPageState createState() => _DiscoverChildPageState(); 11 | } 12 | 13 | class _DiscoverChildPageState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | appBar: AppBar( 18 | backgroundColor: ThemeColor, 19 | title: Text(widget.title), 20 | ), 21 | body: Center( 22 | child: Text(widget.title), 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/views/discover/discover_scene.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'discover_cell.dart'; 4 | 5 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 6 | 7 | 8 | class DiscoverScene extends StatelessWidget { 9 | final Color _themColor = ThemeColor; 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | appBar: AppBar( 14 | brightness: Brightness.light, 15 | backgroundColor: _themColor, 16 | title: Text("发现",style: TextStyle(color: Colors.black),), 17 | centerTitle: true, 18 | ), 19 | body: Container( 20 | color: _themColor, 21 | height: 800, 22 | child: ListView( 23 | children: [ 24 | DiscoverCell( 25 | imageName: 'images/朋友圈.png', 26 | title: '朋友圈', 27 | subTitle: '新消息', 28 | ), 29 | Row( 30 | children: [Container(height: 10)], 31 | ), 32 | DiscoverCell( 33 | imageName: 'images/扫一扫.png', 34 | title: '扫一扫', 35 | ), 36 | Row( 37 | children: [ 38 | Container(width: 50,height: 0.5,color: Colors.white,), 39 | Container(height: 0.5,color: ThemeColor,) 40 | ], 41 | ), 42 | DiscoverCell( 43 | imageName: 'images/摇一摇.png', 44 | title: '摇一摇', 45 | ), 46 | Container(height: 10,), 47 | DiscoverCell( 48 | imageName: 'images/看一看icon.png', 49 | title: '看一看', 50 | ), 51 | Row( 52 | children: [ 53 | Container(width: 50,height: 0.5,color: Colors.white,), 54 | Container(height: 0.5,color: ThemeColor,) 55 | ], 56 | ), 57 | DiscoverCell( 58 | imageName: 'images/搜一搜 2.png', 59 | title: '搜一搜', 60 | ), 61 | SizedBox(height: 10,), 62 | DiscoverCell( 63 | imageName: 'images/附近的人icon.png', 64 | title: '附近的人', 65 | ), 66 | SizedBox(height: 10,), 67 | DiscoverCell( 68 | imageName: 'images/购物.png', 69 | title: '购物', 70 | subTitle: '618限时特价', 71 | subImageName: "images/badge.png", 72 | ), 73 | Row( 74 | children: [ 75 | Container(width: 50,height: 0.5,color: Colors.white,), 76 | Container(height: 0.5,color: ThemeColor,) 77 | ], 78 | ), 79 | DiscoverCell( 80 | imageName: 'images/游戏.png', 81 | title: '游戏', 82 | ), 83 | SizedBox(height: 10,), 84 | DiscoverCell( 85 | imageName: 'images/小程序.png', 86 | title: '小程序', 87 | ), 88 | ], 89 | ), 90 | ) 91 | ); 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/views/friends/friends_data.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Friends{ 4 | final String imageUrl; 5 | final String name; 6 | final String indexLetter; 7 | final String imageAsset; 8 | final String message; 9 | final String time; 10 | Friends({ 11 | this.imageUrl, 12 | this.name, 13 | this.indexLetter, 14 | this.imageAsset, 15 | this.message, 16 | this.time 17 | }); 18 | } 19 | 20 | Listdatas = [ 21 | Friends( 22 | imageUrl: 'https://randomuser.me/api/portraits/women/57.jpg', 23 | name: 'Lina', 24 | indexLetter: 'L', 25 | message: 'hello hank !😁', 26 | time: '下午 3:45', 27 | ), 28 | Friends( 29 | imageUrl: 'https://randomuser.me/api/portraits/women/70.jpg', 30 | name: '菲儿', 31 | indexLetter: 'F', 32 | message: '忙完了吗?', 33 | time: '下午 3:25', 34 | ), 35 | Friends( 36 | imageUrl: 'https://randomuser.me/api/portraits/women/60.jpg', 37 | name: '安莉', 38 | indexLetter: 'A', 39 | message: '我在看看,稍等。', 40 | time: '下午 2:45', 41 | ), 42 | Friends( 43 | imageUrl: 'https://randomuser.me/api/portraits/men/91.jpg', 44 | name: '阿贵', 45 | indexLetter: 'A', 46 | message: '我没弄明白...', 47 | time: '昨天', 48 | ), 49 | Friends( 50 | imageUrl: 'https://randomuser.me/api/portraits/women/22.jpg', 51 | name: '贝拉', 52 | indexLetter: 'B', 53 | message: '这个时候刷圈?', 54 | time: '下午 3:45', 55 | ), 56 | Friends( 57 | imageUrl: 'https://randomuser.me/api/portraits/women/57.jpg', 58 | name: 'Lina', 59 | indexLetter: 'L', 60 | message: 'hello hank !😁', 61 | time: '下午 3:45', 62 | ), 63 | Friends( 64 | imageUrl: 'https://randomuser.me/api/portraits/women/18.jpg', 65 | name: 'Nancy', 66 | indexLetter: 'N', 67 | message: '麻烦通过一下😁', 68 | time: '下午 4:05', 69 | ), 70 | Friends( 71 | imageUrl: 'https://randomuser.me/api/portraits/men/47.jpg', 72 | name: '扣扣', 73 | indexLetter: 'K', 74 | message: '你好啊😁', 75 | time: '下午 2:45', 76 | ), 77 | Friends( 78 | imageUrl: 'https://randomuser.me/api/portraits/men/3.jpg', 79 | name: 'Jack', 80 | indexLetter: 'J', 81 | message: '好久不见 ', 82 | time: '下午 4:15', 83 | ), 84 | Friends( 85 | imageUrl: 'https://randomuser.me/api/portraits/women/5.jpg', 86 | name: 'Emma', 87 | indexLetter: 'E', 88 | message: '在忙什么呢 ', 89 | time: '下午 3:55', 90 | ), 91 | Friends( 92 | imageUrl: 'https://randomuser.me/api/portraits/women/44.jpg', 93 | name: 'Abby', 94 | indexLetter: 'A', 95 | message: ' 最近还好吗😁', 96 | time: '下午 1:45', 97 | ), 98 | Friends( 99 | imageUrl: 'https://randomuser.me/api/portraits/men/65.jpg', 100 | name: 'Betty', 101 | indexLetter: 'B', 102 | message: '什么时候有空呢😁', 103 | time: '上午 8:45', 104 | ), 105 | Friends( 106 | imageUrl: 'https://randomuser.me/api/portraits/men/83.jpg', 107 | name: 'Tony', 108 | indexLetter: 'T', 109 | message: '一起出去玩吧😁', 110 | time: '上午 7:15', 111 | ), 112 | Friends( 113 | imageUrl: 'https://randomuser.me/api/portraits/men/36.jpg', 114 | name: 'Jerry', 115 | indexLetter: 'J', 116 | message: 'How are you?😁', 117 | time: '上午 9:10', 118 | ), 119 | Friends( 120 | imageUrl: 'https://randomuser.me/api/portraits/men/66.jpg', 121 | name: 'Colin', 122 | indexLetter: 'C', 123 | message: '谢谢你', 124 | time: '下午 3:40', 125 | ), 126 | Friends( 127 | imageUrl: 'https://randomuser.me/api/portraits/women/58.jpg', 128 | name: 'Haha', 129 | indexLetter: 'H', 130 | message: '最近去健身了么😁', 131 | time: '下午 5:40', 132 | ), 133 | Friends( 134 | imageUrl: 'https://randomuser.me/api/portraits/women/60.jpg', 135 | name: 'Ketty', 136 | indexLetter: 'K', 137 | message: '你喜欢哪个明星呀😁', 138 | time: '下午 3:45', 139 | ), 140 | Friends( 141 | imageUrl: 'https://randomuser.me/api/portraits/women/57.jpg', 142 | name: 'Lina', 143 | indexLetter: 'L', 144 | message: 'hello hank !😁', 145 | time: '下午 3:45', 146 | ), 147 | Friends( 148 | imageUrl: 'https://randomuser.me/api/portraits/women/57.jpg', 149 | name: 'Lina', 150 | indexLetter: 'L', 151 | message: 'hello hank !😁', 152 | time: '下午 3:45', 153 | ), 154 | ]; -------------------------------------------------------------------------------- /lib/views/friends/friends_page_cell.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 4 | 5 | class FriendCell extends StatelessWidget { 6 | final String imageUrl; 7 | final String name; 8 | final String indexLetter; 9 | final String imageAsset; 10 | final String message; 11 | final String time; 12 | FriendCell({ 13 | this.imageUrl, 14 | this.name, 15 | this.indexLetter, 16 | this.imageAsset, 17 | this.message, 18 | this.time 19 | }); 20 | @override 21 | Widget build(BuildContext context) { 22 | return Container( 23 | child: Column( 24 | children: [ 25 | this.indexLetter!=null? Container( 26 | height: 30.0, 27 | padding: EdgeInsets.only(left: 10,top:5,bottom: 5), 28 | width: MediaQuery.of(context).size.width, 29 | color: ThemeColor, 30 | child: Text(indexLetter), 31 | ):Container(height: 0.0,), 32 | Row( 33 | children: [ 34 | Container( 35 | child: Row( 36 | children: [ 37 | Container( 38 | color: Colors.white, 39 | child: Container( 40 | height: 34, 41 | width: 34, 42 | margin: EdgeInsets.all(10), 43 | decoration: BoxDecoration( 44 | borderRadius: BorderRadius.circular(6.0), 45 | image: DecorationImage( 46 | image:imageUrl!=null?NetworkImage(imageUrl):AssetImage(imageAsset) 47 | ) 48 | ), 49 | ), 50 | ), 51 | Container( 52 | height: 53.5, 53 | width:MediaQuery.of(context).size.width-55, 54 | color: Colors.white, 55 | alignment: Alignment.centerLeft, 56 | child: Text(this.name,), 57 | ), 58 | ], 59 | ), 60 | ), 61 | ], 62 | ), 63 | ], 64 | ) 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/views/friends/friends_scene.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wing_chat/views/discover/discover_child_page.dart'; 3 | import 'friends_data.dart'; 4 | import 'friends_page_cell.dart'; 5 | import 'index_bar.dart'; 6 | 7 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 8 | 9 | class FriendsScene extends StatefulWidget { 10 | @override 11 | _FriendsSceneState createState() => _FriendsSceneState(); 12 | } 13 | 14 | class _FriendsSceneState extends State { 15 | ScrollController _scrollController; 16 | final List _listData = []; 17 | double _contextHeight = 0;//content高度 18 | GlobalKey _contextKey = new GlobalKey(); 19 | //定义位置 20 | final Map _groupOffsetMap = { 21 | INDEX_WORDS[0]:0.0, 22 | INDEX_WORDS[1]:0.0 23 | }; 24 | 25 | //不需要初始 26 | @override 27 | void initState() { 28 | // TODO: implement initState 29 | super.initState(); 30 | _listData..addAll(datas)..addAll(datas); 31 | //排序 32 | _listData.sort((Friends a,Friends b){ 33 | return a.indexLetter.compareTo(b.indexLetter); 34 | }); 35 | //创建一个scroll 36 | _scrollController = ScrollController(); 37 | 38 | //设置位置 39 | var _groupOffset = 54.0*4; 40 | for(int i=0;i<_listData.length;i++){ 41 | if(i<1){ 42 | _groupOffsetMap.addAll({_listData[i].indexLetter:_groupOffset}); 43 | _groupOffset += 84.0; 44 | }else if(_listData[i].indexLetter==_listData[i-1].indexLetter){//没有头 45 | _groupOffset += 54.0; 46 | }else{ 47 | _groupOffsetMap.addAll({_listData[i].indexLetter:_groupOffset}); 48 | _groupOffset += 84.0; 49 | } 50 | } 51 | _contextHeight = _groupOffset; 52 | _groupOffsetMap.addAll({'🔍':0.0}); 53 | } 54 | 55 | final List _headerData = [ 56 | Friends(imageAsset:'images/新的朋友.png', name: '新的朋友',), 57 | Friends(imageAsset:'images/群聊.png', name: '群聊',), 58 | Friends(imageAsset:'images/标签.png', name: '标签',), 59 | Friends(imageAsset:'images/公众号.png', name: '公众号',), 60 | ]; 61 | 62 | //cell 63 | Widget _itemForRow(BuildContext context,int index){ 64 | if(index<_headerData.length) { 65 | return FriendCell( 66 | name:_headerData[index].name, 67 | imageAsset:_headerData[index].imageAsset, 68 | ); 69 | }else{ 70 | //分组显示,如果当前标题和上级标题一样就不显示 71 | if(index>4&&_listData[index-4].indexLetter==_listData[index-5].indexLetter){ 72 | return FriendCell( 73 | name:_listData[index-_headerData.length].name, 74 | imageUrl:_listData[index-_headerData.length].imageUrl, 75 | ); 76 | } 77 | return FriendCell( 78 | name:_listData[index-_headerData.length].name, 79 | imageUrl:_listData[index-_headerData.length].imageUrl, 80 | indexLetter: _listData[index-_headerData.length].indexLetter, 81 | ); 82 | } 83 | } 84 | 85 | @override 86 | Widget build(BuildContext context) { 87 | return Scaffold( 88 | appBar: AppBar( 89 | backgroundColor: ThemeColor, 90 | title: Text("通讯录",), 91 | actions: [ 92 | GestureDetector( 93 | onTap: (){ 94 | Navigator.of(context).push( 95 | MaterialPageRoute(builder: (BuildContext content)=>DiscoverChildPage(title: "添加朋友",)) 96 | ); 97 | }, 98 | child: Container( 99 | margin: EdgeInsets.only(right: 10), 100 | child: Image(image: AssetImage('images/icon_friends_add.png'),width: 26,), 101 | ), 102 | ) 103 | ], 104 | ), 105 | body: Stack( 106 | key: _contextKey, 107 | alignment: Alignment.centerRight, 108 | children: [ 109 | Container( 110 | color: ThemeColor, 111 | child: ListView.builder( 112 | controller: _scrollController, 113 | itemCount:_headerData.length+_listData.length, 114 | itemBuilder: _itemForRow, 115 | ) 116 | ),//通讯录列表 117 | //悬浮控件 118 | IndexBar( 119 | indexBarCallBack: (str){ 120 | if(_groupOffsetMap[str]!=null){ 121 | print(_contextHeight); 122 | // print(ScreenHeight(context)); 123 | print(_scrollController.position.maxScrollExtent); 124 | print(_contextKey.currentContext.size.height); 125 | if(_contextHeight-_groupOffsetMap[str]<=_contextKey.currentContext.size.height){ 126 | _scrollController.jumpTo(_scrollController.position.maxScrollExtent); 127 | }else{ 128 | _scrollController.animateTo( 129 | _groupOffsetMap[str], 130 | duration: Duration(milliseconds: 10), 131 | curve: Curves.easeIn 132 | ); 133 | } 134 | } 135 | }, 136 | ) 137 | ], 138 | ) 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/views/friends/index_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | int getIndex(BuildContext context,Offset globalPosition){ 4 | //RenderBox对象渲染。触摸位置在容器0点位置也为0 5 | RenderBox box = context.findRenderObject(); 6 | double y = box.globalToLocal(globalPosition).dy; 7 | var itemHeight = MediaQuery.of(context).size.height/2/INDEX_WORDS.length; 8 | int index = y ~/ itemHeight;//取整 9 | if(index>=0&&index _IndexBarState(); 25 | } 26 | class _IndexBarState extends State { 27 | 28 | int _selectorIndex = 0; 29 | 30 | Color _backColor = Color.fromRGBO(1, 1, 1, 0.0); 31 | Color _textColor = Colors.black; 32 | 33 | double _indicatorY = 0.0; 34 | String _indicatorText = ''; 35 | bool _indicatorHidden = true;//是否显示 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | //创建索引 40 | Listwords = []; 41 | for(int i=0;i[ 51 | Container( 52 | alignment: Alignment(0, _indicatorY),//-1.1-1.1 53 | width: 100, 54 | child: _indicatorHidden?Container(): 55 | Stack( 56 | alignment: Alignment(-0.2, 0), 57 | children: [ 58 | Image(image: AssetImage("images/气泡.png"),width: 60,), 59 | Text(_indicatorText,style: TextStyle(fontSize: 35,color: Colors.white),) 60 | ], 61 | ), 62 | ), 63 | GestureDetector( 64 | //点击下去 65 | onVerticalDragDown: (DragDownDetails details){ 66 | print('onVerticalDragDown'); 67 | _backColor = Color.fromRGBO(1, 1, 1, 0.3); 68 | _textColor = Colors.white; 69 | setState(() { 70 | 71 | }); 72 | //设置滑动回调 73 | int index = getIndex(context, details.globalPosition); 74 | if(index!=_selectorIndex) { 75 | _selectorIndex = index; 76 | widget.indexBarCallBack( 77 | INDEX_WORDS[getIndex(context, details.globalPosition)]); 78 | } 79 | //气泡回调 80 | _indicatorText = INDEX_WORDS[index]; 81 | _indicatorY = 2.2/(INDEX_WORDS.length-1)*index-1.1; 82 | _indicatorHidden = false; 83 | 84 | setState(() { 85 | 86 | }); 87 | }, 88 | //监听手势拖拽 89 | onVerticalDragUpdate: (DragUpdateDetails details){ 90 | print('onVerticalDragUpdate'); 91 | 92 | //设置滑动回调 93 | int index = getIndex(context, details.globalPosition); 94 | if(index!=_selectorIndex) { 95 | _selectorIndex = index; 96 | widget.indexBarCallBack( 97 | INDEX_WORDS[getIndex(context, details.globalPosition)]); 98 | } 99 | 100 | //气泡回调 101 | _indicatorText = INDEX_WORDS[index]; 102 | _indicatorY = 2.2/(INDEX_WORDS.length-1)*index-1.1; 103 | _indicatorHidden = false; 104 | 105 | setState(() {}); 106 | }, 107 | //松开 108 | onVerticalDragEnd: (DragEndDetails details){ 109 | _backColor = Color.fromRGBO(1, 1, 1, 0.0); 110 | _textColor = Colors.black; 111 | 112 | _indicatorHidden = true;//隐藏 113 | setState(() {}); 114 | }, 115 | child: Container( 116 | color: _backColor, 117 | child: Column( 118 | mainAxisAlignment: MainAxisAlignment.spaceAround, 119 | children: words, 120 | ), 121 | 122 | ), 123 | ) 124 | ], 125 | ) 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/views/login_scene.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:provider/provider.dart'; 5 | import 'package:wing_chat/provider/jmessage_manager_provider.dart'; 6 | import 'package:wing_chat/tools/app_toast.dart'; 7 | import 'package:wing_chat/tools/sp_util.dart'; 8 | import 'package:wing_chat/views/root_scene.dart'; 9 | 10 | class LoginPage extends StatelessWidget { 11 | // LoginPage({Key key}) : super(key: key); 12 | TextEditingController _userController = TextEditingController(); 13 | TextEditingController _pwdController = TextEditingController(); 14 | @override 15 | Widget build(BuildContext context) { 16 | 17 | return Scaffold( 18 | appBar: AppBar( 19 | elevation: 0, 20 | ), 21 | backgroundColor: Colors.white, 22 | body: Theme( 23 | data: ThemeData(primaryColor: Colors.lightBlue), 24 | child: Container( 25 | padding: EdgeInsets.symmetric(horizontal: 30), 26 | child: Column( 27 | crossAxisAlignment: CrossAxisAlignment.start, 28 | mainAxisSize: MainAxisSize.min, 29 | children: [ 30 | Container( 31 | margin: EdgeInsets.only(top: 30), 32 | child: Text( 33 | 'Welcome Back!', 34 | style: TextStyle( 35 | fontWeight: FontWeight.bold, 36 | color: Colors.black87, 37 | fontSize: 34, 38 | ), 39 | ), 40 | ), 41 | Container( 42 | margin: EdgeInsets.only(top: 5), 43 | child: Text( 44 | '星光呓语 App', 45 | style: TextStyle( 46 | color: Colors.grey, 47 | fontSize: 14, 48 | ), 49 | ), 50 | ), 51 | SizedBox( 52 | height: 50, 53 | ), 54 | TextField( 55 | controller: _userController, 56 | keyboardType: TextInputType.text, 57 | decoration: InputDecoration( 58 | hintText: 'UserName', 59 | prefixIcon: Icon( 60 | Icons.phone_iphone, 61 | color: Colors.grey, 62 | )), 63 | ), 64 | SizedBox( 65 | height: 40, 66 | ), 67 | TextField( 68 | obscureText: true, 69 | controller: _pwdController, 70 | decoration: InputDecoration( 71 | hintText: 'Password', 72 | prefixIcon: Icon( 73 | Icons.lock, 74 | color: Colors.grey, 75 | )), 76 | ), 77 | SizedBox( 78 | height: 120, 79 | ), 80 | Container( 81 | width: double.infinity, 82 | color: Colors.lightBlue, 83 | child: FlatButton( 84 | child: Text('登录'), 85 | onPressed: () { 86 | if (_userController.text.isEmpty||_pwdController.text.isEmpty) { 87 | AppToast.show('请输入用户名或密码'); 88 | } else { 89 | JMessage.login(username:_userController.text,password: _pwdController.text).then((result){ 90 | if (result!=null) { 91 | SpUtil.putString('jUserName', _userController.text); 92 | SpUtil.putString('jPassword', _pwdController.text); 93 | Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (context) => RootScene())); 94 | } else { 95 | AppToast.show('用户名或密码错误'); 96 | } 97 | }).catchError((error){ 98 | AppToast.show('登录失败'); 99 | }); 100 | 101 | } 102 | 103 | }, 104 | ), 105 | ) 106 | ], 107 | 108 | ), 109 | ), 110 | ), 111 | ); 112 | } 113 | } -------------------------------------------------------------------------------- /lib/views/message/message_list_scene.dart: -------------------------------------------------------------------------------- 1 | import 'package:badges/badges.dart'; 2 | import 'package:common_utils/common_utils.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:jmessage_flutter/jmessage_flutter.dart'; 6 | import 'package:provider/provider.dart'; 7 | import 'package:wing_chat/provider/group_list_provider.dart'; 8 | import 'package:wing_chat/tools/app_toast.dart'; 9 | import 'package:wing_chat/views/chat/group_chat_scene.dart'; 10 | import 'package:wing_chat/views/chat/single_chat_scene.dart'; 11 | 12 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 13 | 14 | class MessageListScene extends StatelessWidget { 15 | Widget _groupListCell(context, JMConversationInfo conversation) { 16 | String messageText = ''; 17 | String title = ''; 18 | var msgTime; 19 | var chatInfo; 20 | //如果该群组最近的一条消息对象不存在消息,则 conversation 对象中没有latestMessage该属性。 21 | if (conversation.latestMessage != null) { 22 | var lastMsg = 23 | JMNormalMessage.fromJson(conversation.latestMessage.toJson()); 24 | msgTime = TimelineUtil.format(lastMsg.createTime, 25 | locale: DateTime.now().timeZoneName, dayFormat: DayFormat.Full); 26 | } 27 | if (conversation.conversationType == JMConversationType.group) { 28 | var chatInfo = JMGroupInfo.fromJson(conversation.target.toJson()); 29 | title = chatInfo.name; 30 | } else { 31 | var chatInfo = JMUserInfo.fromJson(conversation.target.toJson()); 32 | title = chatInfo.nickname; 33 | } 34 | 35 | if (conversation.latestMessage is JMTextMessage) { 36 | var textMsg = JMTextMessage.fromJson(conversation.latestMessage.toJson()); 37 | messageText = textMsg.text; 38 | } else if (conversation.latestMessage is JMImageMessage) { 39 | messageText = '[图片]'; 40 | } else { 41 | messageText = '欢迎加入群聊!'; 42 | } 43 | 44 | return GestureDetector( 45 | behavior: HitTestBehavior.opaque, 46 | child: Container( 47 | color: Colors.white, 48 | child: Row( 49 | children: [ 50 | Container( 51 | padding: EdgeInsets.fromLTRB(18, 14, 15, 14), 52 | child: Badge( 53 | elevation: 0, 54 | showBadge: conversation.unreadCount != 0, 55 | badgeContent: Text( 56 | '${conversation.unreadCount}', 57 | style: TextStyle(color: Colors.white, fontSize: 8), 58 | ), 59 | child: CircleAvatar( 60 | radius: 23, 61 | backgroundImage: AssetImage('images/dog.jpg'), 62 | ), 63 | ), 64 | ), 65 | Expanded( 66 | child: Container( 67 | child: Column( 68 | crossAxisAlignment: CrossAxisAlignment.start, 69 | mainAxisAlignment: MainAxisAlignment.center, 70 | children: [ 71 | Text('$title', 72 | style: TextStyle( 73 | color: Colors.black, 74 | fontSize: 15, 75 | fontWeight: FontWeight.w500)), 76 | Text( 77 | messageText, 78 | overflow: TextOverflow.ellipsis, 79 | maxLines: 1, 80 | style: TextStyle(color: Colors.black38, fontSize: 12), 81 | ) 82 | ], 83 | ), 84 | ), 85 | ), 86 | Container( 87 | padding: EdgeInsets.all(15), 88 | // color: Colors.pink, 89 | child: Column( 90 | mainAxisAlignment: MainAxisAlignment.center, 91 | crossAxisAlignment: CrossAxisAlignment.end, 92 | children: [ 93 | // Text('创意与写作',style: TextStyle(color: AppColor.textColor,fontSize: 15,fontWeight: FontWeight.w500)), 94 | Text( 95 | '$msgTime', 96 | style: TextStyle(color: Colors.grey[400], fontSize: 10), 97 | ), 98 | SizedBox( 99 | height: 8, 100 | ), 101 | Container( 102 | child: null, 103 | ) 104 | ], 105 | ), 106 | ), 107 | ], 108 | ), 109 | ), 110 | onTap: () { 111 | if (conversation.conversationType == JMConversationType.group) { 112 | chatInfo = JMGroupInfo.fromJson(conversation.target.toJson()); 113 | Navigator.push( 114 | context, 115 | CupertinoPageRoute( 116 | builder: (context) => 117 | GroupChatScene(groupInfo: chatInfo))).then((value) { 118 | // Provider.of(context).loginStatusCheck(); 119 | }); 120 | }else if(conversation.conversationType == JMConversationType.single) { 121 | 122 | chatInfo = JMUserInfo.fromJson(conversation.target.toJson()); 123 | Navigator.push( 124 | context, 125 | CupertinoPageRoute( 126 | builder: (context) => 127 | SingleChatScene(userInfo: chatInfo))).then((value) { 128 | // Provider.of(context).loginStatusCheck(); 129 | }); 130 | } 131 | }, 132 | ); 133 | } 134 | 135 | @override 136 | Widget build(BuildContext context) { 137 | return MultiProvider( 138 | providers: [ 139 | ChangeNotifierProvider( 140 | create: (_) => GroupListProvider()..loginStatusCheck()), 141 | ], 142 | child: Scaffold( 143 | appBar: AppBar( 144 | title: Text( 145 | '消息', 146 | style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), 147 | ), 148 | centerTitle: true, 149 | // elevation: 0, 150 | ), 151 | backgroundColor: ThemeColor, 152 | body: Consumer(builder: (_, provider, widget) { 153 | if (provider.groupList.length > 0) { 154 | return ListView.builder( 155 | itemBuilder: (context, index) { 156 | return _groupListCell(context, provider.groupList[index]); 157 | }, 158 | itemCount: provider.groupList.length, 159 | ); 160 | } else { 161 | return Container(); 162 | } 163 | })), 164 | ); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /lib/views/mine/mine_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wing_chat/views/discover/discover_cell.dart'; 3 | 4 | final ThemeColor = Color.fromRGBO(237, 237, 237, 1.0); 5 | 6 | class MineScene extends StatefulWidget { 7 | @override 8 | _MineSceneState createState() => _MineSceneState(); 9 | } 10 | 11 | class _MineSceneState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | body: Stack( 16 | children: [ 17 | Container( 18 | color: ThemeColor, 19 | child: MediaQuery.removePadding( 20 | removeTop: true, 21 | context: context, 22 | child: ListView( 23 | children: [ 24 | Container( 25 | height: 200, 26 | color: Colors.white, 27 | padding: EdgeInsets.only(left: 20,right: 10), 28 | child: Column( 29 | mainAxisAlignment: MainAxisAlignment.spaceAround, 30 | children: [ 31 | Container(height: 20,), 32 | Container( 33 | margin: EdgeInsets.only(top: 40), 34 | child: Row( 35 | children: [ 36 | Container( 37 | height:70, 38 | width: 70, 39 | decoration: BoxDecoration( 40 | color:Colors.blue, 41 | borderRadius: BorderRadius.circular(6.0), 42 | image: DecorationImage(image: AssetImage("images/dog.jpg")) 43 | ), 44 | ), 45 | Expanded( 46 | child: Container( 47 | padding: EdgeInsets.only(left: 10), 48 | width: MediaQuery.of(context).size.width-110, 49 | child: Column( 50 | mainAxisAlignment: MainAxisAlignment.center, 51 | children: [ 52 | Container( 53 | height:35, 54 | alignment: Alignment.centerLeft, 55 | child: Text( 56 | "飞鱼", 57 | style: TextStyle(fontSize: 20.0,fontWeight: FontWeight.w500), 58 | ), 59 | ),//微信名称 60 | Container( 61 | child: Row( 62 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 63 | children: [ 64 | Text("微信号:PP514718793", 65 | style: TextStyle(fontSize: 17.0,color: Colors.grey) 66 | ), 67 | Image(image:AssetImage("images/icon_right.png"),width: 15) 68 | ], 69 | ), 70 | ) 71 | ], 72 | ), 73 | ) 74 | ) 75 | ], 76 | ), 77 | ) 78 | ], 79 | ), 80 | ), 81 | SizedBox(height: 10,), 82 | DiscoverCell( 83 | imageName: 'images/微信 支付.png', 84 | title: '支付', 85 | ), 86 | Row( 87 | children: [ 88 | Container(width: 50,height: 0.5,color: Colors.white,), 89 | Container(height: 0.5,color: Colors.grey,), 90 | ], 91 | ), 92 | DiscoverCell( 93 | imageName: 'images/微信收藏.png', 94 | title: '收藏', 95 | ), 96 | Row( 97 | children: [ 98 | Container(width: 50,height: 0.5,color: Colors.white,), 99 | Container(height: 0.5,color: Colors.grey,), 100 | ], 101 | ), 102 | DiscoverCell( 103 | imageName: 'images/微信相册.png', 104 | title: '相册', 105 | ), 106 | Row( 107 | children: [ 108 | Container(width: 50,height: 0.5,color: Colors.white,), 109 | Container(height: 0.5,color: Colors.grey,), 110 | ], 111 | ), 112 | DiscoverCell( 113 | imageName: 'images/微信卡包.png', 114 | title: '卡包', 115 | ), 116 | Row( 117 | children: [ 118 | Container(width: 50,height: 0.5,color: Colors.white,), 119 | Container(height: 0.5,color: Colors.grey,), 120 | ], 121 | ), 122 | DiscoverCell( 123 | imageName: 'images/微信表情.png', 124 | title: '表情', 125 | ), 126 | Container(height: 10,), 127 | DiscoverCell( 128 | imageName: 'images/微信设置.png', 129 | title: '设置', 130 | ), 131 | ], 132 | ), 133 | ) 134 | ), 135 | Container( 136 | margin: EdgeInsets.only(top: 40,right: 15), 137 | height: 30, 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.end, 140 | children: [ 141 | Image(image: AssetImage("images/相机.png"),width: 26,) 142 | ], 143 | ), 144 | ) 145 | ], 146 | ), 147 | ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /lib/views/root_scene.dart: -------------------------------------------------------------------------------- 1 | // import 'package:badges/badges.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:wing_chat/provider/root_scene_provider.dart'; 5 | import 'friends/friends_scene.dart'; 6 | import 'message/message_list_scene.dart'; 7 | import 'discover/discover_scene.dart'; 8 | import 'mine/mine_page.dart'; 9 | 10 | 11 | class RootScene extends StatelessWidget { 12 | final List _tabPages = [ 13 | MessageListScene(), 14 | FriendsScene(), 15 | DiscoverScene(), 16 | MineScene() 17 | ]; 18 | 19 | List _bottomTabs() { 20 | return [ 21 | BottomNavigationBarItem( 22 | icon: Image(image: AssetImage('images/tabbar_chat.png'),height: 24,), 23 | activeIcon: Image(image: AssetImage('images/tabbar_chat_hl.png'),height: 24,), 24 | title: Text('微信') 25 | ), 26 | BottomNavigationBarItem( 27 | icon: Image(image: AssetImage('images/tabbar_friends.png'),height: 24,), 28 | activeIcon: Image(image: AssetImage('images/tabbar_friends_hl.png'),height: 24,), 29 | title: Text('通讯录') 30 | ), 31 | BottomNavigationBarItem( 32 | icon: Image(image: AssetImage('images/tabbar_discover.png'),height: 24,), 33 | activeIcon: Image(image: AssetImage('images/tabbar_discover_hl.png'),height: 24,), 34 | title: Text('发现') 35 | ), 36 | BottomNavigationBarItem( 37 | icon: Image(image: AssetImage('images/tabbar_mine.png'),height: 24,), 38 | activeIcon: Image(image: AssetImage('images/tabbar_mine_hl.png'),height: 24,), 39 | title: Text('我'), 40 | ), 41 | ]; 42 | } 43 | 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | 48 | return MultiProvider( 49 | providers: [ 50 | ChangeNotifierProvider(create: (_) => RootSceneProvider()), 51 | ], 52 | child: Consumer(builder: (context,provider,widget){ 53 | return Scaffold( 54 | backgroundColor: Colors.white, 55 | body: IndexedStack(index: provider.currentIndex, children: _tabPages), 56 | bottomNavigationBar: BottomNavigationBar( 57 | backgroundColor: Colors.white, 58 | fixedColor: Colors.green, 59 | items: _bottomTabs(), 60 | type: BottomNavigationBarType.fixed, 61 | currentIndex: provider.currentIndex, 62 | onTap: (index) { 63 | provider.currentIndex = index; 64 | if (index == 0) { 65 | 66 | } else if (index == 1) { 67 | 68 | } 69 | }, 70 | ), 71 | ); 72 | }) 73 | ); 74 | } 75 | } 76 | 77 | // class RootScene extends StatelessWidget { 78 | // final List _tabPages = [ 79 | // MessageListScene(), 80 | // FriendsScene(), 81 | // DiscoverScene(), 82 | // MineScene() 83 | // ]; 84 | 85 | // List _bottomTabs(int selectIndex, int unreadCount) { 86 | // return [ 87 | // BottomNavigationBarItem( 88 | // icon: Image(image: AssetImage('images/tabbar_chat.png'),height: 24,), 89 | // activeIcon: Image(image: AssetImage('images/tabbar_chat_hl.png'),height: 24,), 90 | // title: Text('微信') 91 | // ), 92 | // BottomNavigationBarItem( 93 | // icon: Image(image: AssetImage('images/tabbar_friends.png'),height: 24,), 94 | // activeIcon: Image(image: AssetImage('images/tabbar_friends_hl.png'),height: 24,), 95 | // title: Text('通讯录') 96 | // ), 97 | // BottomNavigationBarItem( 98 | // icon: Image(image: AssetImage('images/tabbar_discover.png'),height: 24,), 99 | // activeIcon: Image(image: AssetImage('images/tabbar_discover_hl.png'),height: 24,), 100 | // title: Text('发现') 101 | // ), 102 | // BottomNavigationBarItem( 103 | // icon: Image(image: AssetImage('images/tabbar_mine.png'),height: 24,), 104 | // activeIcon: Image(image: AssetImage('images/tabbar_mine_hl.png'),height: 24,), 105 | // title: Text('我'), 106 | // ), 107 | // ]; 108 | // } 109 | 110 | // @override 111 | // Widget build(BuildContext context) { 112 | 113 | // return MultiProvider( 114 | // providers: [ 115 | // ChangeNotifierProvider(builder: (_) => RootSceneProvider()), 116 | // // ChangeNotifierProvider( 117 | // // builder: (_) => GroupListProvider()..loginStatusCheck()), 118 | // // ChangeNotifierProvider(builder: (_) => CourseProvider()..getData()), 119 | // ], 120 | // child: Consumer( 121 | // builder: (context, provider, widget) { 122 | // return Scaffold( 123 | // backgroundColor: Colors.white, 124 | // body: IndexedStack(index: provider.currentIndex, children: _tabPages), 125 | // bottomNavigationBar: BottomNavigationBar( 126 | // backgroundColor: Colors.white, 127 | // items: 128 | // _bottomTabs(provider.currentIndex, provider.currentIndex), 129 | // type: BottomNavigationBarType.fixed, 130 | // currentIndex: provider.currentIndex, 131 | // onTap: (index) { 132 | // provider.currentIndex = index; 133 | // if (index == 1) { 134 | // // groupProvider.loginStatusCheck(); 135 | // } else if (index == 0) { 136 | // // courseProvider.getData(); 137 | // } 138 | // }, 139 | // ), 140 | // ); 141 | // }), 142 | // ); 143 | // } 144 | // } 145 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wing_chat 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | provider: ^3.1.0 27 | badges: ^1.1.0 28 | extended_image: ^0.6.4 29 | extended_text: any 30 | extended_text_field: ^0.4.4 31 | image_picker: ^0.6.0+10 32 | fluttertoast: ^3.1.0 33 | flutter_easyrefresh: ^2.0.3 34 | common_utils: ^1.1.1 35 | pull_to_refresh: ^1.5.4 36 | jmessage_flutter: ^0.5.0 37 | flutter_screenutil: 38 | shared_preferences: 39 | synchronized: 40 | 41 | flutter_localizations: 42 | sdk: flutter 43 | 44 | dev_dependencies: 45 | flutter_test: 46 | sdk: flutter 47 | 48 | 49 | # For information on the generic Dart part of this file, see the 50 | # following page: https://dart.dev/tools/pub/pubspec 51 | 52 | # The following section is specific to Flutter. 53 | flutter: 54 | 55 | # The following line ensures that the Material Icons font is 56 | # included with your application, so that you can use the icons in 57 | # the material Icons class. 58 | uses-material-design: true 59 | 60 | # To add assets to your application, add an assets section, like this: 61 | assets: 62 | - images/ 63 | - emoji/ 64 | # - images/a_dot_ham.jpeg 65 | 66 | # An image asset can refer to one or more resolution-specific "variants", see 67 | # https://flutter.dev/assets-and-images/#resolution-aware. 68 | 69 | # For details regarding adding assets from package dependencies, see 70 | # https://flutter.dev/assets-and-images/#from-packages 71 | 72 | # To add custom fonts to your application, add a fonts section here, 73 | # in this "flutter" section. Each entry in this list should have a 74 | # "family" key with the font family name, and a "fonts" key with a 75 | # list giving the asset and other descriptors for the font. For 76 | # example: 77 | # fonts: 78 | # - family: Schyler 79 | # fonts: 80 | # - asset: fonts/Schyler-Regular.ttf 81 | # - asset: fonts/Schyler-Italic.ttf 82 | # style: italic 83 | # - family: Trajan Pro 84 | # fonts: 85 | # - asset: fonts/TrajanPro.ttf 86 | # - asset: fonts/TrajanPro_Bold.ttf 87 | # weight: 700 88 | # 89 | # For details regarding fonts from package dependencies, 90 | # see https://flutter.dev/custom-fonts/#from-packages 91 | -------------------------------------------------------------------------------- /show_images/会话列表.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/show_images/会话列表.jpg -------------------------------------------------------------------------------- /show_images/群聊.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/show_images/群聊.jpg -------------------------------------------------------------------------------- /show_images/表情.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShellPeng/wing_chat/581ba0e4766d9686de4d91938f0d7de23c5dc5da/show_images/表情.jpg -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:wing_chat/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | // await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------