├── .gitignore ├── .metadata ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── wechat │ │ │ │ └── MainActivity.java │ │ └── 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 ├── assets ├── demo │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── ercode.png ├── fonts │ ├── demo.css │ ├── demo_index.html │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.svg │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.woff2 └── images │ ├── ad_game_notify.png │ ├── default_nor_avatar.png │ ├── ic_album.png │ ├── ic_bottle_msg.png │ ├── ic_cards_wallet.png │ ├── ic_collections.png │ ├── ic_emotions.png │ ├── ic_feeds.png │ ├── ic_fengchao.png │ ├── ic_file_transfer.png │ ├── ic_game_entry.png │ ├── ic_group_chat.png │ ├── ic_mini_program.png │ ├── ic_new_friend.png │ ├── ic_people_nearby.png │ ├── ic_public_account.png │ ├── ic_qrcode_preview_tiny.png │ ├── ic_quick_scan.png │ ├── ic_quick_search.png │ ├── ic_settings.png │ ├── ic_shake_phone.png │ ├── ic_shopping.png │ ├── ic_social_circle.png │ ├── ic_tag.png │ ├── ic_tx_news.png │ ├── ic_wallet.png │ └── ic_wx_games.png ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── 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 │ └── main.m ├── lib ├── common │ └── style │ │ └── style.dart ├── main.dart ├── model │ ├── contacts.dart │ ├── conversation.dart │ └── me.dart ├── pages │ ├── chat_detail │ │ └── chat_content_view.dart │ ├── chat_detail_page.dart │ ├── component │ │ ├── 404.dart │ │ ├── full_with_icon_button.dart │ │ └── user_avatat.dart │ ├── contacts_page.dart │ ├── contacts_page │ │ └── contact_item.dart │ ├── discover_page.dart │ ├── index_page.dart │ ├── message_page.dart │ ├── message_page │ │ └── conversation_item.dart │ └── mine_page.dart ├── provide │ ├── currentIndex.dart │ └── websocket.dart └── routers │ ├── application.dart │ ├── router_handler.dart │ └── routers.dart ├── package-lock.json ├── package.json ├── pubspec.lock ├── pubspec.yaml ├── server └── index.js ├── test └── widget_test.dart └── wechat.apk /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | node_modules 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | /build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 仿写微信 2 | 3 | 这是一个使用 Flutter 框架开发的高仿微信移动客户端的项目,目的是学习 Flutter 框架。 4 | 5 | ## 项目进度 6 | 7 | 目前已经实现了部分websocket服务,server文件夹下为使用node构建的websocket服务,自定义固定群,所有用户启动APP自动加入flutter交流群,实时添加新加入的用户。目前支持群聊与私聊。聊天记录后台不存储只提供消息通信 8 | 9 | 项目内提供了固定的websocket后端服务,可修改为本地server服务端口 10 | 11 | ## 安卓APK下载 12 | 快来下载进入APP,在群里聊骚吧,哈哈...... 13 | 14 | 15 | 16 | ![下载二维码](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/ercode.png "下载二维码") 17 | 18 | 19 | ## 项目成果预览 20 | 21 | | 页面 | 图片 | 页面 | 图片 | 22 | | -- | -- | -- | -- | 23 | | 会话列表 | ![会话列表](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/2.png "会话列表") | 通讯录 | ![通讯录](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/3.png "通讯录") | 24 | | 发现频道 | ![发现频道](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/5.png "发现频道") | 我 | ![我](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/6.png "我") | 25 | | 快速滚动条 | ![快速滚动条](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/4.png "快速滚动条") | 弹出菜单 | ![弹出菜单](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/1.png "弹出菜单") | 26 | | 聊天详情页 | ![聊天详情页](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/7.png "聊天详情页") | 消息弹出菜单 | ![消息弹出菜单](https://raw.githubusercontent.com/wkiwi/flutter_WeChat/master/assets/demo/8.png "消息弹出菜单") | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.wechat" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 14 | 21 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/wechat/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.wechat; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /assets/demo/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/1.png -------------------------------------------------------------------------------- /assets/demo/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/2.png -------------------------------------------------------------------------------- /assets/demo/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/3.png -------------------------------------------------------------------------------- /assets/demo/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/4.png -------------------------------------------------------------------------------- /assets/demo/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/5.png -------------------------------------------------------------------------------- /assets/demo/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/6.png -------------------------------------------------------------------------------- /assets/demo/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/7.png -------------------------------------------------------------------------------- /assets/demo/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/8.png -------------------------------------------------------------------------------- /assets/demo/ercode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/demo/ercode.png -------------------------------------------------------------------------------- /assets/fonts/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /assets/fonts/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "wxIconFont"; 2 | src: url('iconfont.eot?t=1558074402412'); /* IE9 */ 3 | src: url('iconfont.eot?t=1558074402412#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1558074402412') format('woff'), 6 | url('iconfont.ttf?t=1558074402412') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1558074402412#wxIconFont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .wxIconFont { 11 | font-family: "wxIconFont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .iconqiaquan:before { 19 | content: "\e607"; 20 | } 21 | 22 | .iconbiaoqing:before { 23 | content: "\e88f"; 24 | } 25 | 26 | .iconweibiaoti554:before { 27 | content: "\e63b"; 28 | } 29 | 30 | .iconwenjian:before { 31 | content: "\e75b"; 32 | } 33 | 34 | .iconmiandarao:before { 35 | content: "\e75e"; 36 | } 37 | 38 | .iconwin:before { 39 | content: "\e64f"; 40 | } 41 | 42 | .iconxiaoxi1:before { 43 | content: "\e66e"; 44 | } 45 | 46 | .iconzhiwen:before { 47 | content: "\e604"; 48 | } 49 | 50 | .iconcomputer_icon:before { 51 | content: "\e673"; 52 | } 53 | 54 | .iconguide-fill:before { 55 | content: "\e600"; 56 | } 57 | 58 | .iconxiangji1:before { 59 | content: "\e6cf"; 60 | } 61 | 62 | .iconxiaoxi:before { 63 | content: "\e622"; 64 | } 65 | 66 | .iconcaidan:before { 67 | content: "\e60e"; 68 | } 69 | 70 | .iconsearch:before { 71 | content: "\e63e"; 72 | } 73 | 74 | .iconxiaoxidian:before { 75 | content: "\e620"; 76 | } 77 | 78 | .icontongxunlu:before { 79 | content: "\e648"; 80 | } 81 | 82 | .iconyuyin:before { 83 | content: "\e860"; 84 | } 85 | 86 | .iconhongbao:before { 87 | content: "\e608"; 88 | } 89 | 90 | .iconwo:before { 91 | content: "\e670"; 92 | } 93 | 94 | .icontianjiapengyou:before { 95 | content: "\e624"; 96 | } 97 | 98 | .iconwo1:before { 99 | content: "\e601"; 100 | } 101 | 102 | .iconzhinan:before { 103 | content: "\e613"; 104 | } 105 | 106 | .iconxin:before { 107 | content: "\e605"; 108 | } 109 | 110 | .iconyuyin1:before { 111 | content: "\e606"; 112 | } 113 | 114 | .iconnext-copy:before { 115 | content: "\e60b"; 116 | } 117 | 118 | .iconpengyouquan:before { 119 | content: "\e690"; 120 | } 121 | 122 | .iconjia:before { 123 | content: "\e6d3"; 124 | } 125 | 126 | .iconmingpian:before { 127 | content: "\e8c2"; 128 | } 129 | 130 | .iconxiangji:before { 131 | content: "\e6f3"; 132 | } 133 | 134 | .iconyuyinshuru:before { 135 | content: "\e658"; 136 | } 137 | 138 | .iconico:before { 139 | content: "\e646"; 140 | } 141 | 142 | .iconshoufukuan:before { 143 | content: "\e602"; 144 | } 145 | 146 | .iconlifangti:before { 147 | content: "\e666"; 148 | } 149 | 150 | .iconweizhi:before { 151 | content: "\e609"; 152 | } 153 | 154 | .iconicon-scan:before { 155 | content: "\e634"; 156 | } 157 | 158 | .iconyuyintonghua:before { 159 | content: "\e60a"; 160 | } 161 | 162 | .iconxiangce:before { 163 | content: "\e667"; 164 | } 165 | 166 | .icontongxunlu1:before { 167 | content: "\e603"; 168 | } 169 | 170 | -------------------------------------------------------------------------------- /assets/fonts/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/fonts/iconfont.eot -------------------------------------------------------------------------------- /assets/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/fonts/iconfont.ttf -------------------------------------------------------------------------------- /assets/fonts/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/fonts/iconfont.woff -------------------------------------------------------------------------------- /assets/fonts/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/fonts/iconfont.woff2 -------------------------------------------------------------------------------- /assets/images/ad_game_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ad_game_notify.png -------------------------------------------------------------------------------- /assets/images/default_nor_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/default_nor_avatar.png -------------------------------------------------------------------------------- /assets/images/ic_album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_album.png -------------------------------------------------------------------------------- /assets/images/ic_bottle_msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_bottle_msg.png -------------------------------------------------------------------------------- /assets/images/ic_cards_wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_cards_wallet.png -------------------------------------------------------------------------------- /assets/images/ic_collections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_collections.png -------------------------------------------------------------------------------- /assets/images/ic_emotions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_emotions.png -------------------------------------------------------------------------------- /assets/images/ic_feeds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_feeds.png -------------------------------------------------------------------------------- /assets/images/ic_fengchao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_fengchao.png -------------------------------------------------------------------------------- /assets/images/ic_file_transfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_file_transfer.png -------------------------------------------------------------------------------- /assets/images/ic_game_entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_game_entry.png -------------------------------------------------------------------------------- /assets/images/ic_group_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_group_chat.png -------------------------------------------------------------------------------- /assets/images/ic_mini_program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_mini_program.png -------------------------------------------------------------------------------- /assets/images/ic_new_friend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_new_friend.png -------------------------------------------------------------------------------- /assets/images/ic_people_nearby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_people_nearby.png -------------------------------------------------------------------------------- /assets/images/ic_public_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_public_account.png -------------------------------------------------------------------------------- /assets/images/ic_qrcode_preview_tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_qrcode_preview_tiny.png -------------------------------------------------------------------------------- /assets/images/ic_quick_scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_quick_scan.png -------------------------------------------------------------------------------- /assets/images/ic_quick_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_quick_search.png -------------------------------------------------------------------------------- /assets/images/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_settings.png -------------------------------------------------------------------------------- /assets/images/ic_shake_phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_shake_phone.png -------------------------------------------------------------------------------- /assets/images/ic_shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_shopping.png -------------------------------------------------------------------------------- /assets/images/ic_social_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_social_circle.png -------------------------------------------------------------------------------- /assets/images/ic_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_tag.png -------------------------------------------------------------------------------- /assets/images/ic_tx_news.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_tx_news.png -------------------------------------------------------------------------------- /assets/images/ic_wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_wallet.png -------------------------------------------------------------------------------- /assets/images/ic_wx_games.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/assets/images/ic_wx_games.png -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_webview_plugin (0.0.1): 4 | - Flutter 5 | - fluttertoast (0.0.2): 6 | - Flutter 7 | - shared_preferences (0.0.1): 8 | - Flutter 9 | - url_launcher (0.0.1): 10 | - Flutter 11 | 12 | DEPENDENCIES: 13 | - Flutter (from `.symlinks/flutter/ios`) 14 | - flutter_webview_plugin (from `.symlinks/plugins/flutter_webview_plugin/ios`) 15 | - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) 16 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 17 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`) 18 | 19 | EXTERNAL SOURCES: 20 | Flutter: 21 | :path: ".symlinks/flutter/ios" 22 | flutter_webview_plugin: 23 | :path: ".symlinks/plugins/flutter_webview_plugin/ios" 24 | fluttertoast: 25 | :path: ".symlinks/plugins/fluttertoast/ios" 26 | shared_preferences: 27 | :path: ".symlinks/plugins/shared_preferences/ios" 28 | url_launcher: 29 | :path: ".symlinks/plugins/url_launcher/ios" 30 | 31 | SPEC CHECKSUMS: 32 | Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a 33 | flutter_webview_plugin: ed9e8a6a96baf0c867e90e1bce2673913eeac694 34 | fluttertoast: b644586ef3b16f67fae9a1f8754cef6b2d6b634b 35 | shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523 36 | url_launcher: 0067ddb8f10d36786672aa0722a21717dba3a298 37 | 38 | PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 39 | 40 | COCOAPODS: 1.6.1 41 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 7DBDF5CD0746BBF09453C376 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 21945F6FBA6A4DC7B02C29D2 /* libPods-Runner.a */; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 34 | ); 35 | name = "Embed Frameworks"; 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 21945F6FBA6A4DC7B02C29D2 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 29D639EBD96AD840D3F1FDF6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 45 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 46 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 47 | 6B2D72ECAF65DE9E7474B59F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 48 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 49 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 50 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 53 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 54 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 56 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 57 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 58 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 59 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 9AF4C0CF0CD84EEF59C8A434 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 69 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 70 | 7DBDF5CD0746BBF09453C376 /* libPods-Runner.a in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 16D7C5B3336B04D19C0413D4 /* Frameworks */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 21945F6FBA6A4DC7B02C29D2 /* libPods-Runner.a */, 81 | ); 82 | name = Frameworks; 83 | sourceTree = ""; 84 | }; 85 | 9740EEB11CF90186004384FC /* Flutter */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3B80C3931E831B6300D905FE /* App.framework */, 89 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 90 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 91 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 92 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 93 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 94 | ); 95 | name = Flutter; 96 | sourceTree = ""; 97 | }; 98 | 97C146E51CF9000F007C117D = { 99 | isa = PBXGroup; 100 | children = ( 101 | 9740EEB11CF90186004384FC /* Flutter */, 102 | 97C146F01CF9000F007C117D /* Runner */, 103 | 97C146EF1CF9000F007C117D /* Products */, 104 | EF01D12819A32D477AF13015 /* Pods */, 105 | 16D7C5B3336B04D19C0413D4 /* Frameworks */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | 97C146EF1CF9000F007C117D /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 97C146EE1CF9000F007C117D /* Runner.app */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 97C146F01CF9000F007C117D /* Runner */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 121 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 122 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 123 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 124 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 125 | 97C147021CF9000F007C117D /* Info.plist */, 126 | 97C146F11CF9000F007C117D /* Supporting Files */, 127 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 128 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 129 | ); 130 | path = Runner; 131 | sourceTree = ""; 132 | }; 133 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 97C146F21CF9000F007C117D /* main.m */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | EF01D12819A32D477AF13015 /* Pods */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 6B2D72ECAF65DE9E7474B59F /* Pods-Runner.debug.xcconfig */, 145 | 9AF4C0CF0CD84EEF59C8A434 /* Pods-Runner.release.xcconfig */, 146 | 29D639EBD96AD840D3F1FDF6 /* Pods-Runner.profile.xcconfig */, 147 | ); 148 | name = Pods; 149 | path = Pods; 150 | sourceTree = ""; 151 | }; 152 | /* End PBXGroup section */ 153 | 154 | /* Begin PBXNativeTarget section */ 155 | 97C146ED1CF9000F007C117D /* Runner */ = { 156 | isa = PBXNativeTarget; 157 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 158 | buildPhases = ( 159 | D168D372EA2B54EEBA5498BA /* [CP] Check Pods Manifest.lock */, 160 | 9740EEB61CF901F6004384FC /* Run Script */, 161 | 97C146EA1CF9000F007C117D /* Sources */, 162 | 97C146EB1CF9000F007C117D /* Frameworks */, 163 | 97C146EC1CF9000F007C117D /* Resources */, 164 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 165 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 166 | 210F4B639EFB1097AC9D0F9F /* [CP] Embed Pods Frameworks */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | ); 172 | name = Runner; 173 | productName = Runner; 174 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 175 | productType = "com.apple.product-type.application"; 176 | }; 177 | /* End PBXNativeTarget section */ 178 | 179 | /* Begin PBXProject section */ 180 | 97C146E61CF9000F007C117D /* Project object */ = { 181 | isa = PBXProject; 182 | attributes = { 183 | LastUpgradeCheck = 0910; 184 | ORGANIZATIONNAME = "The Chromium Authors"; 185 | TargetAttributes = { 186 | 97C146ED1CF9000F007C117D = { 187 | CreatedOnToolsVersion = 7.3.1; 188 | }; 189 | }; 190 | }; 191 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 192 | compatibilityVersion = "Xcode 3.2"; 193 | developmentRegion = English; 194 | hasScannedForEncodings = 0; 195 | knownRegions = ( 196 | en, 197 | Base, 198 | ); 199 | mainGroup = 97C146E51CF9000F007C117D; 200 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 201 | projectDirPath = ""; 202 | projectRoot = ""; 203 | targets = ( 204 | 97C146ED1CF9000F007C117D /* Runner */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXResourcesBuildPhase section */ 210 | 97C146EC1CF9000F007C117D /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 215 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 216 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 217 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXResourcesBuildPhase section */ 223 | 224 | /* Begin PBXShellScriptBuildPhase section */ 225 | 210F4B639EFB1097AC9D0F9F /* [CP] Embed Pods Frameworks */ = { 226 | isa = PBXShellScriptBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | inputFileListPaths = ( 231 | ); 232 | inputPaths = ( 233 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 234 | "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", 235 | ); 236 | name = "[CP] Embed Pods Frameworks"; 237 | outputFileListPaths = ( 238 | ); 239 | outputPaths = ( 240 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | shellPath = /bin/sh; 244 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 245 | showEnvVarsInLog = 0; 246 | }; 247 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 248 | isa = PBXShellScriptBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | ); 252 | inputPaths = ( 253 | ); 254 | name = "Thin Binary"; 255 | outputPaths = ( 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | shellPath = /bin/sh; 259 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 260 | }; 261 | 9740EEB61CF901F6004384FC /* Run Script */ = { 262 | isa = PBXShellScriptBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | ); 266 | inputPaths = ( 267 | ); 268 | name = "Run Script"; 269 | outputPaths = ( 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | shellPath = /bin/sh; 273 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 274 | }; 275 | D168D372EA2B54EEBA5498BA /* [CP] Check Pods Manifest.lock */ = { 276 | isa = PBXShellScriptBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | inputFileListPaths = ( 281 | ); 282 | inputPaths = ( 283 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 284 | "${PODS_ROOT}/Manifest.lock", 285 | ); 286 | name = "[CP] Check Pods Manifest.lock"; 287 | outputFileListPaths = ( 288 | ); 289 | outputPaths = ( 290 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | shellPath = /bin/sh; 294 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 295 | showEnvVarsInLog = 0; 296 | }; 297 | /* End PBXShellScriptBuildPhase section */ 298 | 299 | /* Begin PBXSourcesBuildPhase section */ 300 | 97C146EA1CF9000F007C117D /* Sources */ = { 301 | isa = PBXSourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 305 | 97C146F31CF9000F007C117D /* main.m in Sources */, 306 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | /* End PBXSourcesBuildPhase section */ 311 | 312 | /* Begin PBXVariantGroup section */ 313 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 314 | isa = PBXVariantGroup; 315 | children = ( 316 | 97C146FB1CF9000F007C117D /* Base */, 317 | ); 318 | name = Main.storyboard; 319 | sourceTree = ""; 320 | }; 321 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 322 | isa = PBXVariantGroup; 323 | children = ( 324 | 97C147001CF9000F007C117D /* Base */, 325 | ); 326 | name = LaunchScreen.storyboard; 327 | sourceTree = ""; 328 | }; 329 | /* End PBXVariantGroup section */ 330 | 331 | /* Begin XCBuildConfiguration section */ 332 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 333 | isa = XCBuildConfiguration; 334 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 335 | buildSettings = { 336 | ALWAYS_SEARCH_USER_PATHS = NO; 337 | CLANG_ANALYZER_NONNULL = YES; 338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 339 | CLANG_CXX_LIBRARY = "libc++"; 340 | CLANG_ENABLE_MODULES = YES; 341 | CLANG_ENABLE_OBJC_ARC = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 347 | CLANG_WARN_EMPTY_BODY = YES; 348 | CLANG_WARN_ENUM_CONVERSION = YES; 349 | CLANG_WARN_INFINITE_RECURSION = YES; 350 | CLANG_WARN_INT_CONVERSION = YES; 351 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 352 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 353 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 355 | CLANG_WARN_STRICT_PROTOTYPES = YES; 356 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 360 | COPY_PHASE_STRIP = NO; 361 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 362 | ENABLE_NS_ASSERTIONS = NO; 363 | ENABLE_STRICT_OBJC_MSGSEND = YES; 364 | GCC_C_LANGUAGE_STANDARD = gnu99; 365 | GCC_NO_COMMON_BLOCKS = YES; 366 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 368 | GCC_WARN_UNDECLARED_SELECTOR = YES; 369 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 370 | GCC_WARN_UNUSED_FUNCTION = YES; 371 | GCC_WARN_UNUSED_VARIABLE = YES; 372 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 373 | MTL_ENABLE_DEBUG_INFO = NO; 374 | SDKROOT = iphoneos; 375 | TARGETED_DEVICE_FAMILY = "1,2"; 376 | VALIDATE_PRODUCT = YES; 377 | }; 378 | name = Profile; 379 | }; 380 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 381 | isa = XCBuildConfiguration; 382 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 383 | buildSettings = { 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 386 | DEVELOPMENT_TEAM = S8QB4VV633; 387 | ENABLE_BITCODE = NO; 388 | FRAMEWORK_SEARCH_PATHS = ( 389 | "$(inherited)", 390 | "$(PROJECT_DIR)/Flutter", 391 | ); 392 | INFOPLIST_FILE = Runner/Info.plist; 393 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 394 | LIBRARY_SEARCH_PATHS = ( 395 | "$(inherited)", 396 | "$(PROJECT_DIR)/Flutter", 397 | ); 398 | PRODUCT_BUNDLE_IDENTIFIER = com.example.wechat; 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | VERSIONING_SYSTEM = "apple-generic"; 401 | }; 402 | name = Profile; 403 | }; 404 | 97C147031CF9000F007C117D /* Debug */ = { 405 | isa = XCBuildConfiguration; 406 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 407 | buildSettings = { 408 | ALWAYS_SEARCH_USER_PATHS = NO; 409 | CLANG_ANALYZER_NONNULL = YES; 410 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 411 | CLANG_CXX_LIBRARY = "libc++"; 412 | CLANG_ENABLE_MODULES = YES; 413 | CLANG_ENABLE_OBJC_ARC = YES; 414 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 415 | CLANG_WARN_BOOL_CONVERSION = YES; 416 | CLANG_WARN_COMMA = YES; 417 | CLANG_WARN_CONSTANT_CONVERSION = YES; 418 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 419 | CLANG_WARN_EMPTY_BODY = YES; 420 | CLANG_WARN_ENUM_CONVERSION = YES; 421 | CLANG_WARN_INFINITE_RECURSION = YES; 422 | CLANG_WARN_INT_CONVERSION = YES; 423 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 424 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 427 | CLANG_WARN_STRICT_PROTOTYPES = YES; 428 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 429 | CLANG_WARN_UNREACHABLE_CODE = YES; 430 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 431 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 432 | COPY_PHASE_STRIP = NO; 433 | DEBUG_INFORMATION_FORMAT = dwarf; 434 | ENABLE_STRICT_OBJC_MSGSEND = YES; 435 | ENABLE_TESTABILITY = YES; 436 | GCC_C_LANGUAGE_STANDARD = gnu99; 437 | GCC_DYNAMIC_NO_PIC = NO; 438 | GCC_NO_COMMON_BLOCKS = YES; 439 | GCC_OPTIMIZATION_LEVEL = 0; 440 | GCC_PREPROCESSOR_DEFINITIONS = ( 441 | "DEBUG=1", 442 | "$(inherited)", 443 | ); 444 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 445 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 446 | GCC_WARN_UNDECLARED_SELECTOR = YES; 447 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 448 | GCC_WARN_UNUSED_FUNCTION = YES; 449 | GCC_WARN_UNUSED_VARIABLE = YES; 450 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 451 | MTL_ENABLE_DEBUG_INFO = YES; 452 | ONLY_ACTIVE_ARCH = YES; 453 | SDKROOT = iphoneos; 454 | TARGETED_DEVICE_FAMILY = "1,2"; 455 | }; 456 | name = Debug; 457 | }; 458 | 97C147041CF9000F007C117D /* Release */ = { 459 | isa = XCBuildConfiguration; 460 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 461 | buildSettings = { 462 | ALWAYS_SEARCH_USER_PATHS = NO; 463 | CLANG_ANALYZER_NONNULL = YES; 464 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 465 | CLANG_CXX_LIBRARY = "libc++"; 466 | CLANG_ENABLE_MODULES = YES; 467 | CLANG_ENABLE_OBJC_ARC = YES; 468 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 469 | CLANG_WARN_BOOL_CONVERSION = YES; 470 | CLANG_WARN_COMMA = YES; 471 | CLANG_WARN_CONSTANT_CONVERSION = YES; 472 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 473 | CLANG_WARN_EMPTY_BODY = YES; 474 | CLANG_WARN_ENUM_CONVERSION = YES; 475 | CLANG_WARN_INFINITE_RECURSION = YES; 476 | CLANG_WARN_INT_CONVERSION = YES; 477 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 478 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 479 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 480 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 481 | CLANG_WARN_STRICT_PROTOTYPES = YES; 482 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 483 | CLANG_WARN_UNREACHABLE_CODE = YES; 484 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 485 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 486 | COPY_PHASE_STRIP = NO; 487 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 488 | ENABLE_NS_ASSERTIONS = NO; 489 | ENABLE_STRICT_OBJC_MSGSEND = YES; 490 | GCC_C_LANGUAGE_STANDARD = gnu99; 491 | GCC_NO_COMMON_BLOCKS = YES; 492 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 493 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 494 | GCC_WARN_UNDECLARED_SELECTOR = YES; 495 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 496 | GCC_WARN_UNUSED_FUNCTION = YES; 497 | GCC_WARN_UNUSED_VARIABLE = YES; 498 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 499 | MTL_ENABLE_DEBUG_INFO = NO; 500 | SDKROOT = iphoneos; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | VALIDATE_PRODUCT = YES; 503 | }; 504 | name = Release; 505 | }; 506 | 97C147061CF9000F007C117D /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 512 | ENABLE_BITCODE = NO; 513 | FRAMEWORK_SEARCH_PATHS = ( 514 | "$(inherited)", 515 | "$(PROJECT_DIR)/Flutter", 516 | ); 517 | INFOPLIST_FILE = Runner/Info.plist; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 519 | LIBRARY_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "$(PROJECT_DIR)/Flutter", 522 | ); 523 | PRODUCT_BUNDLE_IDENTIFIER = com.example.wechat; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | VERSIONING_SYSTEM = "apple-generic"; 526 | }; 527 | name = Debug; 528 | }; 529 | 97C147071CF9000F007C117D /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 532 | buildSettings = { 533 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 534 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 535 | ENABLE_BITCODE = NO; 536 | FRAMEWORK_SEARCH_PATHS = ( 537 | "$(inherited)", 538 | "$(PROJECT_DIR)/Flutter", 539 | ); 540 | INFOPLIST_FILE = Runner/Info.plist; 541 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 542 | LIBRARY_SEARCH_PATHS = ( 543 | "$(inherited)", 544 | "$(PROJECT_DIR)/Flutter", 545 | ); 546 | PRODUCT_BUNDLE_IDENTIFIER = com.example.wechat; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | VERSIONING_SYSTEM = "apple-generic"; 549 | }; 550 | name = Release; 551 | }; 552 | /* End XCBuildConfiguration section */ 553 | 554 | /* Begin XCConfigurationList section */ 555 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 556 | isa = XCConfigurationList; 557 | buildConfigurations = ( 558 | 97C147031CF9000F007C117D /* Debug */, 559 | 97C147041CF9000F007C117D /* Release */, 560 | 249021D3217E4FDB00AE95B9 /* Profile */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | 97C147061CF9000F007C117D /* Debug */, 569 | 97C147071CF9000F007C117D /* Release */, 570 | 249021D4217E4FDB00AE95B9 /* Profile */, 571 | ); 572 | defaultConfigurationIsVisible = 0; 573 | defaultConfigurationName = Release; 574 | }; 575 | /* End XCConfigurationList section */ 576 | }; 577 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 578 | } 579 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/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 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | wechat 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/common/style/style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wechat/pages/component/full_with_icon_button.dart'; 3 | 4 | class AppColors { 5 | static const APPBarColor = 0xffededed; 6 | static const APPBarTextColor = 0xff010101; 7 | static const APPCardColor = 0xff303030; 8 | static const TabIconNormal = 0xff999999; 9 | static const TabIconActive = 0xff46c11b; 10 | static const AppBarPopupMenuTextColor = 0xffffffff; 11 | static const TitleColor = 0xff353535; 12 | static const ConversationItemBg = 0xffffffff; 13 | static const DesTextColor = 0xff9e9e9e; 14 | static const DividerColor = 0xffd9d9d9; 15 | static const NotifyDotBg = 0xffff3e3e; 16 | static const NotifyDotText = 0xffffffff; 17 | static const ConversationMuteIcon = 0xffd8d8d8; 18 | static const DeviceInfoItemBg = 0xfff5f5f5; 19 | static const DeviceInfoItemText = 0xff606062; 20 | static const PrimaryColor = 0xffebebeb; 21 | static const BackgroundColor = 0xffededed; 22 | static const AppBarColor = 0xffededed; 23 | static const ActionIconColor = 0xff000000; 24 | static const ActionMenuBgColor = 0xff4c4c4c; 25 | static const CardBgColor = 0xffffffff; 26 | static const AppBarPopupMenuColor = 0xffffffff; 27 | static const DeviceInfoItemIcon = 0xff606062; 28 | static const ContactGroupTitleBg = 0xffebebeb; 29 | static const ContactGroupTitleText = 0xff888888; 30 | static const IndexLetterBoxBg = Colors.black45; 31 | static const HeaderCardBg = Colors.white; 32 | static const HeaderCardTitleText = 0xff353535; 33 | static const HeaderCardDesText = 0xff7f7f7f; 34 | static const ButtonDesText = 0xff8c8c8c; 35 | static const ButtonArrowColor = 0xffadadad; 36 | static const NewTagBg = 0xfffa5251; 37 | static const FullWithIconButton = 0xff3d3d3d; 38 | static const KeyboardArrowRight = 0xffacacac; 39 | static const TextBobuleRight = 0xff9def71; 40 | static const TextBobuleLeft = 0xffffffff; 41 | static const TextBobule = 0xff3e3e3e; 42 | static const ChatDetailBg = 0xffefefef; 43 | static const ChatTime = 0xffababab; 44 | } 45 | 46 | 47 | class ICons { 48 | static const String FONT_FAMILY = 'wxIconFont'; 49 | 50 | static const IconData MESSAGE = const IconData( 51 | 0xe622, fontFamily: ICons.FONT_FAMILY); 52 | 53 | static const IconData ADDRESSLIST = const IconData( 54 | 0xe648, fontFamily: ICons.FONT_FAMILY); 55 | 56 | static const IconData DISCOVER = const IconData( 57 | 0xe613, fontFamily: ICons.FONT_FAMILY); 58 | 59 | static const IconData MINE = const IconData( 60 | 0xe670, fontFamily: ICons.FONT_FAMILY); 61 | 62 | static const IconData MESSAGE_ACTIVE = const IconData( 63 | 0xe620, fontFamily: ICons.FONT_FAMILY); 64 | 65 | static const IconData ADDRESSLIST_ACTIVE = const IconData( 66 | 0xe603, fontFamily: ICons.FONT_FAMILY); 67 | 68 | static const IconData DISCOVER_ACTIVE = const IconData( 69 | 0xe600, fontFamily: ICons.FONT_FAMILY); 70 | 71 | static const IconData MINE_ACTIVE = const IconData( 72 | 0xe601, fontFamily: ICons.FONT_FAMILY); 73 | 74 | static const IconData QR_SCAN = const IconData( 75 | 0xe634, fontFamily: ICons.FONT_FAMILY); 76 | 77 | static const IconData GROUP_CHAT = const IconData( 78 | 0xe620, fontFamily: ICons.FONT_FAMILY); 79 | 80 | static const IconData ADD_FRIEND = const IconData( 81 | 0xe624, fontFamily: ICons.FONT_FAMILY); 82 | 83 | static const IconData PAYMENT = const IconData( 84 | 0xe602, fontFamily: ICons.FONT_FAMILY); 85 | 86 | static const IconData HELP = const IconData( 87 | 0xe63b, fontFamily: ICons.FONT_FAMILY); 88 | 89 | static const IconData MUTE_ICON = const IconData( 90 | 0xe75e, fontFamily: ICons.FONT_FAMILY); 91 | 92 | static const IconData MAC = const IconData( 93 | 0xe673, fontFamily: ICons.FONT_FAMILY); 94 | 95 | static const IconData WINDOWS = const IconData( 96 | 0xe64f, fontFamily: ICons.FONT_FAMILY); 97 | 98 | static const IconData SEARCH = const IconData( 99 | 0xe63e, fontFamily: ICons.FONT_FAMILY); 100 | 101 | static const IconData ADD = const IconData( 102 | 0xe6d3, fontFamily: ICons.FONT_FAMILY); 103 | 104 | static const IconData ER_CODE = const IconData( 105 | 0xe646, fontFamily: ICons.FONT_FAMILY); 106 | 107 | static const IconData RIGHT = const IconData( 108 | 0xe60b, fontFamily: ICons.FONT_FAMILY); 109 | 110 | static const IconData MENUS = const IconData( 111 | 0xe60e, fontFamily: ICons.FONT_FAMILY); 112 | 113 | static const IconData FACES = const IconData( 114 | 0xe88f, fontFamily: ICons.FONT_FAMILY); 115 | 116 | static const IconData VOICE = const IconData( 117 | 0xe606, fontFamily: ICons.FONT_FAMILY); 118 | 119 | } 120 | 121 | class Constants{ 122 | static const String MENU_MARK_AS_UNREAD = 'MENU_MARK_AS_UNREAD'; 123 | static const String MENU_MARK_AS_UNREAD_VALUE = '标为未读'; 124 | static const String MENU_PIN_TO_TOP = 'MENU_PIN_TO_TOP'; 125 | static const String MENU_PIN_TO_TOP_VALUE = '置顶聊天'; 126 | static const String MENU_DELETE_CONVERSATION = 'MENU_DELETE_CONVERSATION'; 127 | static const String MENU_DELETE_CONVERSATION_VALUE = '删除该聊天'; 128 | static const String MENU_PIN_PA_TO_TOP = 'MENU_PIN_PA_TO_TOP'; 129 | static const String MENU_PIN_PA_TO_TOP_VALUE = '置顶公众号'; 130 | static const String MENU_UNSUBSCRIBE = 'MENU_UNSUBSCRIBE'; 131 | static const String MENU_UNSUBSCRIBE_VALUE = '取消关注'; 132 | } 133 | 134 | 135 | class MessageDetailSelects{ 136 | static const String MENU_COPY = 'MENU_COPY'; 137 | static const String MENU_COPY_VALUE = '复制'; 138 | static const String MENU_SHARE_FRIENDS = 'MENU_SHARE_FRIENDS'; 139 | static const String MENU_SHARE_FRIENDS_VALUE = '发送给朋友'; 140 | static const String MENU_FAVORIITE = 'MENU_MENU_FAVORIITE'; 141 | static const String MENU_FAVORIITE_VALUE = '收藏'; 142 | static const String MENU_REMIND = 'MENU_REMIND'; 143 | static const String MENU_REMIND_VALUE = '提醒'; 144 | static const String MENU_TRANSLATE = 'MENU_TRANSLATE'; 145 | static const String MENU_TRANSLATE_VALUE = '翻译'; 146 | static const String MENU_DELATE = 'MENU_DELATE'; 147 | static const String MENU_DELATE_VALUE = '删除'; 148 | static const String MENU_MULTIPE_CHOICE = 'MENU_MULTIPE_CHOICE'; 149 | static const String MENU_MULTIPE_CHOICE_VALUE = '多选'; 150 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import './pages/index_page.dart'; 3 | import 'package:provide/provide.dart'; 4 | import './provide/currentIndex.dart'; 5 | import './provide/websocket.dart'; 6 | import './common/style/style.dart' show AppColors; 7 | import 'package:fluro/fluro.dart'; 8 | import './routers/routers.dart'; 9 | import './routers/application.dart'; 10 | void main() { 11 | var providers = Providers(); 12 | var currentIndexProvide = CurrentIndexProvide(); 13 | var websocketProvide = WebSocketProvide(); 14 | providers 15 | ..provide(Provider.value(currentIndexProvide)) 16 | ..provide(Provider.value(websocketProvide)); 17 | runApp(ProviderNode(child:MyApp(),providers: providers)); 18 | } 19 | 20 | class MyApp extends StatelessWidget { 21 | // This widget is the root of your application. 22 | @override 23 | Widget build(BuildContext context) { 24 | final router = Router(); 25 | Routers.configureRouters(router); 26 | Application.router = router; 27 | Provide.value(context).init(); 28 | return Container( 29 | child:MaterialApp( 30 | title: '微信', 31 | theme: ThemeData.light().copyWith( 32 | primaryColor: Color(AppColors.PrimaryColor), 33 | cardColor: Color(AppColors.CardBgColor), 34 | backgroundColor: Color(AppColors.BackgroundColor), 35 | ), 36 | home: IndexPage(), 37 | ) 38 | ); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /lib/model/contacts.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Contact{ 4 | String avatar; 5 | String name; 6 | String nameIndex; 7 | VoidCallback onPressed; 8 | 9 | bool isAvatarFromNet(){ 10 | if(this.avatar.indexOf('http') == 0 || this.avatar.indexOf('https') == 0) { 11 | return true; 12 | } 13 | return false; 14 | } 15 | 16 | Contact({ 17 | this.avatar, 18 | this.name, 19 | this.nameIndex, 20 | this.onPressed, 21 | }): assert(avatar != null), 22 | assert(name != null); 23 | 24 | static List contacts =[ 25 | new Contact( 26 | avatar: 'https://randomuser.me/api/portraits/men/53.jpg', 27 | name: 'Maurice Sutton', 28 | nameIndex: 'M', 29 | ), 30 | new Contact( 31 | avatar: 'https://randomuser.me/api/portraits/women/76.jpg', 32 | name: 'Jerry', 33 | nameIndex: 'J', 34 | ), 35 | new Contact( 36 | avatar: 'https://randomuser.me/api/portraits/women/17.jpg', 37 | name: 'Dangdang', 38 | nameIndex: 'D', 39 | ), 40 | new Contact( 41 | avatar: 'https://randomuser.me/api/portraits/women/55.jpg', 42 | name: 'Teddy', 43 | nameIndex: 'T', 44 | ), 45 | new Contact( 46 | avatar: 'https://randomuser.me/api/portraits/women/11.jpg', 47 | name: 'Steave', 48 | nameIndex: 'S', 49 | ), 50 | new Contact( 51 | avatar: 'https://randomuser.me/api/portraits/women/65.jpg', 52 | name: 'Vivian', 53 | nameIndex: 'V', 54 | ), 55 | new Contact( 56 | avatar: 'https://randomuser.me/api/portraits/women/50.jpg', 57 | name: 'Mary', 58 | nameIndex: 'M', 59 | ), 60 | new Contact( 61 | avatar: 'https://randomuser.me/api/portraits/women/91.jpg', 62 | name: 'David', 63 | nameIndex: 'D', 64 | ), 65 | new Contact( 66 | avatar: 'https://randomuser.me/api/portraits/women/60.jpg', 67 | name: 'Bob', 68 | nameIndex: 'B', 69 | ), 70 | new Contact( 71 | avatar: 'https://randomuser.me/api/portraits/men/60.jpg', 72 | name: 'Jeff Green', 73 | nameIndex: 'J', 74 | ), 75 | new Contact( 76 | avatar: 'https://randomuser.me/api/portraits/men/45.jpg', 77 | name: 'Adam', 78 | nameIndex: 'A', 79 | ), 80 | new Contact( 81 | avatar: 'https://randomuser.me/api/portraits/men/7.jpg', 82 | name: 'Michel', 83 | nameIndex: 'M', 84 | ), 85 | new Contact( 86 | avatar: 'https://randomuser.me/api/portraits/men/35.jpg', 87 | name: 'Green', 88 | nameIndex: 'G', 89 | ), 90 | new Contact( 91 | avatar: 'https://randomuser.me/api/portraits/men/64.jpg', 92 | name: 'Jack Ma', 93 | nameIndex: 'J', 94 | ), 95 | new Contact( 96 | avatar: 'https://randomuser.me/api/portraits/men/86.jpg', 97 | name: 'Tom', 98 | nameIndex: 'T', 99 | ), 100 | new Contact( 101 | avatar: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537868900176&di=ddbe94a75a3cc33f880a5f3f675b8acd&imgtype=0&src=http%3A%2F%2Fs2.sinaimg.cn%2Fmw690%2F003wRTwMty6IGZWzd2p31', 102 | name: '张伟', 103 | nameIndex: 'Z', 104 | ), 105 | new Contact( 106 | avatar: 'https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1537858866&di=fe35e4465c73122f14e1c9475dd68e47&src=http://a2.att.hudong.com/63/26/01300001128119143503262347361.jpg', 107 | name: '张益达', 108 | nameIndex: 'Z', 109 | ), 110 | new Contact( 111 | avatar: 'https://randomuser.me/api/portraits/men/86.jpg', 112 | name: '01234', 113 | nameIndex: '#', 114 | ), 115 | ]; 116 | } 117 | 118 | class ContactEventItem{ 119 | String avatar; 120 | String name; 121 | VoidCallback onPressed; 122 | 123 | ContactEventItem({ 124 | @required this.avatar, 125 | @required this.name, 126 | @required this.onPressed, 127 | }); 128 | } -------------------------------------------------------------------------------- /lib/model/conversation.dart: -------------------------------------------------------------------------------- 1 | import '../common/style/style.dart'; 2 | 3 | class Conversation { 4 | String avatar; 5 | String title; 6 | int titleColor; 7 | String des; 8 | String updateAt; 9 | bool isMute; 10 | int unreadMsgCount; 11 | bool displayDot; 12 | int groupId; 13 | String userId; 14 | int type; 15 | 16 | bool isAvatarFromNet() { 17 | if(this.avatar.indexOf('http') == 0 || this.avatar.indexOf('https') == 0) { 18 | return true; 19 | } 20 | return false; 21 | } 22 | 23 | Conversation({ 24 | this.avatar, 25 | this.title, 26 | this.titleColor : AppColors.TitleColor, 27 | this.des, 28 | this.updateAt, 29 | this.isMute : false, 30 | this.unreadMsgCount : 0, 31 | this.displayDot : false, 32 | this.groupId, 33 | this.userId, 34 | this.type 35 | }) : assert(avatar != null), 36 | assert(title != null), 37 | assert(updateAt != null); 38 | 39 | static List mockConversations = [ 40 | new Conversation( 41 | avatar: 'assets/images/ic_file_transfer.png', 42 | title: '[模拟数据]文件传输助手', 43 | des: '[模拟数据]', 44 | updateAt: '19:56', 45 | unreadMsgCount: 0, 46 | displayDot: true, 47 | groupId: 000000, 48 | userId:"000000", 49 | type: 1 50 | ), 51 | new Conversation( 52 | avatar: 'assets/images/ic_tx_news.png', 53 | title: '[模拟数据]腾讯新闻', 54 | des: '[模拟数据]豪车与出租车刮擦 俩车主划拳定责', 55 | updateAt: '17:20', 56 | groupId: 000000, 57 | userId:"000000", 58 | type: 1 59 | ), 60 | new Conversation( 61 | avatar: 'assets/images/ic_wx_games.png', 62 | title: '[模拟数据]微信游戏', 63 | titleColor: 0xff586b95, 64 | des: '[模拟数据]25元现金助力开学季!', 65 | updateAt: '17:12', 66 | groupId: 000000, 67 | userId:"000000", 68 | type: 1 69 | ), 70 | new Conversation( 71 | avatar: 'https://randomuser.me/api/portraits/men/10.jpg', 72 | title: '[模拟数据]汤姆丁', 73 | des: '[模拟数据]今晚要一起去吃肯德基吗?', 74 | updateAt: '17:56', 75 | isMute: true, 76 | unreadMsgCount: 0, 77 | groupId: 000000, 78 | userId:"000000", 79 | type: 1 80 | ), 81 | // new Conversation( 82 | // avatar: 'https://randomuser.me/api/portraits/women/10.jpg', 83 | // title: 'Tina Morgan', 84 | // des: '晚自习是什么来着?你知道吗,看到的话赶紧回复我', 85 | // updateAt: '17:58', 86 | // isMute: false, 87 | // unreadMsgCount: 0, 88 | // groupId: 000000, 89 | // userId:"000000", 90 | // type: 1 91 | // ), 92 | // new Conversation( 93 | // avatar: 'assets/images/ic_fengchao.png', 94 | // title: '蜂巢智能柜', 95 | // titleColor: 0xff586b95, 96 | // des: '喷一喷,竟比洗牙还神奇!5秒钟还你一个漂亮洁白的口腔。', 97 | // updateAt: '17:12', 98 | // groupId: 000000, 99 | // userId:"000000", 100 | // type: 1 101 | // ), 102 | // new Conversation( 103 | // avatar: 'https://randomuser.me/api/portraits/women/57.jpg', 104 | // title: 'Lily', 105 | // des: '今天要去运动场锻炼吗?', 106 | // updateAt: '昨天', 107 | // isMute: false, 108 | // unreadMsgCount: 0, 109 | // groupId: 000000, 110 | // userId:"000000", 111 | // type: 1 112 | // ), 113 | // new Conversation( 114 | // avatar: 'https://randomuser.me/api/portraits/men/10.jpg', 115 | // title: '汤姆丁', 116 | // des: '今晚要一起去吃肯德基吗?', 117 | // updateAt: '17:56', 118 | // isMute: true, 119 | // unreadMsgCount: 0, 120 | // ), 121 | // new Conversation( 122 | // avatar: 'https://randomuser.me/api/portraits/women/10.jpg', 123 | // title: 'Tina Morgan', 124 | // des: '晚自习是什么来着?你知道吗,看到的话赶紧回复我', 125 | // updateAt: '17:58', 126 | // isMute: false, 127 | // unreadMsgCount: 0, 128 | // groupId: 000000, 129 | // userId:"000000", 130 | // type: 1 131 | // ), 132 | // new Conversation( 133 | // avatar: 'https://randomuser.me/api/portraits/women/57.jpg', 134 | // title: 'Lily', 135 | // des: '今天要去运动场锻炼吗?', 136 | // updateAt: '昨天', 137 | // isMute: false, 138 | // unreadMsgCount: 0, 139 | // groupId: 000000, 140 | // userId:"000000", 141 | // type: 1 142 | // ), 143 | // new Conversation( 144 | // avatar: 'https://randomuser.me/api/portraits/men/10.jpg', 145 | // title: '汤姆丁', 146 | // des: '今晚要一起去吃肯德基吗?', 147 | // updateAt: '17:56', 148 | // isMute: true, 149 | // unreadMsgCount: 0, 150 | // groupId: 000000, 151 | // userId:"000000", 152 | // type: 1 153 | // ), 154 | // new Conversation( 155 | // avatar: 'https://randomuser.me/api/portraits/women/10.jpg', 156 | // title: 'Tina Morgan', 157 | // des: '晚自习是什么来着?你知道吗,看到的话赶紧回复我', 158 | // updateAt: '17:58', 159 | // isMute: false, 160 | // unreadMsgCount: 0, 161 | // groupId: 000000, 162 | // userId:"000000", 163 | // type: 1 164 | // ), 165 | // new Conversation( 166 | // avatar: 'https://randomuser.me/api/portraits/women/57.jpg', 167 | // title: 'Lily', 168 | // des: '今天要去运动场锻炼吗?', 169 | // updateAt: '昨天', 170 | // isMute: false, 171 | // unreadMsgCount: 0, 172 | // groupId: 000000, 173 | // userId:"000000", 174 | // type: 1 175 | // ) 176 | ]; 177 | } -------------------------------------------------------------------------------- /lib/model/me.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class Profile { 4 | String name; 5 | String avatar; 6 | String account; 7 | 8 | Profile({ 9 | this.name, 10 | this.account, 11 | this.avatar 12 | }); 13 | } 14 | 15 | Profile me = new Profile( 16 | name: 'wkiwi', 17 | avatar:'https://avatars3.githubusercontent.com/u/35719531?s=460&v=4', 18 | account: 'WZ610935700' 19 | ); -------------------------------------------------------------------------------- /lib/pages/chat_detail/chat_content_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import '../../common/style/style.dart' show AppColors,Constants,ICons,MessageDetailSelects; 4 | import '../component/user_avatat.dart'; 5 | 6 | class ChatContentView extends StatelessWidget { 7 | int type; //0 代表对方 , 1代表自己 8 | String text;//聊天内容 9 | String avatar;//头像url 10 | String username;//昵称 11 | int userType;//聊天类型 2群组 1单聊 12 | bool isNetwork; 13 | ChatContentView({Key key, this.type, this.text,this.avatar,this.isNetwork,this.username,this.userType}) 14 | : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | var tapPos; 19 | //头像组件 20 | Widget userAvatar = UserAvatar( 21 | padding: EdgeInsets.only( 22 | top: ScreenUtil().setHeight(5), 23 | right: (type == 0 ? 0.0 : ScreenUtil().setWidth(15)), 24 | left: (type == 0 ? ScreenUtil().setWidth(15) : 0.0) 25 | ), 26 | width: ScreenUtil().setWidth(80), 27 | height: ScreenUtil().setWidth(80), 28 | image: avatar!='' ? avatar: 'assets/images/ic_public_account.png', 29 | isNetwork: isNetwork, 30 | onPressed: () {print('点击头像');} 31 | ); 32 | 33 | Widget userNameWidget = Container( 34 | margin: EdgeInsets.only(left: type == 0 ? ScreenUtil().setWidth(20) : 0,bottom: ScreenUtil().setHeight(10),right: type == 0 ? 0: ScreenUtil().setWidth(20)), 35 | child: Text(username,style: TextStyle(color: Color(AppColors.ChatTime),fontSize: ScreenUtil().setSp(23.0)),), 36 | ); 37 | _showMenu(BuildContext context,Offset tapPos){ 38 | final RenderBox overlay =Overlay.of(context).context.findRenderObject(); 39 | final RelativeRect position = RelativeRect.fromLTRB( 40 | tapPos.dx, tapPos.dy, 41 | overlay.size.width - tapPos.dx, 42 | overlay.size.height - tapPos.dy 43 | ); 44 | showMenu( 45 | context: context, 46 | position: position, 47 | items: >[ 48 | PopupMenuItem( 49 | child: Text(MessageDetailSelects.MENU_COPY_VALUE), 50 | value: MessageDetailSelects.MENU_COPY, 51 | ), 52 | PopupMenuItem( 53 | child: Text(MessageDetailSelects.MENU_SHARE_FRIENDS_VALUE), 54 | value: MessageDetailSelects.MENU_SHARE_FRIENDS, 55 | ), 56 | PopupMenuItem( 57 | child: Text(MessageDetailSelects.MENU_FAVORIITE_VALUE), 58 | value: MessageDetailSelects.MENU_FAVORIITE, 59 | ), 60 | PopupMenuItem( 61 | child: Text(MessageDetailSelects.MENU_REMIND_VALUE), 62 | value: MessageDetailSelects.MENU_REMIND, 63 | ), 64 | PopupMenuItem( 65 | child: Text(MessageDetailSelects.MENU_TRANSLATE_VALUE), 66 | value: MessageDetailSelects.MENU_TRANSLATE, 67 | ), 68 | PopupMenuItem( 69 | child: Text(MessageDetailSelects.MENU_DELATE_VALUE), 70 | value: MessageDetailSelects.MENU_DELATE, 71 | ), 72 | PopupMenuItem( 73 | child: Text(MessageDetailSelects.MENU_MULTIPE_CHOICE_VALUE), 74 | value: MessageDetailSelects.MENU_MULTIPE_CHOICE, 75 | ), 76 | ] 77 | ).then((String selected) { 78 | switch(selected){ 79 | default: 80 | print('当前选中的是:$selected'); 81 | } 82 | }); 83 | } 84 | Widget messageTextWidget = InkWell( 85 | onTapDown: (TapDownDetails details) { 86 | tapPos = details.globalPosition; 87 | }, 88 | onLongPress: (){ 89 | _showMenu(context,tapPos); 90 | print('弹出会话菜单'); 91 | }, 92 | child: Container( 93 | margin: type == 0 ? EdgeInsets.only(left:ScreenUtil().setWidth(20),right: ScreenUtil().setWidth(115)) :EdgeInsets.only(left:ScreenUtil().setWidth(115),right: ScreenUtil().setWidth(20)), 94 | child: Text(text, 95 | style: TextStyle(fontSize: ScreenUtil().setSp(30.0),color: Color(AppColors.TextBobule),height: 1.3), 96 | ), 97 | padding: EdgeInsets.only(left:ScreenUtil().setWidth(15),right:ScreenUtil().setWidth(15),bottom:ScreenUtil().setHeight(15),top:ScreenUtil().setHeight(15)), 98 | decoration: BoxDecoration( 99 | borderRadius: BorderRadius.circular(5.0), 100 | color: type == 0 ? Color(AppColors.TextBobuleLeft) : Color(AppColors.TextBobuleRight), 101 | ), 102 | ), 103 | ); 104 | 105 | final List nameAndText = [ 106 | userNameWidget, 107 | messageTextWidget 108 | ]; 109 | final List onlyText = [ 110 | messageTextWidget 111 | ]; 112 | textBubble(){ 113 | return Expanded( 114 | child: Column( 115 | crossAxisAlignment: type == 0 ? CrossAxisAlignment.start : CrossAxisAlignment.end, 116 | children: userType == 2 && type == 0 ? nameAndText : onlyText, 117 | ) 118 | ); 119 | } 120 | return Container( 121 | margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(10.0),top: ScreenUtil().setHeight(10.0)), 122 | child: type == 0 ? 123 | Row( 124 | crossAxisAlignment: CrossAxisAlignment.start, 125 | children: [ 126 | userAvatar, 127 | textBubble() 128 | ], 129 | ) : 130 | Row( 131 | crossAxisAlignment: CrossAxisAlignment.start, 132 | children: [ 133 | textBubble(), 134 | userAvatar, 135 | ], 136 | ) , 137 | ); 138 | 139 | } 140 | } -------------------------------------------------------------------------------- /lib/pages/chat_detail_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provide/provide.dart'; 3 | import '../provide/websocket.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import '../common/style/style.dart'; 6 | import './chat_detail/chat_content_view.dart'; 7 | import '../model/conversation.dart'; 8 | 9 | 10 | class ChatDetailPage extends StatefulWidget { 11 | int type; 12 | int index; 13 | ChatDetailPage(this.index,this.type); 14 | @override 15 | _ChatDetailPageState createState() => _ChatDetailPageState(type,index); 16 | } 17 | 18 | class _ChatDetailPageState extends State { 19 | ScrollController _scrollController; 20 | bool hasText = false; 21 | int type; 22 | int index; 23 | Conversation data; 24 | _ChatDetailPageState(this.type,this.index); 25 | var messageList = [ 26 | {'type':0,'text':'hello',}, 27 | {'type':1,'text':'hello',}, 28 | {'type':0,'text':'Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。',}, 29 | {'type':1,'text':'它也是构建未来的Google Fuchsia [1] 应用的主要方式。',}, 30 | {'type':0,'text':'Flutter是谷歌的移动UI框架 [4] ,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。它也是构建未来的Google Fuchsia [1] 应用的主要方式。',}, 31 | {'type':1,'text':'Flutter组件采用现代响应式框架构建,这是从React中获得的灵感,中心思想是用组件(widget)构建你的UI。 组件描述了在给定其当前配置和状态时他们显示的样子。当组件状态改变,组件会重构它的描述(description),Flutter会对比之前的描述, 以确定底层渲染树从当前状态转换到下一个状态所需要的最小更改。',}, 32 | {'type':0,'text':'Flutter的第一个版本被称为“Sky”,运行在Android操作系统上。它是在2015年Dart开发者峰会 [3] 上亮相的,其目的是能够以每秒120帧的速度持续渲染。',}, 33 | {'type':1,'text':'runApp函数接收给定的组件(Widget)并使其成为组件树的根。 在此例中,组件树由两个组件构成,Center组件和它的子组件-Text组件。框架强制根组件覆盖整个屏幕,这意味着“Hello, world”文本在屏幕上居中显示。需要注意的是,在上面的Text实例中必须指定文本显示方向。不必担心,当使用MaterialApp时,它会帮你自动解决这些小事情,稍后将进行演示。',}, 34 | {'type':0,'text':'在编写app时,通常会创建新组件,是继承无状态的StatelessWidget还是有状态的StatefulWidget, 取决于您的组件是否需要管理状态。组件的主要工作是实现一个build函数,它使用其他低级别的组件来构建自己。Flutter框架将依次构建这些组件,最终会到达代表底层渲染对象的组件-RenderObject,它会计算并描述组件的几何形状。',}, 35 | ]; 36 | 37 | final controller = TextEditingController(); 38 | void _handleSubmitted(String text) { 39 | if (controller.text.length > 0) { 40 | print('发送${text}'); 41 | if(type == 1){ 42 | Provide.value(context).sendMessage(2,text,index); 43 | } 44 | setState(() { 45 | hasText = false; 46 | messageList.add({'type':1,'text':text,}); 47 | }); 48 | controller.clear(); //清空输入框 49 | _jumpBottom(); 50 | } 51 | } 52 | void _jumpBottom(){//滚动到底部 53 | _scrollController.animateTo(99999,curve: Curves.easeOut, duration: Duration(milliseconds: 200)); 54 | } 55 | @override 56 | void initState(){ 57 | super.initState(); 58 | _scrollController = new ScrollController(); 59 | // _jumpBottom(); 60 | } 61 | @override 62 | Widget build(BuildContext context) { 63 | if(type ==1){ 64 | data = Provide.value(context).messageList[index]; 65 | }else{ 66 | data = Conversation.mockConversations[index]; 67 | } 68 | return Scaffold( 69 | appBar: AppBar( 70 | centerTitle:false, 71 | title: Text(data.title,style: TextStyle(fontSize: ScreenUtil().setSp(30.0),color: Color(AppColors.APPBarTextColor),),), 72 | iconTheme: IconThemeData( 73 | color: Color(AppColors.APPBarTextColor) 74 | ), 75 | elevation: 0.0, 76 | brightness: Brightness.light, 77 | backgroundColor: Color(AppColors.PrimaryColor), 78 | actions: [ 79 | Container( 80 | child: IconButton( 81 | icon: Icon(ICons.MENUS,color: Color(AppColors.APPBarTextColor),), 82 | onPressed: (){ 83 | print('点击了聊天信息界面'); 84 | }, 85 | ), 86 | ), 87 | ] 88 | ), 89 | body: Container( 90 | color: Color(AppColors.ChatDetailBg), 91 | child: Column( 92 | children: [ 93 | Provide( 94 | builder: (context,child,val){ 95 | List>list = []; 96 | if(type == 1){ 97 | messageList = []; 98 | var historyMessage = Provide.value(context).historyMessage; 99 | for(var i = 0; i< historyMessage.length; i++){ 100 | if(data.userId != null){ 101 | if(historyMessage[i]['bridge'].contains(data.userId)){ 102 | if(historyMessage[i]['uid'] == data.userId){ 103 | list.add({'type':0,'text':historyMessage[i]['msg'],'nickname':historyMessage[i]['nickname']}); 104 | }else{ 105 | list.add({'type':1,'text':historyMessage[i]['msg'],'nickname':historyMessage[i]['nickname']}); 106 | } 107 | } 108 | }else if(data.groupId != null && data.groupId == historyMessage[i]['groupId'] && historyMessage[i]['bridge'].length==0){ 109 | var uid = Provide.value(context).uid; 110 | if(historyMessage[i]['uid'] != uid ){ 111 | list.add({'type':0,'text':historyMessage[i]['msg'],'nickname':historyMessage[i]['nickname']}); 112 | }else{ 113 | list.add({'type':1,'text':historyMessage[i]['msg'],'nickname':historyMessage[i]['nickname']}); 114 | } 115 | } 116 | } 117 | } 118 | return Expanded( 119 | child: ListView.builder( 120 | controller: _scrollController, 121 | physics: ClampingScrollPhysics(), 122 | itemBuilder: (BuildContext context, int index) { 123 | if(type == 1){ 124 | return ChatContentView(type:list[index]['type'],text:list[index]['text'],avatar:list[index]['type'] == 0 ? data.avatar: '',isNetwork: list[index]['type'] == 0 ? data.isAvatarFromNet() : false,username:list[index]['nickname'],userType:data.type); 125 | }else{ 126 | return ChatContentView(type:messageList[index]['type'],text:messageList[index]['text'],avatar:messageList[index]['type'] == 0 ? data.avatar: '',isNetwork: messageList[index]['type'] == 0 ? data.isAvatarFromNet() : false,username:data.title,userType:data.type); 127 | } 128 | }, 129 | itemCount:type == 1 ? list.length : messageList.length , 130 | ) 131 | ); 132 | }), 133 | Container( 134 | padding: EdgeInsets.only(top: ScreenUtil().setHeight(2.0), bottom:ScreenUtil().setHeight(2.0),left: 0,right: 0), 135 | color: Color(0xFFF7F7F7), 136 | child: Row( 137 | children: [ 138 | Container( 139 | width: ScreenUtil().setWidth(60.0), 140 | margin: EdgeInsets.only(right: ScreenUtil().setWidth(20.0)), 141 | child: IconButton( 142 | icon: Icon(ICons.VOICE), 143 | onPressed: () { 144 | print('切换到语音'); 145 | } 146 | ), 147 | ), 148 | Expanded( 149 | child: Container( 150 | padding: EdgeInsets.only(top: ScreenUtil().setHeight(15.0), bottom: ScreenUtil().setHeight(15.0)), 151 | height: ScreenUtil().setHeight(60.0), 152 | decoration: BoxDecoration( 153 | borderRadius: BorderRadius.all(Radius.circular(5.0)), 154 | color: Colors.white 155 | ), 156 | child: TextField( 157 | controller: controller, 158 | decoration: InputDecoration.collapsed(hintText: null), 159 | maxLines: 1, 160 | autocorrect: true, 161 | autofocus: false, 162 | textAlign: TextAlign.start, 163 | style: TextStyle(color: Colors.black), 164 | cursorColor: Colors.green, 165 | onChanged: (text) { 166 | setState(() { 167 | hasText = text.length > 0 ? true : false; 168 | }); 169 | print('change=================== $text'); 170 | }, 171 | onSubmitted:_handleSubmitted, 172 | enabled: true, //是否禁用 173 | ), 174 | )), 175 | Container( 176 | width: ScreenUtil().setWidth(60.0), 177 | child: IconButton( 178 | icon: Icon(ICons.FACES), //发送按钮图标 179 | onPressed: () { 180 | print('打开表情面板'); 181 | } 182 | ), 183 | ), 184 | Container( 185 | width: ScreenUtil().setWidth(60.0), 186 | margin: EdgeInsets.only(right: ScreenUtil().setWidth(20.0)), 187 | child: IconButton( 188 | //发送按钮或者+按钮 189 | icon: hasText ? Icon(Icons.send) :Icon(ICons.ADD), //发送按钮图标 190 | onPressed: () { 191 | if(!hasText){ 192 | print('打开功能面板'); 193 | }else{ 194 | _handleSubmitted(controller.text); 195 | } 196 | } 197 | ), 198 | ) 199 | ], 200 | ), 201 | ) 202 | ], 203 | ), 204 | ), 205 | ); 206 | } 207 | } 208 | 209 | -------------------------------------------------------------------------------- /lib/pages/component/404.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class WidgetNotFound extends StatelessWidget { 4 | Widget build(BuildContext context) { 5 | return Scaffold( 6 | appBar: AppBar( 7 | elevation: 0.0, 8 | title: Text("widget not found"), 9 | ), 10 | body: Center( 11 | child: Text("widget not found") 12 | ) 13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /lib/pages/component/full_with_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import '../../common/style/style.dart' show ICons,AppColors; 5 | class FullWithIconButton extends StatelessWidget { 6 | const FullWithIconButton({ 7 | this.title, 8 | this.iconPath, 9 | this.onPressed, 10 | this.showDivider, 11 | this.description 12 | }): assert (title != null), 13 | assert (iconPath != null), 14 | assert (onPressed != null); 15 | 16 | final bool showDivider; 17 | final String iconPath; 18 | final String title; 19 | final VoidCallback onPressed; 20 | final String description; 21 | @override 22 | Widget build(BuildContext context) { 23 | return FlatButton( 24 | padding: EdgeInsets.only(right: 0.0,left: ScreenUtil().setWidth(30.0)), 25 | onPressed: (){onPressed();}, 26 | color: Colors.white, 27 | child: Row( 28 | crossAxisAlignment: CrossAxisAlignment.center, 29 | children: [ 30 | Image.asset( 31 | iconPath, 32 | width: ScreenUtil().setWidth(50.0), 33 | height: ScreenUtil().setWidth(50.0), 34 | ), 35 | SizedBox(width: ScreenUtil().setWidth(35.0),), 36 | Expanded( 37 | child: Container( 38 | height: ScreenUtil().setHeight(80.0), 39 | alignment: Alignment.centerLeft, 40 | decoration: BoxDecoration( 41 | border: Border( 42 | bottom: BorderSide(width: 0.5,color: showDivider ? Colors.black12 : Colors.white) 43 | ) 44 | ), 45 | child: Row( 46 | children: [ 47 | Expanded( 48 | child:Text(title,style: TextStyle(fontSize: ScreenUtil().setSp(30.0),color: Color(AppColors.FullWithIconButton)),), 49 | ), 50 | Icon(ICons.RIGHT,color: Color(AppColors.KeyboardArrowRight),), 51 | SizedBox(width: ScreenUtil().setWidth(25.0),) 52 | ], 53 | ), 54 | ), 55 | ) 56 | ], 57 | ), 58 | ); 59 | } 60 | } -------------------------------------------------------------------------------- /lib/pages/component/user_avatat.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import '../../common/style/style.dart' show ICons,AppColors; 5 | 6 | class UserAvatar extends StatelessWidget { 7 | final bool isNetwork; 8 | final String image; 9 | final VoidCallback onPressed; 10 | final double width; 11 | final double height; 12 | final EdgeInsetsGeometry padding; 13 | 14 | UserAvatar( 15 | {this.isNetwork, this.image, this.onPressed, this.width = 30.0, this.height = 30.0, this.padding}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return RawMaterialButton( 20 | materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 21 | padding: padding ?? EdgeInsets.only(top: 4.0, right: 5.0, left: 5.0), 22 | constraints: BoxConstraints(minWidth: 0.0, minHeight: 0.0), 23 | child: ClipRRect( 24 | borderRadius: BorderRadius.all(Radius.circular(5.0)), 25 | child: this.isNetwork ? 26 | FadeInImage.assetNetwork( 27 | placeholder: 'assets/images/ic_public_account.png', 28 | //预览图 29 | fit: BoxFit.fitWidth, 30 | image: image, 31 | width: width, 32 | height: height, 33 | ) 34 | :Image.asset( 35 | image, 36 | fit: BoxFit.cover, 37 | width: width, 38 | height: height, 39 | ), 40 | ), 41 | onPressed: onPressed 42 | ); 43 | } 44 | } -------------------------------------------------------------------------------- /lib/pages/contacts_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import './contacts_page/contact_item.dart'; 5 | import '../model/contacts.dart'; 6 | import '../common/style/style.dart' show ICons,AppColors; 7 | 8 | class ContactsPage extends StatefulWidget { 9 | Color _indexBarBg = Colors.transparent; 10 | String _currentLetter = ''; 11 | @override 12 | _ContactsPageState createState() => _ContactsPageState(); 13 | } 14 | 15 | const INDEX_BAR_WORDS = [ 16 | "↑", "☆", 17 | "A", "B", "C", "D", "E", "F", "G", 18 | "H", "I", "J", "K", "L", "M", "N", 19 | "O", "P", "Q", "R", "S", "T", "U", 20 | "V", "W", "X", "Y", "Z", "#" 21 | ]; 22 | 23 | class _ContactsPageState extends State { 24 | ScrollController _scrollController; 25 | final List data = Contact.contacts; 26 | final List _contacts = []; 27 | final List _functionButtons = [ 28 | new Contact( 29 | avatar: 'assets/images/ic_new_friend.png', 30 | name: '新的朋友', 31 | onPressed: (){ 32 | print('点击了新的朋友'); 33 | }, 34 | ), 35 | new Contact( 36 | avatar: 'assets/images/ic_group_chat.png', 37 | name: '群聊', 38 | onPressed: (){ 39 | print('群聊'); 40 | }, 41 | ), 42 | new Contact( 43 | avatar: 'assets/images/ic_tag.png', 44 | name: '标签', 45 | onPressed: (){ 46 | print('标签'); 47 | }, 48 | ), 49 | new Contact( 50 | avatar: 'assets/images/ic_public_account.png', 51 | name: '公众号', 52 | onPressed: (){ 53 | print('公众号'); 54 | }, 55 | ), 56 | ]; 57 | final Map _letterPosMap = {INDEX_BAR_WORDS[0]: 0.0}; 58 | @override 59 | void initState(){ 60 | super.initState(); 61 | _contacts..addAll(data)..addAll(data)..addAll(data); 62 | _contacts.sort((Contact a, Contact b) => a.nameIndex.compareTo(b.nameIndex)); 63 | _scrollController = new ScrollController(); 64 | //计算indexBar定位关键词列表项的位置 65 | //四个功能选项的高度 66 | var _totalPos = _functionButtons.length * ScreenUtil().setHeight(99.5); 67 | print(_totalPos); 68 | for(int i = 0; i < _contacts.length; i++){ 69 | bool _hasGroupTitle = true; 70 | if(i > 0 && _contacts[i].nameIndex.compareTo(_contacts[i - 1].nameIndex) == 0){ 71 | _hasGroupTitle = false; 72 | } 73 | if(_hasGroupTitle){ 74 | _letterPosMap[_contacts[i].nameIndex] = _totalPos; 75 | _totalPos += ScreenUtil().setHeight(50); 76 | } 77 | _totalPos += ScreenUtil().setHeight(100); 78 | } 79 | print(_letterPosMap); 80 | } 81 | @override 82 | void dispose() { 83 | _scrollController.dispose(); 84 | // TODO: implement dispose 85 | super.dispose(); 86 | } 87 | 88 | String _getLetter(BuildContext context, double tileHeight, Offset globalPos) { 89 | RenderBox _box = context.findRenderObject(); 90 | var local = _box.globalToLocal(globalPos); 91 | int index = (local.dy ~/ tileHeight).clamp(0, INDEX_BAR_WORDS.length - 1); 92 | return INDEX_BAR_WORDS[index]; 93 | } 94 | 95 | void _jumpToIndex(String letter) { 96 | if(_letterPosMap.isNotEmpty) { 97 | final _pos = _letterPosMap[letter]; 98 | if(_pos != null) { 99 | _scrollController.animateTo(_pos, 100 | curve: Curves.easeOut, duration: Duration(milliseconds: 200)); 101 | } 102 | } 103 | } 104 | @override 105 | Widget build(BuildContext context) { 106 | final List _body = [ 107 | ListView.builder( 108 | controller: _scrollController, 109 | itemBuilder: (BuildContext context,index){ 110 | if(index < _functionButtons.length){ 111 | return ContactItem(_functionButtons[index],false); 112 | } 113 | bool _isGroupTitle = true; 114 | int _contactIndex = index - _functionButtons.length; 115 | if(_contactIndex >=1 && _contacts[_contactIndex].nameIndex == _contacts[_contactIndex - 1].nameIndex){ 116 | _isGroupTitle = false; 117 | } 118 | return ContactItem(_contacts[_contactIndex],_isGroupTitle); 119 | }, 120 | itemCount: _contacts.length + _functionButtons.length, 121 | ), 122 | Positioned( 123 | right: 0.0, 124 | top: 0.0, 125 | bottom:0.0, 126 | child: _indexBar(), 127 | ), 128 | ]; 129 | if(widget._currentLetter != null && widget._currentLetter.isNotEmpty){ 130 | _body.add( 131 | Center( 132 | child: Container( 133 | width: ScreenUtil().setWidth(200), 134 | height: ScreenUtil().setWidth(200), 135 | decoration: BoxDecoration( 136 | color: AppColors.IndexLetterBoxBg, 137 | borderRadius: BorderRadius.all(Radius.circular(10)) 138 | ), 139 | child: Center( 140 | child: Text(widget._currentLetter,style: TextStyle(color: Colors.white,fontSize: ScreenUtil().setSp(120)),), 141 | ) 142 | ), 143 | ) 144 | ); 145 | } 146 | return Stack( 147 | children: _body 148 | ); 149 | } 150 | _indexBar(){ 151 | return Container( 152 | color: widget._indexBarBg, 153 | margin: EdgeInsets.only(top: ScreenUtil().setHeight(100),bottom: ScreenUtil().setHeight(100)), 154 | padding: EdgeInsets.only(top: ScreenUtil().setHeight(30),bottom: ScreenUtil().setHeight(30)), 155 | width: ScreenUtil().setWidth(50.0), 156 | child: LayoutBuilder( 157 | builder: _buildIndexBar, 158 | ) 159 | ); 160 | } 161 | Widget _buildIndexBar(BuildContext context,BoxConstraints constraints){ 162 | final List _letters = INDEX_BAR_WORDS.map((String word){ 163 | return Expanded( 164 | child: Text(word), 165 | ); 166 | }).toList(); 167 | final double _totalHeight = constraints.biggest.height; 168 | final double _tileHeight = _totalHeight / _letters.length; 169 | print(_totalHeight); 170 | return GestureDetector( 171 | onVerticalDragDown: (DragDownDetails details){//手指点下去会触发 172 | print('DragDownDetails'); 173 | setState(() { 174 | widget._indexBarBg = Colors.black26; 175 | widget._currentLetter = _getLetter(context,_tileHeight,details.globalPosition); 176 | _jumpToIndex(widget._currentLetter); 177 | }); 178 | }, 179 | onVerticalDragEnd: (DragEndDetails details){//手指滑动后松开会触发 180 | print('DragEndDetails'); 181 | setState(() { 182 | widget._indexBarBg = Colors.transparent; 183 | widget._currentLetter = null; 184 | }); 185 | 186 | // _scrollController.animateTo(_scrollController.position.maxScrollExtent,curve: Curves.easeIn,duration: Duration(milliseconds: 10)); 187 | // _scrollController.jumpTo(_scrollController.position.maxScrollExtent,); 188 | }, 189 | onVerticalDragCancel: (){//手指未滑动,点击后松开 190 | print('onVerticalDragCancel'); 191 | setState(() { 192 | widget._indexBarBg = Colors.transparent; 193 | widget._currentLetter = null; 194 | }); 195 | }, 196 | onVerticalDragUpdate: (DragUpdateDetails details){ 197 | setState(() { 198 | widget._indexBarBg = Colors.black26; 199 | widget._currentLetter = _getLetter(context,_tileHeight,details.globalPosition); 200 | _jumpToIndex(widget._currentLetter); 201 | }); 202 | }, 203 | child: Column( 204 | children: _letters 205 | ), 206 | ); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /lib/pages/contacts_page/contact_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart' as prefix0; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/material.dart' as prefix1; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import '../../model/contacts.dart'; 6 | import '../../common/style/style.dart'; 7 | class ContactItem extends StatelessWidget { 8 | ContactItem(this.contactItemData,this.isGroupTitle) 9 | :assert(contactItemData != null); 10 | 11 | bool isGroupTitle; 12 | final Contact contactItemData; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Column( 17 | children: [ 18 | GroupTitle(), 19 | InkWell( 20 | child: Row( 21 | children: [ 22 | Avatar(), 23 | Title() 24 | ], 25 | ), 26 | onTap: (){ 27 | contactItemData.onPressed(); 28 | }, 29 | ) 30 | ], 31 | ); 32 | } 33 | 34 | Widget GroupTitle(){ 35 | if(isGroupTitle){ 36 | return Container( 37 | child: Text(contactItemData.nameIndex,style: TextStyle(color: Color(AppColors.ContactGroupTitleText)),), 38 | height: ScreenUtil().setHeight(50.0), 39 | width: ScreenUtil().setWidth(750.0), 40 | padding: EdgeInsets.only(left: ScreenUtil().setWidth(25.0)), 41 | alignment: Alignment.centerLeft, 42 | decoration: BoxDecoration( 43 | color: Color(AppColors.ContactGroupTitleBg), 44 | border: BorderDirectional( 45 | top: BorderSide(width: ScreenUtil().setHeight(0.5),color:Color(AppColors.DividerColor)), 46 | bottom: BorderSide(width: ScreenUtil().setHeight(0.5),color:Color(AppColors.DividerColor)) 47 | ) 48 | ), 49 | ); 50 | }else{ 51 | return Container(); 52 | } 53 | } 54 | Widget ClipRRectImg(){ 55 | return ClipRRect( 56 | borderRadius: BorderRadius.circular(5.0), 57 | child: contactItemData.isAvatarFromNet() ? Image.network(contactItemData.avatar,scale: 1.0,) : Image.asset(contactItemData.avatar), 58 | ); 59 | } 60 | Widget Avatar(){ 61 | return Container( 62 | margin: EdgeInsets.only(left: ScreenUtil().setWidth(25.0),right: ScreenUtil().setWidth(25.0),top:ScreenUtil().setWidth(17.0),bottom:ScreenUtil().setWidth(17.0)), 63 | width: ScreenUtil().setWidth(80.0), 64 | height: ScreenUtil().setHeight(66.0), 65 | child: ClipRRectImg(), 66 | decoration: BoxDecoration( 67 | borderRadius: BorderRadius.circular(5.0), 68 | color: Colors.black12, 69 | ), 70 | ); 71 | } 72 | Widget Title(){ 73 | return Expanded( 74 | child: Container( 75 | height: ScreenUtil().setHeight(100.0), 76 | child: Text(contactItemData.name,style: TextStyle(fontSize: ScreenUtil().setSp(30.0),color: Colors.black,fontWeight: FontWeight.w400),), 77 | alignment: Alignment.centerLeft, 78 | decoration: BoxDecoration( 79 | border: Border( 80 | bottom: BorderSide(width: ScreenUtil().setHeight(0.5),color: Color(AppColors.DividerColor)) 81 | ) 82 | ) 83 | ), 84 | ); 85 | } 86 | } -------------------------------------------------------------------------------- /lib/pages/discover_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import './component/full_with_icon_button.dart'; 4 | import '../common/style/style.dart' show ICons,AppColors; 5 | 6 | class DiscoverPage extends StatefulWidget { 7 | @override 8 | _DiscoverPageState createState() => _DiscoverPageState(); 9 | } 10 | 11 | class _DiscoverPageState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return SingleChildScrollView( 15 | child: Column( 16 | children: [ 17 | FullWithIconButton( 18 | iconPath: 'assets/images/ic_social_circle.png', 19 | title: '朋友圈', 20 | showDivider: false, 21 | onPressed: (){}, 22 | ), 23 | SizedBox( 24 | height: ScreenUtil().setHeight(20.0), 25 | ), 26 | FullWithIconButton( 27 | iconPath: 'assets/images/ic_quick_scan.png', 28 | title: '扫一扫', 29 | showDivider: true, 30 | onPressed: (){}, 31 | ),FullWithIconButton( 32 | iconPath: 'assets/images/ic_shake_phone.png', 33 | title: '摇一摇', 34 | showDivider: false, 35 | onPressed: (){}, 36 | ), 37 | SizedBox( 38 | height: ScreenUtil().setHeight(20.0), 39 | ), 40 | FullWithIconButton( 41 | iconPath: 'assets/images/ic_feeds.png', 42 | title: '看一看', 43 | showDivider: true, 44 | onPressed: (){}, 45 | ),FullWithIconButton( 46 | iconPath: 'assets/images/ic_quick_search.png', 47 | title: '搜一搜', 48 | showDivider: false, 49 | onPressed: (){}, 50 | ), 51 | SizedBox( 52 | height: ScreenUtil().setHeight(20.0), 53 | ), 54 | FullWithIconButton( 55 | iconPath: 'assets/images/ic_shopping.png', 56 | title: '购物', 57 | showDivider: true, 58 | onPressed: (){}, 59 | ),FullWithIconButton( 60 | iconPath: 'assets/images/ic_game_entry.png', 61 | title: '游戏', 62 | showDivider: false, 63 | onPressed: (){}, 64 | ), 65 | SizedBox( 66 | height: ScreenUtil().setHeight(20.0), 67 | ), 68 | FullWithIconButton( 69 | iconPath: 'assets/images/ic_mini_program.png', 70 | title: '小程序', 71 | showDivider: false, 72 | onPressed: (){}, 73 | ) 74 | ], 75 | ), 76 | ); 77 | } 78 | } -------------------------------------------------------------------------------- /lib/pages/index_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:provide/provide.dart'; 5 | import '../provide/currentIndex.dart'; 6 | import './message_page.dart'; 7 | import './contacts_page.dart'; 8 | import './discover_page.dart'; 9 | import './mine_page.dart'; 10 | import '../common/style/style.dart' show ICons,AppColors; 11 | // import '../constants.dart' show Constants; 12 | enum ActionItems{ 13 | GROUP_CHAT, ADD_FRIEND, QR_SCAN, PAYMENT, HELP 14 | } 15 | class IndexPage extends StatelessWidget { 16 | _buildPopupMenuItem(Widget icon, String title){ 17 | return Row( 18 | children: [ 19 | Container( 20 | padding: EdgeInsets.only(right: 12), 21 | child: icon, 22 | ), 23 | Text(title,style: TextStyle(color: Color(AppColors.AppBarPopupMenuTextColor)),) 24 | ], 25 | ); 26 | } 27 | 28 | final List bottomTabs = [ 29 | BottomNavigationBarItem( 30 | // icon: Icon(CupertinoIcons.home), 31 | icon: new Icon(ICons.MESSAGE), 32 | title: Text('微信',style: TextStyle(fontSize: 14.0)), 33 | activeIcon: new Icon(ICons.MESSAGE_ACTIVE) 34 | ), 35 | BottomNavigationBarItem( 36 | icon: new Icon(ICons.ADDRESSLIST), 37 | title: Text('通讯录',style: TextStyle(fontSize: 14.0)), 38 | activeIcon: new Icon(ICons.ADDRESSLIST_ACTIVE) 39 | ), 40 | BottomNavigationBarItem( 41 | icon: new Icon(ICons.DISCOVER), 42 | title: Text('发现',style: TextStyle(fontSize: 14.0)), 43 | activeIcon: new Icon(ICons.DISCOVER_ACTIVE) 44 | ), 45 | BottomNavigationBarItem( 46 | icon: new Icon(ICons.MINE), 47 | title: Text('我',style: TextStyle(fontSize: 14.0)), 48 | activeIcon: new Icon(ICons.MINE_ACTIVE) 49 | ) 50 | ]; 51 | 52 | final List tabBodies = [ 53 | MessagePage(), 54 | ContactsPage(), 55 | DiscoverPage(), 56 | MinePage() 57 | ]; 58 | 59 | 60 | /// 单击提示退出 61 | Future _dialogExitApp(BuildContext context) { 62 | return showDialog( 63 | context: context, 64 | builder: (context) => new AlertDialog( 65 | content: new Text('确定要退出应用?'), 66 | actions: [ 67 | new FlatButton( 68 | onPressed: () => Navigator.of(context).pop(false), 69 | child: new Text('取消', style: TextStyle(color: Colors.black54))), 70 | new FlatButton( 71 | onPressed: () => Navigator.of(context).pop(true), 72 | child: new Text('确定', style: TextStyle(color: Colors.black54))) 73 | ], 74 | )); 75 | } 76 | @override 77 | Widget build(BuildContext context) { 78 | final _pageController = PageController(initialPage: Provide.value(context).currentIndex); 79 | 80 | ScreenUtil.instance = ScreenUtil(width: 750,height:1334)..init(context);//初始化屏幕分辨率 81 | return Provide( 82 | builder: (context,child,val){ 83 | int currentIndex = Provide.value(context).currentIndex; 84 | return WillPopScope( 85 | child: Scaffold( 86 | appBar: AppBar( 87 | title: Text('微信',style: TextStyle(fontSize: ScreenUtil().setSp(30.0),color: Color(AppColors.APPBarTextColor),),), 88 | elevation: 0.0, 89 | brightness: Brightness.light, 90 | backgroundColor: Color(AppColors.PrimaryColor), 91 | actions: [ 92 | Container( 93 | padding: EdgeInsets.only(right: 16.0), 94 | child: IconButton( 95 | icon: Icon(ICons.SEARCH,color: Color(AppColors.APPBarTextColor),), 96 | onPressed: (){ 97 | print('点击了搜索按钮'); 98 | }, 99 | ), 100 | ), 101 | Theme( 102 | data: ThemeData( 103 | cardColor: Color(AppColors.APPCardColor) 104 | ), 105 | child: PopupMenuButton( 106 | icon: Icon(ICons.ADD,color: Color(AppColors.APPBarTextColor),), 107 | itemBuilder: (BuildContext context){ 108 | return >[ 109 | PopupMenuItem( 110 | child: _buildPopupMenuItem(Icon(ICons.GROUP_CHAT,size: 22.0,color: Color(AppColors.AppBarPopupMenuTextColor),),'发起群聊'), 111 | value: ActionItems.GROUP_CHAT, 112 | ), 113 | PopupMenuItem( 114 | child: _buildPopupMenuItem(Icon(ICons.ADD_FRIEND,size: 22.0,color: Color(AppColors.AppBarPopupMenuTextColor)),'添加朋友'), 115 | value: ActionItems.ADD_FRIEND, 116 | ),PopupMenuItem( 117 | child: _buildPopupMenuItem(Icon(ICons.QR_SCAN,size: 22.0,color: Color(AppColors.AppBarPopupMenuTextColor)),'扫一扫'), 118 | value: ActionItems.QR_SCAN, 119 | ),PopupMenuItem( 120 | child: _buildPopupMenuItem(Icon(ICons.PAYMENT,size: 22.0,color: Color(AppColors.AppBarPopupMenuTextColor)),'收付款'), 121 | value: ActionItems.PAYMENT, 122 | ),PopupMenuItem( 123 | child: _buildPopupMenuItem(Icon(ICons.HELP,size: 22.0,color: Color(AppColors.AppBarPopupMenuTextColor)),'帮助与反馈'), 124 | value: ActionItems.HELP, 125 | ) 126 | ]; 127 | }, 128 | onSelected: (ActionItems selected){ 129 | print(selected); 130 | }, 131 | ), 132 | ) 133 | ], 134 | ), 135 | backgroundColor: Color.fromRGBO(244, 245, 245, 1.0), 136 | bottomNavigationBar: BottomNavigationBar( 137 | type: BottomNavigationBarType.fixed, 138 | currentIndex: currentIndex, 139 | fixedColor: Color(AppColors.TabIconActive), 140 | items: bottomTabs, 141 | onTap: (index){ 142 | Provide.value(context).changeIndex(index); 143 | _pageController.animateToPage(index,duration: Duration(milliseconds: 10),curve: Curves.easeInOut); 144 | }, 145 | ), 146 | // body:IndexedStack( 147 | // index: currentIndex, 148 | // children: tabBodies, 149 | // ), 150 | body: PageView.builder( 151 | itemBuilder: (BuildContext context, int index){ 152 | return tabBodies[index]; 153 | }, 154 | controller: _pageController, 155 | itemCount: tabBodies.length, 156 | onPageChanged: (int index){ 157 | Provide.value(context).changeIndex(index); 158 | }, 159 | ), 160 | ), 161 | onWillPop: (){ 162 | return _dialogExitApp(context); 163 | }, 164 | ); 165 | }, 166 | ); 167 | } 168 | } -------------------------------------------------------------------------------- /lib/pages/message_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provide/provide.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import '../common/style/style.dart' show ICons,AppColors; 5 | import './message_page/conversation_item.dart'; 6 | import '../model/conversation.dart'; 7 | import '../provide/websocket.dart'; 8 | 9 | enum Device{ 10 | MAC, WIN 11 | } 12 | 13 | class _DeviceinfoItem extends StatelessWidget { 14 | _DeviceinfoItem({this.device:Device.MAC}); 15 | final Device device; 16 | 17 | String get devicename { 18 | return device == Device.WIN ? 'Windows' : 'Mac'; 19 | } 20 | @override 21 | Widget build(BuildContext context) { 22 | return Container( 23 | padding: EdgeInsets.all(10.0), 24 | // color: Color(AppColors.DeviceInfoItemBg), 25 | decoration: BoxDecoration( 26 | color: Color(AppColors.DeviceInfoItemBg), 27 | border: Border( 28 | bottom:BorderSide(width: 0.5,color: Color(AppColors.DividerColor)) 29 | ) 30 | ), 31 | child: Row( 32 | mainAxisAlignment: MainAxisAlignment.start, 33 | crossAxisAlignment: CrossAxisAlignment.center, 34 | children: [ 35 | SizedBox(width: ScreenUtil().setWidth(30),), 36 | device == Device.WIN ? new Icon(ICons.WINDOWS,size: ScreenUtil().setSp(40.0),):new Icon(ICons.MAC,size: ScreenUtil().setSp(40.0),), 37 | SizedBox(width: ScreenUtil().setWidth(50),), 38 | Text('$devicename 微信已登陆,手机通知已关闭',style: TextStyle(fontSize: ScreenUtil().setSp(24.0),color: Color(AppColors.DeviceInfoItemText),)) 39 | ], 40 | ), 41 | ); 42 | } 43 | } 44 | 45 | class MessagePage extends StatelessWidget { 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return Provide( 50 | builder: (context,child,val){ 51 | var messageList = Provide.value(context).messageList; 52 | var length = Conversation.mockConversations.length + 1 + messageList.length; 53 | print(length); 54 | return Container( 55 | child: ListView.builder( 56 | itemBuilder: (BuildContext context, int index){ 57 | if(index == 0){ 58 | return _DeviceinfoItem(); 59 | } else if (index < Conversation.mockConversations.length + 1){ 60 | return ConversationItem(Conversation.mockConversations[index - 1],index-1,0); 61 | }else { 62 | var inde = index - 1 - Conversation.mockConversations.length; 63 | return ConversationItem(messageList[inde],inde,1); 64 | } 65 | }, 66 | itemCount: length , 67 | ) 68 | ); 69 | } 70 | ); 71 | } 72 | } -------------------------------------------------------------------------------- /lib/pages/message_page/conversation_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import '../../common/style/style.dart' show AppColors,Constants,ICons; 4 | import '../../model/conversation.dart'; 5 | import '../../routers/application.dart'; 6 | class ConversationItem extends StatelessWidget { 7 | 8 | ConversationItem(this.conversationItemData,this.index,this.type) 9 | :assert(conversationItemData != null); 10 | int index; 11 | int type; 12 | final Conversation conversationItemData; 13 | var tapPos; 14 | @override 15 | Widget build(BuildContext context) { 16 | return Material( 17 | color: Color(AppColors.ConversationItemBg), 18 | child: InkWell( 19 | onTap: (){ 20 | print('打开会话:${conversationItemData.title}'); 21 | Application.router.navigateTo(context, '/chatdetail?index=${index}&type=${type}'); 22 | 23 | }, 24 | onTapDown: (TapDownDetails details) { 25 | tapPos = details.globalPosition; 26 | }, 27 | onLongPress: (){ 28 | _showMenu(context,tapPos); 29 | print('弹出会话菜单:${conversationItemData.title}'); 30 | }, 31 | child: Container( 32 | height: ScreenUtil().setHeight(120), 33 | child: Row( 34 | mainAxisAlignment: MainAxisAlignment.center, 35 | children: [ 36 | avatarContainer(conversationItemData), 37 | Content(), 38 | ], 39 | ), 40 | ), 41 | ), 42 | ); 43 | } 44 | _showMenu(BuildContext context,Offset tapPos){ 45 | final RenderBox overlay =Overlay.of(context).context.findRenderObject(); 46 | final RelativeRect position = RelativeRect.fromLTRB( 47 | tapPos.dx, tapPos.dy, 48 | overlay.size.width - tapPos.dx, 49 | overlay.size.height - tapPos.dy 50 | ); 51 | showMenu( 52 | context: context, 53 | position: position, 54 | items: >[ 55 | PopupMenuItem( 56 | child: Text(Constants.MENU_MARK_AS_UNREAD_VALUE), 57 | value: Constants.MENU_MARK_AS_UNREAD, 58 | ), 59 | PopupMenuItem( 60 | child: Text(Constants.MENU_PIN_TO_TOP_VALUE), 61 | value: Constants.MENU_PIN_TO_TOP, 62 | ), 63 | PopupMenuItem( 64 | child: Text(Constants.MENU_DELETE_CONVERSATION_VALUE), 65 | value: Constants.MENU_DELETE_CONVERSATION, 66 | ), 67 | ] 68 | ).then((String selected) { 69 | switch(selected){ 70 | default: 71 | print('当前选中的是:$selected'); 72 | } 73 | }); 74 | } 75 | 76 | Widget Content(){ 77 | return Expanded( 78 | child: Container( 79 | height: ScreenUtil().setHeight(120), 80 | margin: EdgeInsets.only(left:ScreenUtil().setWidth(20.0)), 81 | decoration: BoxDecoration( 82 | border: Border( 83 | bottom: BorderSide(width: 0.5,color: Color(AppColors.DividerColor),) 84 | ) 85 | ), 86 | child: Row( 87 | children: [ 88 | Title(conversationItemData), 89 | Tip(conversationItemData) 90 | ], 91 | ), 92 | ), 93 | ); 94 | } 95 | Widget ClipRRectImg(){ 96 | return ClipRRect( 97 | borderRadius: BorderRadius.circular(5.0), 98 | child: conversationItemData.isAvatarFromNet() ? Image.network(conversationItemData.avatar,scale: 1.0, fit: BoxFit.cover,) : Image.asset(conversationItemData.avatar, fit: BoxFit.cover,), 99 | ); 100 | } 101 | 102 | Widget Avatar(conversationItemData){ 103 | return Container( 104 | margin: EdgeInsets.only(left:ScreenUtil().setWidth(20.0)), 105 | child: ClipRRectImg(), 106 | width: ScreenUtil().setWidth(100), 107 | height: ScreenUtil().setWidth(100) 108 | ); 109 | } 110 | 111 | Widget Title(conversationItemData){ 112 | return Expanded( 113 | child: Column( 114 | mainAxisAlignment: MainAxisAlignment.center, 115 | crossAxisAlignment: CrossAxisAlignment.start, 116 | children: [ 117 | Text( 118 | conversationItemData.title, 119 | style: TextStyle(fontSize: ScreenUtil().setSp(30.0),color: Color(AppColors.TitleColor),fontWeight:FontWeight.w400), 120 | maxLines: 1, 121 | overflow: TextOverflow.ellipsis, 122 | ), 123 | SizedBox(height: ScreenUtil().setHeight(15.0),), 124 | Text( 125 | conversationItemData.des, 126 | style: TextStyle(fontSize: ScreenUtil().setSp(24.0),color: Color(AppColors.DesTextColor)), 127 | maxLines: 1, 128 | overflow: TextOverflow.ellipsis, 129 | ) 130 | ], 131 | ), 132 | ); 133 | } 134 | 135 | 136 | Widget Tip(conversationItemData){ 137 | var _rightArea =[ 138 | Text(conversationItemData.updateAt,style:TextStyle(fontSize: ScreenUtil().setSp(24.0),color: Color(AppColors.DesTextColor))), 139 | SizedBox(height: ScreenUtil().setHeight(15.0),) 140 | ]; 141 | if(conversationItemData.isMute){ 142 | _rightArea.add(new Icon(ICons.MUTE_ICON,color: Color(AppColors.ConversationMuteIcon),size: ScreenUtil().setSp(30),)); 143 | }else{ 144 | _rightArea.add(new Icon(ICons.MUTE_ICON,color: Colors.transparent,size: ScreenUtil().setSp(30),)); 145 | } 146 | return Container( 147 | width:ScreenUtil().setWidth(80), 148 | margin: EdgeInsets.only(right: ScreenUtil().setWidth(10.0)), 149 | child: Column( 150 | mainAxisAlignment: MainAxisAlignment.center, 151 | children: _rightArea 152 | ), 153 | ); 154 | } 155 | 156 | Widget avatarContainer(conversationItemData){ 157 | if(conversationItemData.unreadMsgCount > 0){ 158 | return Stack( 159 | overflow: Overflow.visible, 160 | children: [ 161 | Avatar(conversationItemData), 162 | Positioned( 163 | right:-3.0 , 164 | top: -3.0, 165 | child: unreadMsgCountText(conversationItemData), 166 | ) 167 | ], 168 | ); 169 | }else{ 170 | return Avatar(conversationItemData); 171 | } 172 | } 173 | Widget unreadMsgCountText(conversationItemData){ 174 | return Container( 175 | width: ScreenUtil().setWidth(32.0), 176 | height: ScreenUtil().setWidth(32.0), 177 | alignment: Alignment.center, 178 | decoration: BoxDecoration( 179 | borderRadius: BorderRadius.circular(35.0), 180 | color: Color(AppColors.NotifyDotBg) 181 | ), 182 | child: Text(conversationItemData.unreadMsgCount.toString(),style:TextStyle(fontSize: ScreenUtil().setSp(18),color: Color(AppColors.NotifyDotText))), 183 | ); 184 | } 185 | 186 | Widget muteIcon(){ 187 | return new Icon(ICons.MUTE_ICON); 188 | } 189 | } -------------------------------------------------------------------------------- /lib/pages/mine_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_easyrefresh/taurus_header.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import './component/full_with_icon_button.dart'; 5 | import '../common/style/style.dart' show ICons,AppColors; 6 | import '../model/me.dart'; 7 | class _Header extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return FlatButton( 11 | onPressed: (){}, 12 | padding: EdgeInsets.only(left: ScreenUtil().setWidth(40.0),right: ScreenUtil().setWidth(20.0),bottom: ScreenUtil().setWidth(45.0),top: ScreenUtil().setWidth(10.0)), 13 | color: AppColors.HeaderCardBg, 14 | child: Row( 15 | crossAxisAlignment: CrossAxisAlignment.center, 16 | children: [ 17 | ClipRRect( 18 | borderRadius: BorderRadius.circular(5.0), 19 | child: Image.network(me.avatar,width:ScreenUtil().setWidth(120.0),height:ScreenUtil().setWidth(120.0)), 20 | ), 21 | Expanded( 22 | child:Column( 23 | crossAxisAlignment: CrossAxisAlignment.start, 24 | children: [ 25 | Container( 26 | padding: EdgeInsets.only(left: ScreenUtil().setWidth(30.0)), 27 | child: Text(me.name,style: TextStyle(color: Color(AppColors.HeaderCardTitleText),fontSize: ScreenUtil().setSp(40.0)),), 28 | ), 29 | SizedBox( 30 | height: ScreenUtil().setWidth(15.0), 31 | ), 32 | Container( 33 | padding: EdgeInsets.only(left: ScreenUtil().setWidth(30.0)), 34 | child: Row( 35 | children: [ 36 | Expanded( 37 | child: Row( 38 | children: [ 39 | Text('微信号:',style:TextStyle(color: Color(AppColors.HeaderCardDesText),fontSize: ScreenUtil().setSp(24.0)),), 40 | Text(me.account,style:TextStyle(color: Color(AppColors.HeaderCardDesText),fontSize: ScreenUtil().setSp(24.0)),), 41 | ], 42 | ), 43 | ), 44 | Icon(ICons.ER_CODE,color:Color(AppColors.KeyboardArrowRight) ,), 45 | Icon(ICons.RIGHT,color:Color(AppColors.KeyboardArrowRight) ,) 46 | ], 47 | ) 48 | ), 49 | ], 50 | ), 51 | ) 52 | ], 53 | ), 54 | ); 55 | } 56 | } 57 | 58 | class _Body extends StatelessWidget { 59 | @override 60 | Widget build(BuildContext context) { 61 | return Column( 62 | children: [ 63 | SizedBox( 64 | height: ScreenUtil().setHeight(20.0), 65 | ), 66 | FullWithIconButton( 67 | iconPath: 'assets/images/ic_wallet.png', 68 | title: '支付', 69 | showDivider: false, 70 | onPressed: (){}, 71 | ), 72 | SizedBox( 73 | height: ScreenUtil().setHeight(20.0), 74 | ), 75 | FullWithIconButton( 76 | iconPath: 'assets/images/ic_collections.png', 77 | title: '收藏', 78 | showDivider: true, 79 | onPressed: (){}, 80 | ), 81 | FullWithIconButton( 82 | iconPath: 'assets/images/ic_album.png', 83 | title: '相册', 84 | showDivider: true, 85 | onPressed: (){}, 86 | ), 87 | FullWithIconButton( 88 | iconPath: 'assets/images/ic_cards_wallet.png', 89 | title: '卡包', 90 | showDivider: true, 91 | onPressed: (){}, 92 | ), 93 | FullWithIconButton( 94 | iconPath: 'assets/images/ic_emotions.png', 95 | title: '表情', 96 | showDivider: false, 97 | onPressed: (){}, 98 | ), 99 | SizedBox( 100 | height: ScreenUtil().setHeight(20.0), 101 | ), 102 | FullWithIconButton( 103 | iconPath: 'assets/images/ic_settings.png', 104 | title: '设置', 105 | showDivider: false, 106 | description: '账号保护', 107 | onPressed: (){}, 108 | ), 109 | ], 110 | ); 111 | } 112 | } 113 | class MinePage extends StatefulWidget { 114 | @override 115 | _MinePageState createState() => _MinePageState(); 116 | } 117 | 118 | class _MinePageState extends State { 119 | @override 120 | Widget build(BuildContext context) { 121 | return Container( 122 | color: Color(AppColors.BackgroundColor), 123 | child: SingleChildScrollView( 124 | child: Column( 125 | children: [ 126 | _Header(), 127 | _Body() 128 | ], 129 | ), 130 | ), 131 | ); 132 | } 133 | } -------------------------------------------------------------------------------- /lib/provide/currentIndex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CurrentIndexProvide with ChangeNotifier{ 4 | int currentIndex = 0; 5 | 6 | changeIndex(int newIndex){ 7 | currentIndex = newIndex; 8 | notifyListeners(); 9 | } 10 | } -------------------------------------------------------------------------------- /lib/provide/websocket.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:web_socket_channel/io.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:web_socket_channel/web_socket_channel.dart'; 8 | import 'package:web_socket_channel/io.dart'; 9 | import '../model/conversation.dart'; 10 | class WebSocketProvide with ChangeNotifier{ 11 | var uid = ''; 12 | var nickname = ''; 13 | var users = []; 14 | var groups =[]; 15 | var historyMessage = [];//接收到哦的所有的历史消息 16 | var messageList = []; // 所有消息页面人员 17 | var currentMessageList = [];//选择进入详情页的消息历史记录 18 | var connecting = false;//websocket连接状态 19 | IOWebSocketChannel channel; 20 | 21 | 22 | init() async { 23 | SharedPreferences prefs = await SharedPreferences.getInstance(); 24 | final userInfo = prefs.getString('userInfo'); 25 | print(userInfo); 26 | if(userInfo == null){//弹出设置用户名 27 | var now = new DateTime.now(); 28 | print(now.millisecondsSinceEpoch);//单位毫秒,13位时间戳 29 | print(now.microsecondsSinceEpoch);//单位微秒,16位时间戳 30 | uid = "flutter_${now.microsecondsSinceEpoch}"; 31 | nickname = "flutter_${Random().nextInt(100)}"; 32 | var userInfoData = { 33 | "uid" : uid, 34 | "nickname" : nickname 35 | }; 36 | var userInfoStr = json.encode(userInfoData).toString(); 37 | prefs.setString('userInfo', userInfoStr); 38 | }else{ 39 | var userInfoData = json.decode(userInfo.toString()); 40 | uid = userInfoData['uid']; 41 | nickname = userInfoData['nickname']; 42 | } 43 | return await createWebsocket(); 44 | // monitorMessage(); 45 | } 46 | createWebsocket() async {//创建连接并且发送鉴别身份信息 47 | channel = await new IOWebSocketChannel.connect('ws://111.231.225.178:3001'); 48 | var obj = { 49 | "uid": uid, 50 | "type": 1, 51 | "nickname": nickname, 52 | "msg": "", 53 | "bridge": [], 54 | "groupId": "" 55 | }; 56 | String text = json.encode(obj).toString(); 57 | channel.sink.add(text); 58 | //监听到服务器返回消息 59 | channel.stream.listen((data) => listenMessage(data),onError: onError,onDone: onDone); 60 | } 61 | 62 | listenMessage(data){ 63 | connecting = true; 64 | var obj = jsonDecode(data); 65 | print(data); 66 | if(obj['type'] == 1){ // 获取聊天室的人员与群列表 67 | messageList = []; 68 | print(obj['msg']); 69 | users = obj['users']; 70 | groups = obj['groups']; 71 | for(var i = 0; i < groups.length; i++){ 72 | messageList.add(new Conversation( 73 | avatar: 'assets/images/ic_group_chat.png', 74 | title: groups[i]['name'], 75 | des: '点击进入聊天', 76 | updateAt: obj['date'].substring(11,16), 77 | unreadMsgCount: 0, 78 | displayDot: false, 79 | groupId: groups[i]['id'], 80 | type: 2 81 | )); 82 | } 83 | for(var i = 0; i < users.length; i++){ 84 | if(users[i]['uid'] != uid){ 85 | messageList.add(new Conversation( 86 | avatar: 'assets/images/ic_group_chat.png', 87 | title: users[i]['nickname'], 88 | des: '点击进入聊天', 89 | updateAt: obj['date'].substring(11,16), 90 | unreadMsgCount: 0, 91 | displayDot: false, 92 | userId:users[i]['uid'], 93 | type: 1 94 | )); 95 | } 96 | } 97 | }else if (obj['type'] == 2){//接收到消息 98 | historyMessage.add(obj); 99 | print(historyMessage); 100 | for(var i = 0; i < messageList.length; i++){ 101 | if(messageList[i].userId != null){ 102 | var count = 0; 103 | for(var r = 0; r < historyMessage.length; r++){ 104 | if(historyMessage[r]['status']==1 && historyMessage[r]['bridge'].contains(messageList[i].userId) && historyMessage[r]['uid'] != uid){ 105 | count++; 106 | } 107 | } 108 | if(count > 0){ 109 | messageList[i].displayDot = true; 110 | messageList[i].unreadMsgCount = count; 111 | } 112 | } 113 | if(messageList[i].groupId != null){ 114 | var count = 0; 115 | for(var r = 0; r < historyMessage.length; r++){ 116 | if(historyMessage[r]['status']==1 && historyMessage[r]['groupId']==messageList[i].groupId && historyMessage[r]['uid'] != uid){ 117 | count++; 118 | } 119 | } 120 | if(count > 0){ 121 | messageList[i].displayDot = true; 122 | messageList[i].unreadMsgCount = count; 123 | } 124 | } 125 | } 126 | } 127 | notifyListeners(); 128 | } 129 | sendMessage(type,data,index){//发送消息 130 | print(messageList[index].userId); 131 | print(messageList[index].groupId); 132 | var _bridge = []; 133 | if(messageList[index].userId != null){ 134 | _bridge..add(messageList[index].userId)..add(uid); 135 | } 136 | int _groupId; 137 | if(messageList[index].groupId != null){ 138 | _groupId = messageList[index].groupId; 139 | } 140 | print(_bridge); 141 | var obj = { 142 | "uid": uid, 143 | "type": 2, 144 | "nickname": nickname, 145 | "msg": data, 146 | "bridge": _bridge , 147 | "groupId": _groupId 148 | }; 149 | String text = json.encode(obj).toString(); 150 | print(text); 151 | channel.sink.add(text); 152 | } 153 | onError(error){ 154 | print('error------------>${error}'); 155 | } 156 | 157 | void onDone() { 158 | print('websocket断开了'); 159 | createWebsocket(); 160 | print('websocket重连'); 161 | } 162 | 163 | closeWebSocket(){//关闭链接 164 | channel.sink.close(); 165 | print('关闭了websocket'); 166 | notifyListeners(); 167 | } 168 | } -------------------------------------------------------------------------------- /lib/routers/application.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | 3 | class Application{ 4 | static Router router; 5 | } -------------------------------------------------------------------------------- /lib/routers/router_handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import '../pages/chat_detail_page.dart'; 4 | 5 | Handler chatDetailHandler = Handler( 6 | handlerFunc: (BuildContext context,Map> params){ 7 | final index = params['index'].first; 8 | final typ = params['type'].first; 9 | print('message>detail title is ${index}'); 10 | final inde = int.parse(index); 11 | final type = int.parse(typ); 12 | return ChatDetailPage(inde,type); 13 | } 14 | ); -------------------------------------------------------------------------------- /lib/routers/routers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import './router_handler.dart'; 4 | 5 | class Routers{ 6 | static String root = '/'; 7 | static String chatdetailPage = 'chatdetail'; 8 | static void configureRouters(Router router){ 9 | router.notFoundHandler = new Handler( 10 | handlerFunc: (BuildContext context,Map> params){ 11 | print('error====>route was not found!!!!!'); 12 | } 13 | ); 14 | 15 | router.define(chatdetailPage,handler: chatDetailHandler); 16 | } 17 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "nodejs-websocket": { 6 | "version": "1.7.2", 7 | "resolved": "https://registry.npm.taobao.org/nodejs-websocket/download/nodejs-websocket-1.7.2.tgz", 8 | "integrity": "sha1-lKvR4kj1fU0cZj3sODEBXG2tmKY=" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "moment": "^2.24.0", 4 | "nodejs-websocket": "^1.7.2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "2.1.0" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.14.11" 32 | convert: 33 | dependency: transitive 34 | description: 35 | name: convert 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "2.1.1" 39 | cookie_jar: 40 | dependency: transitive 41 | description: 42 | name: cookie_jar 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.0.0" 46 | crypto: 47 | dependency: transitive 48 | description: 49 | name: crypto 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "2.0.6" 53 | csslib: 54 | dependency: transitive 55 | description: 56 | name: csslib 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "0.15.0" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "0.1.2" 67 | dio: 68 | dependency: "direct main" 69 | description: 70 | name: dio 71 | url: "https://pub.flutter-io.cn" 72 | source: hosted 73 | version: "2.1.3" 74 | fluro: 75 | dependency: "direct main" 76 | description: 77 | name: fluro 78 | url: "https://pub.flutter-io.cn" 79 | source: hosted 80 | version: "1.4.0" 81 | flutter: 82 | dependency: "direct main" 83 | description: flutter 84 | source: sdk 85 | version: "0.0.0" 86 | flutter_easyrefresh: 87 | dependency: "direct main" 88 | description: 89 | name: flutter_easyrefresh 90 | url: "https://pub.flutter-io.cn" 91 | source: hosted 92 | version: "1.2.7" 93 | flutter_html: 94 | dependency: "direct main" 95 | description: 96 | name: flutter_html 97 | url: "https://pub.flutter-io.cn" 98 | source: hosted 99 | version: "0.9.6" 100 | flutter_page_indicator: 101 | dependency: transitive 102 | description: 103 | name: flutter_page_indicator 104 | url: "https://pub.flutter-io.cn" 105 | source: hosted 106 | version: "0.0.3" 107 | flutter_screenutil: 108 | dependency: "direct main" 109 | description: 110 | name: flutter_screenutil 111 | url: "https://pub.flutter-io.cn" 112 | source: hosted 113 | version: "0.5.2" 114 | flutter_swiper: 115 | dependency: "direct main" 116 | description: 117 | name: flutter_swiper 118 | url: "https://pub.flutter-io.cn" 119 | source: hosted 120 | version: "1.1.6" 121 | flutter_test: 122 | dependency: "direct dev" 123 | description: flutter 124 | source: sdk 125 | version: "0.0.0" 126 | flutter_webview_plugin: 127 | dependency: "direct main" 128 | description: 129 | name: flutter_webview_plugin 130 | url: "https://pub.flutter-io.cn" 131 | source: hosted 132 | version: "0.3.4" 133 | fluttertoast: 134 | dependency: "direct main" 135 | description: 136 | name: fluttertoast 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "3.0.4" 140 | html: 141 | dependency: transitive 142 | description: 143 | name: html 144 | url: "https://pub.flutter-io.cn" 145 | source: hosted 146 | version: "0.13.4+2" 147 | matcher: 148 | dependency: transitive 149 | description: 150 | name: matcher 151 | url: "https://pub.flutter-io.cn" 152 | source: hosted 153 | version: "0.12.5" 154 | meta: 155 | dependency: transitive 156 | description: 157 | name: meta 158 | url: "https://pub.flutter-io.cn" 159 | source: hosted 160 | version: "1.1.6" 161 | path: 162 | dependency: transitive 163 | description: 164 | name: path 165 | url: "https://pub.flutter-io.cn" 166 | source: hosted 167 | version: "1.6.2" 168 | pedantic: 169 | dependency: transitive 170 | description: 171 | name: pedantic 172 | url: "https://pub.flutter-io.cn" 173 | source: hosted 174 | version: "1.5.0" 175 | provide: 176 | dependency: "direct main" 177 | description: 178 | name: provide 179 | url: "https://pub.flutter-io.cn" 180 | source: hosted 181 | version: "1.0.2" 182 | quiver: 183 | dependency: transitive 184 | description: 185 | name: quiver 186 | url: "https://pub.flutter-io.cn" 187 | source: hosted 188 | version: "2.0.2" 189 | shared_preferences: 190 | dependency: "direct main" 191 | description: 192 | name: shared_preferences 193 | url: "https://pub.flutter-io.cn" 194 | source: hosted 195 | version: "0.5.2" 196 | sky_engine: 197 | dependency: transitive 198 | description: flutter 199 | source: sdk 200 | version: "0.0.99" 201 | source_span: 202 | dependency: transitive 203 | description: 204 | name: source_span 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "1.5.5" 208 | stack_trace: 209 | dependency: transitive 210 | description: 211 | name: stack_trace 212 | url: "https://pub.flutter-io.cn" 213 | source: hosted 214 | version: "1.9.3" 215 | stream_channel: 216 | dependency: transitive 217 | description: 218 | name: stream_channel 219 | url: "https://pub.flutter-io.cn" 220 | source: hosted 221 | version: "2.0.0" 222 | string_scanner: 223 | dependency: transitive 224 | description: 225 | name: string_scanner 226 | url: "https://pub.flutter-io.cn" 227 | source: hosted 228 | version: "1.0.4" 229 | term_glyph: 230 | dependency: transitive 231 | description: 232 | name: term_glyph 233 | url: "https://pub.flutter-io.cn" 234 | source: hosted 235 | version: "1.1.0" 236 | test_api: 237 | dependency: transitive 238 | description: 239 | name: test_api 240 | url: "https://pub.flutter-io.cn" 241 | source: hosted 242 | version: "0.2.4" 243 | transformer_page_view: 244 | dependency: transitive 245 | description: 246 | name: transformer_page_view 247 | url: "https://pub.flutter-io.cn" 248 | source: hosted 249 | version: "0.1.6" 250 | typed_data: 251 | dependency: transitive 252 | description: 253 | name: typed_data 254 | url: "https://pub.flutter-io.cn" 255 | source: hosted 256 | version: "1.1.6" 257 | url_launcher: 258 | dependency: "direct main" 259 | description: 260 | name: url_launcher 261 | url: "https://pub.flutter-io.cn" 262 | source: hosted 263 | version: "5.0.2" 264 | utf: 265 | dependency: transitive 266 | description: 267 | name: utf 268 | url: "https://pub.flutter-io.cn" 269 | source: hosted 270 | version: "0.9.0+5" 271 | vector_math: 272 | dependency: transitive 273 | description: 274 | name: vector_math 275 | url: "https://pub.flutter-io.cn" 276 | source: hosted 277 | version: "2.0.8" 278 | web_socket_channel: 279 | dependency: "direct main" 280 | description: 281 | name: web_socket_channel 282 | url: "https://pub.flutter-io.cn" 283 | source: hosted 284 | version: "1.0.12" 285 | sdks: 286 | dart: ">=2.2.0 <3.0.0" 287 | flutter: ">=0.5.6 <2.0.0" 288 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wechat 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 | flutter_webview_plugin: ^0.3.4 27 | dio: ^2.1.2 28 | flutter_swiper: ^1.1.6 29 | flutter_screenutil: ^0.5.2 30 | url_launcher: ^5.0.2 31 | flutter_easyrefresh: ^1.2.7 32 | provide: ^1.0.2 33 | fluttertoast: ^3.0.4 34 | fluro: ^1.4.0 35 | flutter_html: ^0.9.6 36 | shared_preferences: ^0.5.2 37 | web_socket_channel: ^1.0.12 38 | 39 | dev_dependencies: 40 | flutter_test: 41 | sdk: flutter 42 | 43 | 44 | # For information on the generic Dart part of this file, see the 45 | # following page: https://www.dartlang.org/tools/pub/pubspec 46 | 47 | # The following section is specific to Flutter. 48 | flutter: 49 | 50 | # The following line ensures that the Material Icons font is 51 | # included with your application, so that you can use the icons in 52 | # the material Icons class. 53 | uses-material-design: true 54 | 55 | assets: 56 | - assets/images/ic_file_transfer.png 57 | - assets/images/ic_fengchao.png 58 | - assets/images/ic_tx_news.png 59 | - assets/images/ic_wx_games.png 60 | - assets/images/ic_new_friend.png 61 | - assets/images/ic_group_chat.png 62 | - assets/images/ic_tag.png 63 | - assets/images/ic_public_account.png 64 | - assets/images/ic_bottle_msg.png 65 | - assets/images/ic_feeds.png 66 | - assets/images/ic_game_entry.png 67 | - assets/images/ic_mini_program.png 68 | - assets/images/ic_people_nearby.png 69 | - assets/images/ic_quick_scan.png 70 | - assets/images/ic_quick_search.png 71 | - assets/images/ic_shake_phone.png 72 | - assets/images/ic_shopping.png 73 | - assets/images/ic_social_circle.png 74 | - assets/images/ic_wallet.png 75 | - assets/images/ic_album.png 76 | - assets/images/ic_collections.png 77 | - assets/images/ic_cards_wallet.png 78 | - assets/images/ic_emotions.png 79 | - assets/images/ic_settings.png 80 | - assets/images/ic_qrcode_preview_tiny.png 81 | - assets/images/default_nor_avatar.png 82 | - assets/images/ad_game_notify.png 83 | 84 | fonts: 85 | - family: wxIconFont 86 | fonts: 87 | - asset: assets/fonts/iconfont.ttf -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | var ws = require("nodejs-websocket"); 2 | var moment = require('moment'); 3 | 4 | console.log("开始建立连接...") 5 | 6 | let users = []; 7 | let conns = {}; 8 | let groups = [ 9 | { 10 | "id": 1111111111111, 11 | "name": "flutter交流群", 12 | "users": [] 13 | } 14 | ]; 15 | 16 | function boardcast(obj) { 17 | if(obj.bridge && obj.bridge.length){ 18 | obj.bridge.forEach(item=>{ 19 | conns[item].sendText(JSON.stringify(obj)); 20 | console.log(conns) 21 | console.log(conns[item]) 22 | console.log(JSON.stringify(obj)) 23 | }) 24 | return; 25 | } 26 | if (obj.groupId) { 27 | group = groups.filter(item=>{ 28 | return item.id === obj.groupId 29 | })[0]; 30 | group.users.forEach(item=>{ 31 | conns[item.uid].sendText(JSON.stringify(obj)); 32 | }) 33 | return; 34 | } 35 | 36 | server.connections.forEach((conn, index) => { 37 | console.log(JSON.stringify(obj)) 38 | conn.sendText(JSON.stringify(obj)); 39 | }) 40 | } 41 | 42 | var server = ws.createServer(function(conn){ 43 | conn.on("text", function (obj) { 44 | obj = JSON.parse(obj); 45 | conns[''+obj.uid+''] = conn; 46 | switch(obj.type){ 47 | // 创建连接 48 | case 1: 49 | let isuser = users.some(item=>{ 50 | return item.uid === obj.uid 51 | }) 52 | if(!isuser){ 53 | users.push({ 54 | nickname: obj.nickname, 55 | uid: obj.uid 56 | }); 57 | groups[0].users.push({ //默认加入flutter交流群 58 | nickname: obj.nickname, 59 | uid: obj.uid 60 | }); 61 | } 62 | boardcast({ 63 | type: 1, 64 | date: moment().format('YYYY-MM-DD HH:mm:ss'), 65 | msg: obj.nickname+'加入聊天室', 66 | users: users, 67 | groups: groups, 68 | uid: obj.uid, 69 | nickname: obj.nickname, 70 | bridge: obj.bridge 71 | }); 72 | break; 73 | // 创建群 74 | case 10: 75 | groups.push({ 76 | id: moment().valueOf(), 77 | name: obj.groupName, 78 | users: [{ 79 | uid: obj.uid, 80 | nickname: obj.nickname 81 | }] 82 | }) 83 | boardcast({ 84 | type: 1, 85 | date: moment().format('YYYY-MM-DD HH:mm:ss'), 86 | msg: obj.nickname+'创建了群' + obj.groupName, 87 | users: users, 88 | groups: groups, 89 | uid: obj.uid, 90 | nickname: obj.nickname, 91 | bridge: obj.bridge 92 | }); 93 | break; 94 | // 加入群 95 | case 20: 96 | let group = groups.filter(item=>{ 97 | return item.id === obj.groupId 98 | })[0] 99 | group.users.push({ 100 | uid: obj.uid, 101 | nickname: obj.nickname 102 | }) 103 | boardcast({ 104 | type: 1, 105 | date: moment().format('YYYY-MM-DD HH:mm:ss'), 106 | msg: obj.nickname+'加入了群' + obj.groupName, 107 | users: users, 108 | groups: groups, 109 | uid: obj.uid, 110 | nickname: obj.nickname, 111 | bridge: obj.bridge 112 | }); 113 | break; 114 | // 发送消息 115 | default: 116 | boardcast({ 117 | type: 2, 118 | date: moment().format('YYYY-MM-DD HH:mm:ss'), 119 | msg: obj.msg, 120 | uid: obj.uid, 121 | nickname: obj.nickname, 122 | bridge: obj.bridge, 123 | groupId: obj.groupId, 124 | status: 1 125 | }); 126 | break; 127 | } 128 | }) 129 | conn.on("close", function (code, reason) { 130 | console.log("关闭连接") 131 | }); 132 | conn.on("error", function (code, reason) { 133 | console.log("异常关闭") 134 | }); 135 | }).listen(3001) 136 | console.log("WebSocket建立完毕") 137 | -------------------------------------------------------------------------------- /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:wechat/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 | -------------------------------------------------------------------------------- /wechat.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wkiwi/flutter_WeChat/5ddd2fba767f1f4d9ebbf10411e2b642e6705291/wechat.apk --------------------------------------------------------------------------------