├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── libraries │ ├── Dart_Packages.xml │ ├── Dart_SDK.xml │ └── Flutter_Plugins.xml └── misc.xml ├── .metadata ├── README.md ├── android ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ └── runConfigurations.xml ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── flutterim │ │ │ └── example │ │ │ └── flutterapp │ │ │ └── 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 ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── assets ├── audio │ ├── message.wav │ └── request.mp3 └── images │ ├── clear.png │ ├── favorites.png │ ├── hachker.jpg │ ├── head.png │ ├── icon.png │ ├── message.png │ ├── new_trend │ ├── ai.jpeg │ └── block_chain.jpeg │ ├── password.png │ ├── person.png │ ├── receiver.png │ ├── sender.png │ ├── splash.jpeg │ ├── user.png │ └── user_background.jpeg ├── demonstration_picture ├── demo1.gif ├── demo2.gif ├── demo3.gif ├── demo4.gif └── demo5.gif ├── flutter_app.iml ├── flutter_app_android.iml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── 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 ├── com │ └── navigation │ │ ├── component │ │ ├── chart_message_item.dart │ │ ├── contacts_list_item.dart │ │ ├── message_list_item.dart │ │ ├── new_trend_star.dart │ │ ├── search_contacts_item.dart │ │ ├── search_item.dart │ │ ├── system_propel.dart │ │ └── user_info_item.dart │ │ ├── models │ │ ├── contacts_list_model.dart │ │ ├── message_list_item_model.dart │ │ ├── new_trend_model.dart │ │ └── system_propel_model.dart │ │ ├── netwok │ │ └── socket_handler.dart │ │ ├── page │ │ ├── login.dart │ │ ├── register.dart │ │ ├── subpage │ │ │ ├── about_program.dart │ │ │ ├── address_list.dart │ │ │ ├── application_min.dart │ │ │ ├── application_setting.dart │ │ │ ├── chart_dialog.dart │ │ │ ├── contacts.dart │ │ │ ├── contacts_search.dart │ │ │ ├── message.dart │ │ │ ├── new_trend.dart │ │ │ ├── new_trend │ │ │ │ ├── artificial_intelligence.dart │ │ │ │ └── block_chain.dart │ │ │ ├── picture_select.dart │ │ │ ├── search.dart │ │ │ ├── sweep_code.dart │ │ │ ├── system_inform.dart │ │ │ ├── user_info.dart │ │ │ └── webview.dart │ │ └── user.dart │ │ └── utils │ │ ├── application.dart │ │ ├── constant.dart │ │ ├── file_handler.dart │ │ ├── system_announce.dart │ │ └── utils.dart ├── main.dart └── splash_screne.dart ├── pubspec.lock ├── pubspec.yaml ├── res ├── images │ └── beauty │ │ ├── 1.jpeg │ │ ├── 2.jpeg │ │ └── 3.jpeg └── values │ └── strings_en.arb └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.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: 66091f969653fd3535b265ddcd87436901858a1d 8 | channel: dev 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 畅聊 v0.2 2 | ## 构建环境: 3 | flutter dart java 4 | ## 功能 5 | 即时通信 6 | ## 服务器 7 | 服务端采用github上开源项目[social-vertx](https://github.com/whitewoodcity/social-vertex),并且严格遵守该项目通讯协议 8 | ## 项目整体架构 9 | ![加载失败](https://github.com/GZYangKui/flutter-IM/blob/master/snap-shot/framewrk.png "framework") 10 | 11 | ### 视屏图片 12 | #### 图片一 13 | ![loadFailed](https://github.com/GZYangKui/flutter-IM/blob/master/demonstration_picture/demo1.gif) 14 | #### 图片二 15 | ![loadFailed](https://github.com/GZYangKui/flutter-IM/blob/master/demonstration_picture/demo2.gif) 16 | #### 图片三 17 | ![ loadFailed](https://github.com/GZYangKui/flutter-IM/blob/master/demonstration_picture/demo3.gif) 18 | #### 图片四 19 | ![loadFailed](https://github.com/GZYangKui/flutter-IM/blob/master/demonstration_picture/demo4.gif) 20 | #### 图片五 21 | ![loadFailed](https://github.com/GZYangKui/flutter-IM/blob/master/demonstration_picture/demo5.gif) 22 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /android/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /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 | throw new GradleException("versionCode not found. Define flutter.versionCode in the local.properties file.") 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | throw new GradleException("versionName not found. Define flutter.versionName in the local.properties file.") 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 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 "flutterim.example.flutterapp" 37 | minSdkVersion 21 38 | targetSdkVersion 27 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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/flutterim/example/flutterapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package flutterim.example.flutterapp; 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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.4' 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.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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.4-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /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/audio/message.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/audio/message.wav -------------------------------------------------------------------------------- /assets/audio/request.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/audio/request.mp3 -------------------------------------------------------------------------------- /assets/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/clear.png -------------------------------------------------------------------------------- /assets/images/favorites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/favorites.png -------------------------------------------------------------------------------- /assets/images/hachker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/hachker.jpg -------------------------------------------------------------------------------- /assets/images/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/head.png -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/message.png -------------------------------------------------------------------------------- /assets/images/new_trend/ai.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/new_trend/ai.jpeg -------------------------------------------------------------------------------- /assets/images/new_trend/block_chain.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/new_trend/block_chain.jpeg -------------------------------------------------------------------------------- /assets/images/password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/password.png -------------------------------------------------------------------------------- /assets/images/person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/person.png -------------------------------------------------------------------------------- /assets/images/receiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/receiver.png -------------------------------------------------------------------------------- /assets/images/sender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/sender.png -------------------------------------------------------------------------------- /assets/images/splash.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/splash.jpeg -------------------------------------------------------------------------------- /assets/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/user.png -------------------------------------------------------------------------------- /assets/images/user_background.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/assets/images/user_background.jpeg -------------------------------------------------------------------------------- /demonstration_picture/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/demonstration_picture/demo1.gif -------------------------------------------------------------------------------- /demonstration_picture/demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/demonstration_picture/demo2.gif -------------------------------------------------------------------------------- /demonstration_picture/demo3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/demonstration_picture/demo3.gif -------------------------------------------------------------------------------- /demonstration_picture/demo4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/demonstration_picture/demo4.gif -------------------------------------------------------------------------------- /demonstration_picture/demo5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/demonstration_picture/demo5.gif -------------------------------------------------------------------------------- /flutter_app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /flutter_app_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /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 "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /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 | 8 | -------------------------------------------------------------------------------- /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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/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 | flutter_app 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/com/navigation/component/chart_message_item.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 6 | import 'package:flutter_app/com/navigation/utils/application.dart' 7 | as application; 8 | 9 | /// 10 | /// 11 | /// @_message代表一条消息判其是否以{[|@#\$%|]}结尾来区分发送者身份 12 | /// 13 | 14 | class ChartMessageItem extends StatefulWidget { 15 | final String _message; 16 | 17 | ChartMessageItem(this._message); 18 | 19 | @override 20 | ChartMessageItemState createState() => ChartMessageItemState(); 21 | } 22 | 23 | class ChartMessageItemState extends State { 24 | ChartMessageItemState(); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return widget._message.endsWith(constants.messageOwn) 29 | ? _ownSend() 30 | : _friendSend(); 31 | } 32 | 33 | Widget _friendSend() { 34 | return Row( 35 | children: [ 36 | CircleAvatar( 37 | backgroundColor: Colors.grey, 38 | child: application.images["sender"], 39 | ), 40 | Container( 41 | foregroundDecoration: ShapeDecoration( 42 | shape: RoundedRectangleBorder( 43 | side: BorderSide(color: Colors.lightBlue), 44 | borderRadius: BorderRadius.all( 45 | Radius.circular(10.0), 46 | ), 47 | ), 48 | ), 49 | margin: EdgeInsets.only(top: 15.0), 50 | alignment: Alignment.center, 51 | padding: EdgeInsets.all(10.0), 52 | decoration: BoxDecoration( 53 | color: Color.fromRGBO(250, 250, 210, 1.0), 54 | borderRadius: BorderRadius.all( 55 | Radius.circular(20.00), 56 | ), 57 | ), 58 | width: 59 | (ui.window.physicalSize.width / ui.window.devicePixelRatio) * 0.7, 60 | child: Text( 61 | widget._message, 62 | style: TextStyle(fontSize: 20.0), 63 | ), 64 | ), 65 | ], 66 | ); 67 | } 68 | 69 | Widget _ownSend() { 70 | final message = widget._message.split(constants.messageOwn)[0]; 71 | return Row( 72 | mainAxisAlignment: MainAxisAlignment.end, 73 | children: [ 74 | Container( 75 | foregroundDecoration: ShapeDecoration( 76 | shape: RoundedRectangleBorder( 77 | side: BorderSide(color: Colors.lightBlue), 78 | borderRadius: BorderRadius.all(Radius.circular(10.0))), 79 | ), 80 | margin: EdgeInsets.only(top: 15.0), 81 | alignment: Alignment.center, 82 | padding: EdgeInsets.all(10.0), 83 | decoration: BoxDecoration( 84 | color: Color.fromRGBO(250, 250, 210, 1.0), 85 | borderRadius: BorderRadius.all( 86 | Radius.circular(20.00), 87 | ), 88 | ), 89 | width: 90 | (ui.window.physicalSize.width / ui.window.devicePixelRatio) * 0.7, 91 | child: Text( 92 | message, 93 | style: TextStyle(fontSize: 20.0), 94 | ), 95 | ), 96 | CircleAvatar( 97 | backgroundColor: Colors.grey, 98 | child: application.images["receiver"], 99 | ), 100 | ], 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/com/navigation/component/contacts_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/models/contacts_list_model.dart'; 4 | import 'package:flutter_app/com/navigation/page/subpage/chart_dialog.dart'; 5 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 6 | as handler; 7 | 8 | class ContactItem extends StatefulWidget { 9 | final Entry entry; 10 | 11 | ContactItem(this.entry); 12 | 13 | @override 14 | ContactItemState createState() => ContactItemState(); 15 | } 16 | 17 | class ContactItemState extends State 18 | with SingleTickerProviderStateMixin { 19 | AnimationController _controller; 20 | Animation _drawerContentsOpacity; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | _controller = new AnimationController( 26 | vsync: this, 27 | duration: const Duration(milliseconds: 200), 28 | ); 29 | _drawerContentsOpacity = new CurvedAnimation( 30 | parent: new ReverseAnimation(_controller), 31 | curve: Curves.fastOutSlowIn, 32 | ); 33 | } 34 | 35 | Widget _buildTiles(Entry root) { 36 | if (root.list.isEmpty) 37 | return FadeTransition( 38 | opacity: _drawerContentsOpacity, 39 | child: ListTile( 40 | title: getPerson(root.title), 41 | onTap: () => Navigator.push( 42 | context, 43 | MaterialPageRoute( 44 | builder: (BuildContext context) => ChartDialog( 45 | messages: handler.getChatRecorder(root.title), 46 | name: root.title, 47 | ), 48 | ), 49 | ), 50 | ), 51 | ); 52 | return ExpansionTile( 53 | key: PageStorageKey(root), 54 | title: getGroup(root.title), 55 | children: root.list.map(_buildTiles).toList(), 56 | ); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | return _buildTiles(widget.entry); 62 | } 63 | 64 | getGroup(String title) { 65 | return Row( 66 | children: [ 67 | Column( 68 | children: [ 69 | Text( 70 | title, 71 | style: TextStyle(fontSize: 22.0), 72 | ), 73 | ], 74 | ), 75 | ], 76 | ); 77 | } 78 | 79 | getPerson(String name) { 80 | return Row( 81 | children: [ 82 | Column( 83 | children: [ 84 | CircleAvatar( 85 | radius: 25.0, 86 | backgroundImage: AssetImage("assets/images/icon.png"), 87 | ), 88 | ], 89 | ), 90 | Expanded( 91 | child: Padding( 92 | padding: const EdgeInsets.all(8.0), 93 | child: Column( 94 | crossAxisAlignment: CrossAxisAlignment.start, 95 | children: [ 96 | Text( 97 | name, 98 | style: TextStyle(fontSize: 20.0), 99 | ) 100 | ], 101 | ), 102 | ), 103 | ), 104 | ], 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/com/navigation/component/message_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/models/message_list_item_model.dart'; 4 | import 'package:flutter_app/com/navigation/page/subpage/chart_dialog.dart'; 5 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 6 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 7 | as handler; 8 | 9 | /// 10 | /// 消息列表itme 11 | /// 12 | class MessageListItem extends StatefulWidget { 13 | final MessageListItemModel model; 14 | 15 | MessageListItem(this.model); 16 | 17 | @override 18 | MessageListItemState createState() => MessageListItemState(); 19 | } 20 | 21 | class MessageListItemState extends State { 22 | @override 23 | Widget build(BuildContext context) { 24 | return GestureDetector( 25 | child: Column( 26 | children: [ 27 | Row( 28 | children: [ 29 | CircleAvatar( 30 | radius: 25.0, 31 | backgroundColor: Colors.green, 32 | backgroundImage: AssetImage("assets/images/icon.png"), 33 | ), 34 | Expanded( 35 | child: Padding( 36 | padding: const EdgeInsets.all(8.0), 37 | child: Column( 38 | children: [ 39 | Row( 40 | children: [ 41 | Expanded( 42 | child: Text( 43 | widget.model.name, 44 | style: TextStyle(fontSize: 22.0), 45 | overflow: TextOverflow.ellipsis, 46 | ), 47 | ), 48 | Text(_calTime()), 49 | ], 50 | ), 51 | SizedBox( 52 | height: 3.00, 53 | ), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Text( 58 | widget.model 59 | .messages[widget.model.messages.length - 1] 60 | .split(constants.messageOwn)[0], 61 | overflow: TextOverflow.ellipsis, 62 | ), 63 | ), 64 | widget.model.messages.length != 65 | handler.obtainMessageNumber(widget.model.name) 66 | ? Container( 67 | width: 30.0, 68 | height: 20.0, 69 | alignment: Alignment.center, 70 | decoration: BoxDecoration( 71 | shape: BoxShape.rectangle, 72 | color: Color.fromRGBO(142, 229, 238, 0.8), 73 | borderRadius: BorderRadius.all( 74 | Radius.circular(7.0), 75 | ), 76 | ), 77 | child: Text((widget.model.messages.length - 78 | handler.obtainMessageNumber( 79 | widget.model.name)) 80 | .toString()), 81 | ) 82 | : Text(""), 83 | ], 84 | ), 85 | ], 86 | ), 87 | ), 88 | ), 89 | ], 90 | ), 91 | Divider( 92 | height: 3.0, 93 | ), 94 | ], 95 | ), 96 | onTapDown: (event) { 97 | Navigator.of(context).push( 98 | MaterialPageRoute( 99 | builder: (BuildContext context) => ChartDialog( 100 | name: widget.model.name, 101 | messages: widget.model.messages, 102 | ), 103 | ), 104 | ); 105 | }, 106 | ); 107 | } 108 | 109 | /// 110 | /// 计算当前时间 111 | /// 112 | String _calTime() { 113 | final DateTime dateTime = DateTime.now(); 114 | final int hour = dateTime.hour; 115 | final int minute = dateTime.minute; 116 | if (minute.toInt() < 10) 117 | return "$hour:0$minute"; 118 | else 119 | return "$hour:$minute"; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/com/navigation/component/new_trend_star.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/models/new_trend_model.dart'; 6 | import 'package:flutter_app/com/navigation/page/subpage/webview.dart'; 7 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 8 | as fileHandler; 9 | 10 | class NewTrendStar extends StatefulWidget { 11 | @override 12 | State createState() => _NewTrendStarState(); 13 | } 14 | 15 | class _NewTrendStarState extends State 16 | with TickerProviderStateMixin { 17 | final List menuItems = ["移除"]; 18 | List items = []; 19 | AnimationController _controller; 20 | Animation _drawerContentsOpacity; 21 | @override 22 | void initState() { 23 | super.initState(); 24 | _controller = new AnimationController( 25 | vsync: this, 26 | duration: const Duration(milliseconds: 200), 27 | ); 28 | _drawerContentsOpacity = new CurvedAnimation( 29 | parent: new ReverseAnimation(_controller), 30 | curve: Curves.fastOutSlowIn, 31 | ); 32 | _loadData(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return RefreshIndicator( 38 | child: ListView.builder( 39 | itemBuilder: (BuildContext context, int index) => FadeTransition( 40 | opacity: _drawerContentsOpacity, 41 | child: Column( 42 | children: [ 43 | ListTile( 44 | leading: CircleAvatar( 45 | backgroundImage: 46 | AssetImage("assets/images/favorites.png"), 47 | ), 48 | title: Text(items[index].title), 49 | trailing: PopupMenuButton( 50 | onSelected: (value) { 51 | _removeItem(value); 52 | }, 53 | itemBuilder: (BuildContext context) => 54 | menuItems.map((value) { 55 | return PopupMenuItem( 56 | value: items[index].title, 57 | child: Text(value), 58 | ); 59 | }).toList()), 60 | onTap: () { 61 | Navigator.of(context).push( 62 | MaterialPageRoute( 63 | builder: (BuildContext context) => 64 | WebViewStateful( 65 | url: items[index].url, 66 | ), 67 | ), 68 | ); 69 | }, 70 | ), 71 | Divider( 72 | height: 3.0, 73 | ), 74 | ], 75 | ), 76 | ), 77 | itemCount: items.length, 78 | ), 79 | onRefresh: () async { 80 | return await _loadData(); 81 | }, 82 | ); 83 | } 84 | 85 | Future _loadData() async { 86 | if (items.length > 0) items.clear(); 87 | items = await fileHandler.loadCollects(); 88 | this.setState(() {}); 89 | return TickerFuture.complete(); 90 | } 91 | 92 | void _removeItem(String title) async { 93 | var result = await fileHandler.deleteCollects(title); 94 | if (result > 0) { 95 | int index = 0; 96 | items.forEach((model) { 97 | if (model.title == title) { 98 | return; 99 | } 100 | index++; 101 | }); 102 | this.setState(() { 103 | items.removeAt(index); 104 | }); 105 | } 106 | } 107 | 108 | @override 109 | void dispose() { 110 | super.dispose(); 111 | items.clear(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/com/navigation/component/search_contacts_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 4 | as handler; 5 | import 'package:flutter_app/com/navigation/page/subpage/chart_dialog.dart'; 6 | 7 | class SearchContactsItem extends StatefulWidget { 8 | final String _name; 9 | 10 | SearchContactsItem(this._name); 11 | 12 | @override 13 | State createState() => SearchContactsItemState(); 14 | } 15 | 16 | class SearchContactsItemState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return GestureDetector( 20 | child: Padding( 21 | padding: EdgeInsets.all(5.0), 22 | child: Column( 23 | children: [ 24 | Row( 25 | children: [ 26 | CircleAvatar( 27 | radius: 25.0, 28 | backgroundColor: Colors.green, 29 | child: Image.asset( 30 | "assets/images/icon.png", 31 | width: 45.0, 32 | height: 45.0, 33 | ), 34 | ), 35 | Expanded( 36 | child: Column( 37 | children: [ 38 | Row( 39 | children: [ 40 | Text( 41 | widget._name, 42 | style: TextStyle(fontSize: 20.0), 43 | ) 44 | ], 45 | ), 46 | SizedBox( 47 | width: 0.0, 48 | height: 2.0, 49 | ), 50 | Row( 51 | children: [Text("暂无个性签名")], 52 | ) 53 | ], 54 | ), 55 | ), 56 | ], 57 | ), 58 | Divider( 59 | height: 3.0, 60 | ), 61 | ], 62 | ), 63 | ), 64 | onTapDown: (e) { 65 | Navigator.push( 66 | context, 67 | MaterialPageRoute( 68 | builder: (BuildContext context) => ChartDialog( 69 | messages: handler.getChatRecorder(widget._name), 70 | name: widget._name, 71 | ), 72 | ), 73 | ); 74 | }, 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/com/navigation/component/search_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 5 | as handler; 6 | 7 | class UserItem extends StatefulWidget { 8 | final String userId; 9 | 10 | UserItem(this.userId); 11 | 12 | @override 13 | UserItemState createState() => UserItemState(); 14 | } 15 | 16 | class UserItemState extends State { 17 | UserItemState(); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Padding( 22 | padding: const EdgeInsets.only(top: 4.0, bottom: 4.0), 23 | child: Column( 24 | children: [ 25 | Row( 26 | children: [ 27 | Expanded( 28 | child: Row( 29 | children: [ 30 | Column( 31 | children: [ 32 | Padding( 33 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 34 | child: CircleAvatar( 35 | radius: 25.0, 36 | backgroundColor: Colors.green, 37 | child: Image.asset( 38 | "assets/images/icon.png", 39 | width: 45.0, 40 | height: 45.0, 41 | ), 42 | ), 43 | ), 44 | ], 45 | ), 46 | Expanded( 47 | child: Column( 48 | children: [ 49 | Row( 50 | children: [ 51 | Text( 52 | widget.userId, 53 | style: TextStyle(fontSize: 20.0), 54 | ), 55 | ], 56 | ), 57 | ], 58 | ), 59 | ), 60 | ], 61 | ), 62 | ), 63 | Align( 64 | alignment: Alignment.centerRight, 65 | child: IconButton( 66 | icon: InputDecorator( 67 | decoration: InputDecoration(icon: Icon(Icons.add)), 68 | ), 69 | onPressed: () { 70 | var message = { 71 | constants.type: constants.friend, 72 | constants.subtype: constants.request, 73 | constants.to: widget.userId, 74 | constants.message: "${handler.userId}请求添加你为好友!", 75 | constants.version: constants.currentVersion 76 | }; 77 | handler.sendRequest(message); 78 | Scaffold 79 | .of(context) 80 | .showSnackBar(SnackBar(content: Text("请求已发送"))); 81 | }, 82 | ), 83 | ), 84 | ], 85 | ), 86 | Divider(), 87 | ], 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/com/navigation/component/system_propel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_app/com/navigation/models/system_propel_model.dart'; 3 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' as handler; 6 | 7 | /// 8 | /// 9 | /// 10 | /// 系统推送消息 11 | /// @message 推送内容 12 | /// @type 推送类型(目前只有好友请求和好友回复两种类型) 13 | /// 14 | /// 15 | 16 | class SystemPropel extends StatefulWidget { 17 | final SystemPropelModel _model; 18 | 19 | SystemPropel(this._model); 20 | 21 | @override 22 | SystemPropelState createState() => SystemPropelState(); 23 | } 24 | 25 | class SystemPropelState extends State { 26 | @override 27 | Widget build(BuildContext context) { 28 | switch (widget._model.type) { 29 | case constants.response: 30 | return _showResponse(); 31 | default: 32 | return _showRequest(); 33 | } 34 | } 35 | 36 | Widget _showRequest() { 37 | return Container( 38 | margin: EdgeInsets.all(10.0), 39 | decoration: BoxDecoration( 40 | border: Border( 41 | left: BorderSide(style: BorderStyle.solid), 42 | bottom: BorderSide(style: BorderStyle.solid), 43 | right: BorderSide(style: BorderStyle.solid), 44 | ), 45 | ), 46 | child: Column( 47 | children: [ 48 | Container( 49 | alignment: Alignment.centerLeft, 50 | decoration: BoxDecoration(color: Colors.lightBlue), 51 | child: Padding( 52 | padding: const EdgeInsets.all(4.0), 53 | child: Text( 54 | "好友请求", 55 | style: TextStyle(fontSize: 23.0), 56 | ), 57 | ), 58 | ), 59 | Container( 60 | margin: EdgeInsets.only(top: 10.0, bottom: 10.0), 61 | child: Text( 62 | widget._model.message, 63 | style: TextStyle(fontSize: 20.0), 64 | ), 65 | ), 66 | Container( 67 | alignment: Alignment.center, 68 | margin: EdgeInsets.all(5.0), 69 | child: widget._model.isDeal 70 | ? (widget._model.isAccept 71 | ? Text( 72 | "你已经同意该好友请求", 73 | style: TextStyle(fontSize: 17.0), 74 | ) 75 | : Text( 76 | "你已经拒绝该好友请求", 77 | style: TextStyle(fontSize: 17.0), 78 | )) 79 | : _showHandler(), 80 | ), 81 | ], 82 | ), 83 | ); 84 | } 85 | 86 | Widget _showResponse() { 87 | return Container( 88 | margin: EdgeInsets.all(10.0), 89 | decoration: BoxDecoration( 90 | border: Border( 91 | left: BorderSide(style: BorderStyle.solid), 92 | bottom: BorderSide(style: BorderStyle.solid), 93 | right: BorderSide(style: BorderStyle.solid), 94 | ), 95 | ), 96 | child: Column( 97 | children: [ 98 | Container( 99 | alignment: Alignment.centerLeft, 100 | decoration: BoxDecoration(color: Colors.lightBlue), 101 | child: Text("好友回复",style: TextStyle(fontSize: 23.0),), 102 | ), 103 | Container( 104 | margin: EdgeInsets.all(10.0), 105 | child: Text(widget._model.message,style: TextStyle(fontSize: 17.0),), 106 | ), 107 | ], 108 | ), 109 | ); 110 | } 111 | Widget _showHandler() { 112 | return Row( 113 | mainAxisAlignment: MainAxisAlignment.center, 114 | children: [ 115 | GestureDetector( 116 | child: Container( 117 | decoration: BoxDecoration( 118 | border: Border( 119 | top: BorderSide(color: Colors.green), 120 | bottom: BorderSide(color: Colors.green), 121 | left: BorderSide(color: Colors.green), 122 | right: BorderSide(color: Colors.green))), 123 | margin: EdgeInsets.only(right: 10.0), 124 | child: Padding( 125 | padding: const EdgeInsets.all(8.0), 126 | child: Text( 127 | "同意", 128 | style: TextStyle(fontSize: 18.0), 129 | ), 130 | ), 131 | ), 132 | onTapDown: (e) { 133 | _sendResponse(true); 134 | }, 135 | ), 136 | GestureDetector( 137 | child: Container( 138 | decoration: BoxDecoration( 139 | border: Border( 140 | top: BorderSide(color: Colors.red), 141 | bottom: BorderSide(color: Colors.red), 142 | left: BorderSide(color: Colors.red), 143 | right: BorderSide(color: Colors.red))), 144 | child: Padding( 145 | padding: const EdgeInsets.all(8.0), 146 | child: Text( 147 | "拒绝", 148 | style: TextStyle(fontSize: 18.0), 149 | ), 150 | ), 151 | ), 152 | onTapDown: (e) { 153 | _sendResponse(false); 154 | }, 155 | ), 156 | ], 157 | ); 158 | } 159 | void _sendResponse(bool isAccept) { 160 | widget._model.isDeal = true; 161 | widget._model.isAccept = isAccept; 162 | Map message = { 163 | constants.type: constants.friend, 164 | constants.subtype: constants.response, 165 | constants.to: widget._model.to, 166 | constants.accept: isAccept, 167 | constants.version: constants.currentVersion 168 | }; 169 | handler.sendRequest(message); 170 | if(isAccept) handler.handlerContacts(widget._model.to); 171 | this.setState(() { 172 | handler.systemPropel=new List.from(handler.systemPropel); 173 | }); 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lib/com/navigation/component/user_info_item.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 6 | as handler; 7 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 8 | as fileHandler; 9 | 10 | Map userInfo = {}; 11 | 12 | class UserInfoItem extends StatefulWidget { 13 | @override 14 | State createState() => _UserInfoState(); 15 | } 16 | 17 | class _UserInfoState extends State { 18 | final List _items = ["账号", "用户名", "个性签名", "个人邮箱", "手机号", "个人网址"]; 19 | final List panels = []; 20 | @override 21 | void initState() { 22 | super.initState(); 23 | for (var item in _items) 24 | panels.add(InfoItem(title: item, itemValue: _checkData(item))); 25 | _loadInfo(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return ExpansionPanelList( 31 | expansionCallback: (int index, bool isExpanded) { 32 | this.setState(() { 33 | panels[index].isExpanded = !isExpanded; 34 | }); 35 | }, 36 | children: panels.map((panel) { 37 | return panel.buildExpansionPanel; 38 | }).toList(), 39 | ); 40 | } 41 | 42 | void _loadInfo() async { 43 | userInfo = await fileHandler.obtainUsers(userId: handler.userId); 44 | this.setState(() { 45 | userInfo = Map.from(userInfo); 46 | }); 47 | } 48 | 49 | String _checkData(String title) { 50 | switch (title) { 51 | case "用户名": 52 | return userInfo["userName"] ?? handler.userId; 53 | case "个性签名": 54 | return userInfo["userName"] ?? ""; 55 | case "个人邮箱": 56 | return userInfo["mail"] ?? ""; 57 | case "手机号": 58 | return userInfo["phone"] ?? ""; 59 | case "个人网址": 60 | return userInfo["website"] ?? ""; 61 | default: 62 | return handler.userId; 63 | } 64 | } 65 | } 66 | 67 | class InfoItem { 68 | String title; 69 | bool isExpanded = false; 70 | String itemValue; 71 | 72 | InfoItem({@required this.title, @required this.itemValue}); 73 | ExpansionPanel get buildExpansionPanel => ExpansionPanel( 74 | headerBuilder: (BuildContext context, bool expanded) => ListTile( 75 | leading: Text(title), 76 | title: Text(itemValue), 77 | ), 78 | body: Column( 79 | mainAxisAlignment: MainAxisAlignment.center, 80 | children: [ 81 | Container( 82 | margin: EdgeInsets.only(left: 30.0, right: 30.0), 83 | child: Row( 84 | children: [ 85 | Text( 86 | "修改:", 87 | style: TextStyle( 88 | fontSize: 18.0, 89 | color: Colors.red, 90 | ), 91 | ), 92 | Expanded( 93 | child: TextField(), 94 | ), 95 | ], 96 | ), 97 | ), 98 | Row( 99 | mainAxisAlignment: MainAxisAlignment.end, 100 | children: [ 101 | FlatButton( 102 | onPressed: () { 103 | onSave(); 104 | }, 105 | child: Text( 106 | "确定", 107 | style: TextStyle(color: Colors.lightBlue, fontSize: 20.0), 108 | ), 109 | ), 110 | ], 111 | ), 112 | ], 113 | ), 114 | isExpanded: title != "账号" ? isExpanded : false, 115 | ); 116 | void onSave() async {} 117 | } 118 | -------------------------------------------------------------------------------- /lib/com/navigation/models/contacts_list_model.dart: -------------------------------------------------------------------------------- 1 | class Entry { 2 | final String title; 3 | final List list; 4 | Entry(this.title, [this.list = const []]); 5 | } -------------------------------------------------------------------------------- /lib/com/navigation/models/message_list_item_model.dart: -------------------------------------------------------------------------------- 1 | class MessageListItemModel { 2 | List messages; 3 | String name; 4 | 5 | MessageListItemModel({List messags, String name}) { 6 | this.messages = messags; 7 | this.name = name; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/com/navigation/models/new_trend_model.dart: -------------------------------------------------------------------------------- 1 | class NewTrendModel { 2 | String title; 3 | String url; 4 | String cnbrief; 5 | String enbrief; 6 | 7 | NewTrendModel(this.title, this.url, this.cnbrief, this.enbrief); 8 | } 9 | -------------------------------------------------------------------------------- /lib/com/navigation/models/system_propel_model.dart: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 系统推送数据model 4 | /// @ _message 推送内容 5 | /// @ _type 推送类型 6 | /// @ _to 若需要回复,回复对象 7 | /// @ isDeal 该条消息是否处理 8 | /// @ isAccept 处理结果 9 | /// 10 | /// 11 | class SystemPropelModel{ 12 | final String message; 13 | final String type; 14 | final String to; 15 | bool isDeal = false; 16 | bool isAccept = false; 17 | SystemPropelModel(this.message, this.type, this.to); 18 | 19 | } -------------------------------------------------------------------------------- /lib/com/navigation/netwok/socket_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_app/com/navigation/models/contacts_list_model.dart'; 7 | import 'package:flutter_app/com/navigation/models/system_propel_model.dart'; 8 | import 'package:flutter_app/com/navigation/page/login.dart'; 9 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 10 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 11 | import 'package:http/http.dart'; 12 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 13 | as fileHandler; 14 | import 'package:flutter_app/com/navigation/utils/system_announce.dart' 15 | as system; 16 | import 'package:flutter_app/com/navigation/utils/application.dart' 17 | as application; 18 | 19 | /// 20 | /// 整个app向服务器发送请求和接收服务器回复的综合处理类, 21 | /// 此类强烈不建议与ui部分发生直接交互,只负责产生数据,某个页面需要数据直接获取即可. 22 | /// @socket 整个app socket 23 | /// @contactsList 储存联系人列表 24 | /// @systemPropel为系统推送消息 25 | /// @timer 定时器,定时向服务器发送数据以确保,连接正常可用 26 | /// @ messageList 消息列表 key代表好友名称 value代表和该好友聊天的所有消息 27 | /// 28 | /// 29 | /// 30 | 31 | Socket socket; 32 | List contactsList = []; 33 | List systemPropel = []; 34 | Map> messageList = {}; 35 | Map lastNum = {}; 36 | 37 | LoginState loginState; 38 | String userId; 39 | String password; 40 | State currentState; 41 | 42 | Timer _timer; 43 | 44 | /// 45 | /// 46 | /// 初始化连接 47 | /// 48 | 49 | void initSocket(Map req) async { 50 | if (socket != null) { 51 | socket.destroy(); 52 | socket.close(); 53 | } 54 | Socket.connect(constants.domain, constants.tcpPort) 55 | ..then((_socket) { 56 | if (_socket != null) { 57 | socket = _socket; 58 | socketHandler(); 59 | sendRequest(req); 60 | keepAlive(); 61 | } 62 | }) 63 | ..catchError((error) { 64 | showToast("连接服务器发生未知错误!"); 65 | dispose(); 66 | }) 67 | ..timeout(Duration(seconds: 5), onTimeout: () { 68 | showToast("连接超时....."); 69 | }); 70 | } 71 | 72 | /// 73 | /// 74 | /// 处理服务器返回来的数据id 75 | /// 76 | /// 77 | void socketHandler() { 78 | if (socket != null) { 79 | socket.done.catchError((error) { 80 | showToast("网络异常,请重新登录!"); 81 | dispose(); 82 | }); 83 | socket.transform(utf8.decoder).listen((data) { 84 | var result = json.decode(data); 85 | var type = result[constants.type]; 86 | switch (type) { 87 | case constants.user: 88 | handlerUser(result); 89 | break; 90 | case constants.friend: 91 | handlerFriend(result); 92 | break; 93 | case constants.message: 94 | handlerMessage(result); 95 | break; 96 | } 97 | }); 98 | } 99 | } 100 | 101 | /// 102 | /// 103 | /// 104 | /// 向服务器发送数据 105 | /// 106 | /// 107 | void sendRequest(Map message) async { 108 | socket.write(json.encode(message) + constants.end); 109 | } 110 | 111 | /// 112 | /// 113 | /// 处理type为user的所有socket事件 114 | /// 115 | /// 116 | 117 | void handlerUser(dynamic data) async { 118 | var subtype = data[constants.subtype]; 119 | if (subtype == "login") { 120 | var status = data["login"]; 121 | if (status) { 122 | showToast("登录成功"); 123 | fileHandler.addUser(userId, password); 124 | password = md5(password); 125 | var friends = data["friends"]; 126 | if (friends.length > 0) { 127 | List list = []; 128 | for (var friend in friends) list.add(Entry(friend["id"])); 129 | contactsList.add(Entry("我的好友", list)); 130 | } 131 | loginState.toUserCenter(); 132 | _offlineMessage(); 133 | } else { 134 | dispose(); 135 | showToast("用户名/密码错误!"); 136 | } 137 | } 138 | } 139 | 140 | /// 141 | /// 142 | ///处理type为friend的所有socket事件 143 | /// 144 | void handlerFriend(dynamic data) { 145 | bool isPlay = true; 146 | if (application.settings["voiceSwitch"] != null && 147 | application.settings["voiceSwitch"] == "false") isPlay = false; 148 | if (isPlay) system.playFriendMention(); 149 | var subtype = data[constants.subtype]; 150 | if (subtype == constants.request) { 151 | systemPropel.add(SystemPropelModel(data[constants.message], 152 | data[constants.subtype], data[constants.from])); 153 | } 154 | if (subtype == constants.response) { 155 | if (data[constants.accept]) { 156 | systemPropel.add(SystemPropelModel("${data[constants.from]}同意添加你为好友!", 157 | constants.response, data["from"])); 158 | handlerContacts(data[constants.from]); 159 | } else { 160 | systemPropel.add(SystemPropelModel("${data[constants.from]}拒绝添加你为好友!", 161 | constants.response, data["from"])); 162 | } 163 | } 164 | } 165 | 166 | /// 167 | /// 168 | /// 处理所有type为message的服务器返回数据 169 | /// 目前只有subtype为text的数据,在将来可能添加image voice等 170 | /// 171 | /// 172 | void handlerMessage(dynamic data) { 173 | bool isPlay = true; 174 | var subtype = data["subtype"]; 175 | var id = data[constants.from]; 176 | var body = data[constants.body]; 177 | if (application.settings["voiceSwitch"] != null && 178 | application.settings["voiceSwitch"] == "false") isPlay = false; 179 | if (subtype == constants.text) { 180 | if (isPlay) system.playMessageMention(); 181 | handlerMessageList(id, body); 182 | } 183 | } 184 | 185 | /// 186 | /// 如果存在该用户信息连天记录,更新记录 187 | /// 如果不存存在添加记录 188 | /// 189 | /// 190 | void handlerMessageList(String id, String message) async { 191 | if (messageList.containsKey(id)) { 192 | messageList.update(id, (old) { 193 | old.add(message); 194 | return old; 195 | }); 196 | } else { 197 | List list = List(); 198 | list.add(message); 199 | messageList.putIfAbsent(id, () => list); 200 | if (!lastNum.containsKey(id)) lastNum.putIfAbsent(id, () => 0); 201 | } 202 | } 203 | 204 | /// 205 | /// 如果好友列表中已经存在该用户,将不做任何处理,如果不存在将其加入到好友列表中去 206 | /// 目前服务端对好友默认是我的好友目录下,暂不支持分组,分组功能将会留在以后实现 207 | /// @ isExist 判断是否已经时好友关系 208 | /// 209 | void handlerContacts(String id) { 210 | var isExist = false; 211 | if (contactsList.length > 0) { 212 | var list = contactsList[0].list; 213 | for (Entry e in list) if (e.title == id) isExist = true; 214 | if (!isExist) { 215 | for (Entry entry in contactsList) { 216 | if (entry.title == "我的好友") entry.list.add(Entry(id)); 217 | } 218 | } 219 | } else { 220 | List list = List(); 221 | list.add(Entry(id)); 222 | contactsList.add(Entry("我的好友", list)); 223 | } 224 | } 225 | 226 | /// 227 | /// 通过用户id查找相应的聊天记录 228 | /// 229 | /// 230 | List getChatRecorder(String id) { 231 | List record = []; 232 | if (messageList.containsKey(id)) { 233 | messageList.forEach((key, list) { 234 | if (key == id) { 235 | record = list; 236 | return; 237 | } 238 | }); 239 | } else { 240 | messageList.putIfAbsent(id, () => record); 241 | } 242 | if (!lastNum.containsKey(id)) lastNum.putIfAbsent(id, () => 0); 243 | return record; 244 | } 245 | 246 | /// 247 | /// 清除指定用户的聊天记录 248 | /// 249 | /// 250 | void clearMessage(String id) async { 251 | bool isRemove = false; 252 | if (messageList.containsKey(id)) { 253 | messageList.forEach((key, value) { 254 | if (key == id) { 255 | value.clear(); 256 | isRemove = true; 257 | return; 258 | } 259 | }); 260 | } 261 | if (isRemove) messageList.remove(id); 262 | } 263 | 264 | /// 265 | /// 请求获取离线消息 266 | /// todo 将其加入到handler的Map中去 267 | /// 268 | /// 269 | void _offlineMessage() { 270 | Map requestMes = { 271 | constants.type: constants.user, 272 | constants.subtype: constants.offline, 273 | constants.id: userId, 274 | constants.password: password, 275 | constants.version: constants.currentVersion 276 | }; 277 | put("${constants.http}${constants.domain}/${constants.user}/${constants.offline}", 278 | body: "${json.encode(requestMes)}${constants.end}").then((response) { 279 | if (response.statusCode == 200) { 280 | var result = json.decode(utf8.decode(response.bodyBytes)); 281 | print("offline:$result"); 282 | } 283 | }); 284 | } 285 | 286 | /// 287 | /// 通过id获取上一次消息数目 288 | /// 289 | int obtainMessageNumber(String id) { 290 | int number = 0; 291 | if (lastNum.containsKey(id)) { 292 | lastNum.forEach((key, value) { 293 | if (key == id) { 294 | number = value; 295 | return; 296 | } 297 | }); 298 | } 299 | return number; 300 | } 301 | 302 | /// 303 | /// 304 | /// 通过id更新消息条数 305 | /// 306 | void updateMessageNumber(String id, int number) async { 307 | if (lastNum.containsKey(id)) { 308 | lastNum.update(id, (old) => number); 309 | } 310 | } 311 | 312 | /// 313 | /// 心跳机制保持连接可用 314 | /// 315 | /// 316 | void keepAlive() async { 317 | if (_timer != null && _timer.isActive) _timer; 318 | _timer = Timer.periodic(Duration(seconds: 5), (event) { 319 | handlerMessage({}); 320 | }); 321 | } 322 | 323 | /// 324 | /// 325 | ///退出登录时释放掉该用户所有信息 326 | /// 327 | /// 328 | 329 | void dispose() { 330 | socket?.destroy(); 331 | socket?.close(); 332 | contactsList?.clear(); 333 | systemPropel?.clear(); 334 | messageList?.clear(); 335 | lastNum?.clear(); 336 | if (_timer != null && _timer.isActive) _timer.cancel(); 337 | } 338 | -------------------------------------------------------------------------------- /lib/com/navigation/page/login.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_app/com/navigation/page/register.dart'; 5 | import 'package:flutter_app/com/navigation/page/user.dart'; 6 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 7 | as handler; 8 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 9 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 10 | import 'package:fluttertoast/fluttertoast.dart'; 11 | import 'package:flutter_app/com/navigation/utils/application.dart' 12 | as application; 13 | 14 | class Login extends StatefulWidget { 15 | @override 16 | LoginState createState() => LoginState(); 17 | } 18 | 19 | class LoginState extends State with TickerProviderStateMixin { 20 | final GlobalKey key = GlobalKey(); 21 | final List _counts = []; 22 | String _userId = ""; 23 | String _password = ""; 24 | bool isSelect = true; 25 | bool isExpanded = true; 26 | AnimationController _controller; 27 | Animation _drawerContentsOpacity; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return WillPopScope( 32 | child: 33 | isSelect && _counts.length > 0 ? _selectCounts() : _inputCountLogin(), 34 | onWillPop: () { 35 | SystemNavigator.pop(); 36 | }, 37 | ); 38 | } 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | handler.loginState = this; 44 | application.counts.forEach((count) { 45 | count.forEach((key, value) { 46 | if (key == "userId") _counts.add(value); 47 | }); 48 | }); 49 | _controller = new AnimationController( 50 | vsync: this, 51 | duration: const Duration(milliseconds: 200), 52 | ); 53 | _drawerContentsOpacity = new CurvedAnimation( 54 | parent: new ReverseAnimation(_controller), 55 | curve: Curves.fastOutSlowIn, 56 | ); 57 | if (_counts.length > 0) { 58 | _userId = _counts[0]; 59 | _password = application.findUser(_userId); 60 | } 61 | } 62 | 63 | void _vailUser() async { 64 | if (_userId.trim() == "") { 65 | showToast("用户名不能为空!"); 66 | return; 67 | } 68 | if (_password.trim() == "") { 69 | showToast("密码不能为空!"); 70 | return; 71 | } 72 | Map requestMes = { 73 | constants.type: constants.user, 74 | constants.subtype: constants.login, 75 | constants.id: _userId, 76 | constants.password: md5(_password), 77 | constants.version: constants.currentVersion, 78 | }; 79 | handler.userId = _userId; 80 | handler.password = _password; 81 | handler.initSocket(requestMes); 82 | } 83 | 84 | void toUserCenter() { 85 | Navigator.push(context, 86 | MaterialPageRoute(builder: (BuildContext context) => UserCenter())); 87 | } 88 | 89 | Widget _inputCountLogin() { 90 | return Scaffold( 91 | key: key, 92 | body: Container( 93 | margin: EdgeInsets.only(top: 100.0, left: 20.0, right: 20.0), 94 | child: ListView( 95 | children: [ 96 | Row( 97 | children: [ 98 | Expanded( 99 | child: Container( 100 | alignment: Alignment.centerLeft, 101 | child: Row( 102 | children: [ 103 | Image.asset( 104 | "assets/images/icon.png", 105 | width: 70.0, 106 | height: 70.0, 107 | ), 108 | Expanded( 109 | child: Padding( 110 | padding: const EdgeInsets.only(left: 8.0), 111 | child: Text( 112 | "畅聊", 113 | style: TextStyle(fontSize: 25.0), 114 | ), 115 | ), 116 | ), 117 | ], 118 | ), 119 | ), 120 | ), 121 | ], 122 | ), 123 | Row( 124 | children: [ 125 | Expanded( 126 | child: TextField( 127 | decoration: InputDecoration( 128 | labelText: "用户名", 129 | ), 130 | onChanged: (value) => _userId = value, 131 | ), 132 | ), 133 | ], 134 | ), 135 | Row( 136 | children: [ 137 | Expanded( 138 | child: TextField( 139 | decoration: InputDecoration(labelText: "密码"), 140 | onChanged: (value) => _password = value, 141 | obscureText: true, 142 | ), 143 | ) 144 | ], 145 | ), 146 | Row( 147 | children: [ 148 | Expanded( 149 | child: GestureDetector( 150 | child: Container( 151 | height: 50.0, 152 | margin: EdgeInsets.all(10.0), 153 | alignment: Alignment.center, 154 | decoration: BoxDecoration( 155 | color: Color.fromRGBO(0, 245, 255, 0.8), 156 | ), 157 | child: const Text( 158 | "登录", 159 | style: TextStyle( 160 | fontSize: 20.0, 161 | color: Color.fromRGBO(248, 248, 255, 1.0)), 162 | ), 163 | ), 164 | onTapDown: (e) { 165 | _vailUser(); 166 | }, 167 | ), 168 | ), 169 | ], 170 | ), 171 | Row( 172 | children: [ 173 | Expanded( 174 | child: GestureDetector( 175 | child: const Text( 176 | "选择账号", 177 | style: TextStyle(fontSize: 18.0, color: Colors.green), 178 | ), 179 | onTapDown: (e) { 180 | if (_counts.length > 0) { 181 | _userId = _counts[0]; 182 | _password = application.findUser(_userId); 183 | this.setState(() { 184 | isSelect = !isSelect; 185 | }); 186 | } else { 187 | showToast("暂无账号!"); 188 | } 189 | }, 190 | ), 191 | ), 192 | Expanded( 193 | child: Align( 194 | alignment: Alignment.centerRight, 195 | child: GestureDetector( 196 | child: const Text("新用户注册", 197 | style: 198 | TextStyle(fontSize: 18.0, color: Colors.green)), 199 | onTapDown: (e) { 200 | Navigator.push( 201 | context, 202 | MaterialPageRoute( 203 | builder: (BuildContext context) => Register())); 204 | }, 205 | ), 206 | ), 207 | ) 208 | ], 209 | ) 210 | ], 211 | ), 212 | ), 213 | ); 214 | } 215 | 216 | Widget _selectCounts() { 217 | return Scaffold( 218 | body: Center( 219 | child: Column( 220 | mainAxisAlignment: MainAxisAlignment.center, 221 | children: [ 222 | Container( 223 | margin: EdgeInsets.only(left: 10.0, bottom: 10.0, right: 10.0), 224 | alignment: Alignment.centerLeft, 225 | child: Text( 226 | "畅聊", 227 | style: TextStyle(fontSize: 30.0), 228 | ), 229 | ), 230 | ExpansionPanelList( 231 | expansionCallback: (int index, bool isExpanded) { 232 | this.setState(() { 233 | this.isExpanded = !isExpanded; 234 | }); 235 | }, 236 | children: [ 237 | ExpansionPanel( 238 | isExpanded: isExpanded, 239 | headerBuilder: (BuildContext context, bool isExpand) { 240 | return Row( 241 | children: [ 242 | CircleAvatar( 243 | backgroundImage: AssetImage("assets/images/head.png"), 244 | radius: 30.0, 245 | ), 246 | Expanded( 247 | child: Padding( 248 | padding: EdgeInsets.only(left: 10.0), 249 | child: Text( 250 | _userId, 251 | style: TextStyle(fontSize: 20.0), 252 | ), 253 | ), 254 | ), 255 | ], 256 | ); 257 | }, 258 | body: Column( 259 | children: [ 260 | Column( 261 | children: _counts.map((countName) { 262 | return FadeTransition( 263 | opacity: _drawerContentsOpacity, 264 | child: ListTile( 265 | leading: CircleAvatar( 266 | backgroundImage: 267 | AssetImage("assets/images/head.png"), 268 | ), 269 | title: Text(countName), 270 | onTap: () { 271 | this.setState(() { 272 | _userId = countName; 273 | _password = application.findUser(_userId); 274 | }); 275 | }, 276 | ), 277 | ); 278 | }).toList(), 279 | ), 280 | Row( 281 | mainAxisAlignment: MainAxisAlignment.end, 282 | children: [ 283 | new Container( 284 | margin: const EdgeInsets.only(right: 8.0), 285 | child: new FlatButton( 286 | onPressed: () { 287 | this.setState(() { 288 | isSelect = !isSelect; 289 | _userId = ""; 290 | _password = ""; 291 | }); 292 | }, 293 | textTheme: ButtonTextTheme.accent, 294 | child: const Text( 295 | "输入账号", 296 | style: TextStyle(fontSize: 20.0), 297 | ), 298 | ), 299 | ), 300 | new Container( 301 | margin: const EdgeInsets.only(right: 8.0), 302 | child: new FlatButton( 303 | onPressed: () { 304 | _vailUser(); 305 | }, 306 | textTheme: ButtonTextTheme.accent, 307 | child: const Text( 308 | "登录", 309 | style: TextStyle(fontSize: 20.0), 310 | ), 311 | ), 312 | ), 313 | ], 314 | ), 315 | ], 316 | ), 317 | ), 318 | ], 319 | ) 320 | ], 321 | ), 322 | ), 323 | ); 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /lib/com/navigation/page/register.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'dart:convert'; 4 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 5 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 6 | import 'package:http/http.dart'; 7 | import 'package:flutter_app/com/navigation/utils/application.dart' 8 | as application; 9 | 10 | class Register extends StatefulWidget { 11 | @override 12 | RegisterState createState() => RegisterState(); 13 | } 14 | 15 | class RegisterState extends State { 16 | String _userName = ""; 17 | String _password = ""; 18 | String _rePassword = ""; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | body: ListView( 24 | children: [ 25 | Container( 26 | margin: EdgeInsets.only(top: 20.0), 27 | child: Column( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | crossAxisAlignment: CrossAxisAlignment.center, 30 | children: [ 31 | Padding( 32 | padding: const EdgeInsets.all(8.0), 33 | child: Row( 34 | children: [ 35 | Expanded( 36 | child: Image.asset( 37 | "assets/images/icon.png", 38 | width: 100.0, 39 | height: 100.0, 40 | ), 41 | ) 42 | ], 43 | ), 44 | ), 45 | Row( 46 | children: [ 47 | Expanded( 48 | child: Center( 49 | child: Text( 50 | "加入我们,一起畅聊", 51 | style: TextStyle(fontSize: 25.0), 52 | ), 53 | ), 54 | ), 55 | ], 56 | ), 57 | ], 58 | ), 59 | ), 60 | Padding( 61 | padding: const EdgeInsets.all(8.0), 62 | child: Row( 63 | children: [ 64 | application.images["user"], 65 | Expanded( 66 | child: TextField( 67 | decoration: InputDecoration(labelText: "用户名"), 68 | onChanged: (value) { 69 | _userName = value; 70 | }, 71 | ), 72 | ), 73 | ], 74 | ), 75 | ), 76 | Padding( 77 | padding: const EdgeInsets.all(8.0), 78 | child: Row( 79 | children: [ 80 | application.images["password"], 81 | Expanded( 82 | child: TextField( 83 | obscureText: true, 84 | decoration: InputDecoration(labelText: "密码"), 85 | onChanged: (value) { 86 | _password = value; 87 | }, 88 | ), 89 | ) 90 | ], 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Row( 96 | children: [ 97 | application.images["password"], 98 | Expanded( 99 | child: TextField( 100 | obscureText: true, 101 | decoration: InputDecoration(labelText: "确认密码"), 102 | onChanged: (value) { 103 | _rePassword = value; 104 | }, 105 | ), 106 | ) 107 | ], 108 | ), 109 | ), 110 | Padding( 111 | padding: const EdgeInsets.all(8.0), 112 | child: Row( 113 | children: [ 114 | Expanded( 115 | child: Builder( 116 | builder: (BuildContext context) => GestureDetector( 117 | child: Builder( 118 | builder: (BuildContext context) => Container( 119 | height: 40.0, 120 | margin: EdgeInsets.only(top: 10.0), 121 | alignment: Alignment.center, 122 | decoration: BoxDecoration( 123 | color: Color.fromRGBO(0, 245, 255, 0.8), 124 | ), 125 | child: Text("注册"), 126 | ), 127 | ), 128 | onTapDown: (event) { 129 | _vailData(context); 130 | }, 131 | ), 132 | ), 133 | ), 134 | ], 135 | ), 136 | ) 137 | ], 138 | ), 139 | ); 140 | } 141 | 142 | void _sendRequest(String message, BuildContext context) { 143 | put("${constants.http}${constants.domain}/${constants.user}/${constants.register}", 144 | body: message).then((response) { 145 | if (response.statusCode == 200) { 146 | var result = json.decode(utf8.decode(response.bodyBytes)); 147 | if (result["register"]) { 148 | _showMessage(context, "注册成功!"); 149 | } else { 150 | _showMessage(context, result["info"]); 151 | } 152 | } else { 153 | _showMessage(context, "服务器异常,请重试!"); 154 | } 155 | }); 156 | } 157 | 158 | void _vailData(BuildContext context) { 159 | if (_userName.trim() != "" && 160 | _password.trim() != "" && 161 | _rePassword.trim() != "") { 162 | if (_password == _rePassword) { 163 | Map map = { 164 | constants.type: constants.user, 165 | constants.subtype: constants.register, 166 | constants.id: "$_userName", 167 | constants.password: md5("$_password"), 168 | constants.version: constants.currentVersion 169 | }; 170 | _sendRequest(json.encode(map) + constants.end, context); 171 | } else { 172 | _showMessage(context, "两次密码不一致!"); 173 | } 174 | } else { 175 | _showMessage(context, "尚有必要信息为空!"); 176 | } 177 | } 178 | 179 | void _showMessage(BuildContext context, String message) { 180 | Scaffold.of(context).showSnackBar(SnackBar(content: Text(message))); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/about_program.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/page/subpage/webview.dart'; 4 | import 'package:flutter_app/com/navigation/utils/application.dart' 5 | as application; 6 | 7 | class About extends StatefulWidget { 8 | @override 9 | AboutState createState() => AboutState(); 10 | } 11 | 12 | class AboutState extends State { 13 | final GlobalKey key = GlobalKey(); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Theme( 18 | data: ThemeData( 19 | primaryColor: application.settings["primaryColor"] == null 20 | ? Colors.lightBlue 21 | : Color( 22 | int.parse(application.settings["primaryColor"]), 23 | ), 24 | ), 25 | child: Scaffold( 26 | key: key, 27 | appBar: AppBar( 28 | title: new Text("关于"), 29 | centerTitle: true, 30 | ), 31 | body: Column( 32 | children: [ 33 | Container( 34 | alignment: Alignment.center, 35 | child: Column( 36 | children: [ 37 | Row( 38 | mainAxisAlignment: MainAxisAlignment.center, 39 | children: [ 40 | Padding( 41 | padding: const EdgeInsets.all(8.0), 42 | child: CircleAvatar( 43 | backgroundColor: Colors.deepPurple, 44 | minRadius: 60.0, 45 | child: Image.asset( 46 | "assets/images/icon.png", 47 | width: 100.0, 48 | height: 100.0, 49 | ), 50 | ), 51 | ), 52 | ], 53 | ), 54 | Row( 55 | mainAxisAlignment: MainAxisAlignment.center, 56 | children: [ 57 | Text( 58 | "版本:0.2", 59 | style: TextStyle(fontSize: 20.0), 60 | ), 61 | ], 62 | ), 63 | Padding( 64 | padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), 65 | child: Row( 66 | mainAxisAlignment: MainAxisAlignment.center, 67 | children: [ 68 | GestureDetector( 69 | child: Text( 70 | "开源地址:github", 71 | style: TextStyle( 72 | fontSize: 20.0, color: Colors.lightBlue), 73 | ), 74 | onTapDown: (e) { 75 | _openLink( 76 | "https://github.com/GZYangKui/flutter-IM"); 77 | }, 78 | ), 79 | ], 80 | ), 81 | ) 82 | ], 83 | ), 84 | ), 85 | Padding( 86 | padding: const EdgeInsets.all(8.0), 87 | child: Container( 88 | alignment: Alignment.center, 89 | child: Column( 90 | children: [ 91 | Row( 92 | children: [ 93 | Text( 94 | "版本更新", 95 | style: TextStyle(fontSize: 20.0), 96 | ), 97 | Expanded( 98 | child: Row( 99 | mainAxisAlignment: MainAxisAlignment.end, 100 | children: [ 101 | Builder( 102 | builder: (BuildContext context) => IconButton( 103 | icon: Icon(Icons.update), 104 | onPressed: () { 105 | _updateProgram(context); 106 | }, 107 | ), 108 | ), 109 | ], 110 | ), 111 | ), 112 | ], 113 | ), 114 | ], 115 | ), 116 | ), 117 | ), 118 | ], 119 | ), 120 | ), 121 | ); 122 | } 123 | 124 | void _updateProgram(BuildContext context) { 125 | Scaffold.of(context).showSnackBar(SnackBar(content: Text("检测更新中....."))); 126 | } 127 | 128 | void _openLink(String url) async { 129 | Navigator.of(context).push( 130 | MaterialPageRoute( 131 | builder: (BuildContext context) => WebViewStateful( 132 | url: url, 133 | ), 134 | ), 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/address_list.dart: -------------------------------------------------------------------------------- 1 | /// 2 | /// 通讯录好友界面 3 | /// 4 | /// -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/application_min.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/page/subpage/new_trend.dart'; 4 | 5 | class MinApplication extends StatefulWidget { 6 | @override 7 | MinApplicationState createState() => MinApplicationState(); 8 | } 9 | 10 | class MinApplicationState extends State 11 | with TickerProviderStateMixin { 12 | AnimationController _controller; 13 | Animation _drawerContentsOpacity; 14 | 15 | @override 16 | void initState() { 17 | super.initState(); 18 | _controller = new AnimationController( 19 | vsync: this, 20 | duration: const Duration(milliseconds: 200), 21 | ); 22 | _drawerContentsOpacity = new CurvedAnimation( 23 | parent: new ReverseAnimation(_controller), 24 | curve: Curves.fastOutSlowIn, 25 | ); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Tab( 31 | child: ListView( 32 | children: [ 33 | FadeTransition( 34 | opacity: _drawerContentsOpacity, 35 | child: Column( 36 | children: [ 37 | ListTile( 38 | leading: Icon(Icons.new_releases), 39 | title: Text( 40 | "新趋势", 41 | style: TextStyle(fontSize: 20.0), 42 | ), 43 | onTap: () => Navigator.of(context).push( 44 | MaterialPageRoute( 45 | builder: (BuildContext context) => NewTrend()), 46 | ), 47 | ), 48 | Divider( 49 | height: 3.0, 50 | ), 51 | ], 52 | ), 53 | ), 54 | ], 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/application_setting.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/utils/application.dart' 4 | as application; 5 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 6 | as fileHandler; 7 | 8 | /// 9 | /// Application设置界面 10 | /// 11 | /// 12 | /// 13 | class ApplicationSetting extends StatefulWidget { 14 | @override 15 | State createState() => ApplicationSettingState(); 16 | } 17 | 18 | class ApplicationSettingState extends State 19 | with TickerProviderStateMixin { 20 | final List colors = [ 21 | Colors.lightBlue, 22 | Colors.black, 23 | Colors.black12, 24 | Colors.deepPurpleAccent, 25 | Colors.teal, 26 | Colors.brown, 27 | Colors.red, 28 | Colors.green, 29 | Colors.deepOrange, 30 | Colors.deepPurple, 31 | Colors.yellow, 32 | Colors.purpleAccent, 33 | Colors.black54 34 | ]; 35 | AnimationController _controller; 36 | Animation _drawerContentsOpacity; 37 | bool isExpand = false; 38 | bool voiceSwitch = true; 39 | Color primaryColor = Colors.lightBlue; 40 | @override 41 | void initState() { 42 | super.initState(); 43 | _controller = new AnimationController( 44 | vsync: this, 45 | duration: const Duration(milliseconds: 200), 46 | ); 47 | _drawerContentsOpacity = new CurvedAnimation( 48 | parent: new ReverseAnimation(_controller), 49 | curve: Curves.fastOutSlowIn, 50 | ); 51 | if (application.settings["voiceSwitch"] != null && 52 | application.settings["voiceSwitch"] == "false") voiceSwitch = false; 53 | if (application.settings["primaryColor"] != null && 54 | Color(int.parse(application.settings["primaryColor"])) != primaryColor) 55 | primaryColor = Color(int.parse(application.settings["primaryColor"])); 56 | } 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | return Theme( 61 | data: ThemeData(primaryColor: primaryColor), 62 | child: Scaffold( 63 | appBar: AppBar( 64 | title: Text("设置"), 65 | centerTitle: true, 66 | ), 67 | body: ListView( 68 | children: [ 69 | FadeTransition( 70 | opacity: _drawerContentsOpacity, 71 | child: ListTile( 72 | leading: Icon(Icons.volume_up), 73 | title: Text( 74 | "新消息提醒", 75 | style: TextStyle(fontSize: 20.0), 76 | ), 77 | trailing: Switch( 78 | value: voiceSwitch, 79 | onChanged: (value) { 80 | this.setState(() { 81 | voiceSwitch = value; 82 | application.settings["voiceSwitch"] = value.toString(); 83 | }); 84 | }), 85 | ), 86 | ), 87 | Divider( 88 | height: 10.0, 89 | ), 90 | _selectTheme(), 91 | Divider( 92 | height: 10.0, 93 | ), 94 | ], 95 | ), 96 | ), 97 | ); 98 | } 99 | 100 | Widget _selectTheme() { 101 | return Row( 102 | children: [ 103 | Expanded( 104 | child: ExpansionPanelList( 105 | expansionCallback: (int index, bool isExpanded) { 106 | print(index); 107 | this.setState(() { 108 | this.isExpand = !isExpanded; 109 | }); 110 | }, 111 | children: [ 112 | ExpansionPanel( 113 | headerBuilder: (BuildContext context, bool isExpanded) { 114 | return Row( 115 | children: [ 116 | Container( 117 | margin: EdgeInsets.only(left: 10.0, right: 30.0), 118 | child: Icon(Icons.palette), 119 | ), 120 | Expanded( 121 | child: Row( 122 | children: [ 123 | Text( 124 | "主题", 125 | style: TextStyle(fontSize: 20.0), 126 | ), 127 | Container( 128 | margin: EdgeInsets.only(left: 10.0), 129 | width: 20.0, 130 | height: 20.0, 131 | color: primaryColor, 132 | ), 133 | ], 134 | ), 135 | ), 136 | ], 137 | ); 138 | }, 139 | body: Wrap( 140 | children: colors.map((color) { 141 | return GestureDetector( 142 | child: Container( 143 | margin: EdgeInsets.all(3.0), 144 | height: 40.0, 145 | width: 40.0, 146 | color: color, 147 | ), 148 | onTapDown: (event) { 149 | application.settings["primaryColor"] = 150 | color.value.toString(); 151 | this.setState(() { 152 | primaryColor = color; 153 | }); 154 | }, 155 | ); 156 | }).toList(), 157 | ), 158 | isExpanded: this.isExpand, 159 | ), 160 | ], 161 | ), 162 | ), 163 | ], 164 | ); 165 | } 166 | 167 | @override 168 | void dispose() { 169 | super.dispose(); 170 | fileHandler.updateConfig(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/chart_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/component/chart_message_item.dart'; 6 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 7 | as handler; 8 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 9 | import 'package:flutter_app/com/navigation/utils/application.dart' 10 | as application; 11 | 12 | class ChartDialog extends StatefulWidget { 13 | final List _list; 14 | final String _name; 15 | 16 | ChartDialog({ 17 | @required List messages, 18 | @required String name, 19 | }) : this._list = messages, 20 | this._name = name; 21 | 22 | @override 23 | ChartDialogState createState() => ChartDialogState(); 24 | } 25 | 26 | class ChartDialogState extends State { 27 | String _message = ""; 28 | Timer _timer; 29 | bool enable = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | handler.currentState = this; 35 | _timer = Timer.periodic(Duration(milliseconds: 30), (event) { 36 | this.setState(() {}); 37 | }); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Theme( 43 | data: ThemeData( 44 | primaryColor: application.settings["primaryColor"] == null 45 | ? Colors.lightBlue 46 | : Color( 47 | int.parse(application.settings["primaryColor"]), 48 | ), 49 | ), 50 | child: Scaffold( 51 | appBar: AppBar( 52 | title: Text(widget._name), 53 | centerTitle: true, 54 | actions: [ 55 | Tooltip( 56 | child: IconButton( 57 | icon: application.images["clear"], 58 | onPressed: () { 59 | _clearMessage(); 60 | }, 61 | ), 62 | message: "清除聊天记录", 63 | ), 64 | ], 65 | ), 66 | body: Column( 67 | children: [ 68 | Expanded( 69 | child: ListView.builder( 70 | itemBuilder: (BuildContext context, int index) => 71 | ChartMessageItem(widget._list[index]), 72 | itemCount: widget._list.length, 73 | ), 74 | ), 75 | Row( 76 | children: [ 77 | Expanded( 78 | child: TextField( 79 | decoration: InputDecoration( 80 | filled: true, 81 | suffixIcon: GestureDetector( 82 | child: Container( 83 | margin: EdgeInsets.all(8.0), 84 | width: 50.0, 85 | height: 20.0, 86 | decoration: BoxDecoration( 87 | color: enable ? Colors.white : Colors.grey, 88 | border: Border.all(color: Colors.red), 89 | borderRadius: BorderRadius.all( 90 | Radius.circular(5.0), 91 | ), 92 | ), 93 | alignment: Alignment.center, 94 | child: Text( 95 | "发送", 96 | style: TextStyle(fontSize: 16.0), 97 | ), 98 | ), 99 | onTapDown: (e) { 100 | _sendMessage(_message); 101 | }, 102 | ), 103 | ), 104 | onChanged: (value) { 105 | if (value != "") 106 | enable = true; 107 | else 108 | enable = false; 109 | _message = value; 110 | }, 111 | controller: MyController( 112 | text: _message, 113 | textSelection: TextSelection( 114 | baseOffset: _message.length, 115 | extentOffset: _message.length)), 116 | ), 117 | ), 118 | ], 119 | ), 120 | ], 121 | ), 122 | ), 123 | ); 124 | } 125 | 126 | void _sendMessage(String value) { 127 | if (enable) { 128 | widget._list.add(value + constants.messageOwn); 129 | var message = { 130 | constants.type: constants.message, 131 | constants.subtype: constants.text, 132 | constants.to: widget._name, 133 | constants.body: value, 134 | constants.version: constants.currentVersion 135 | }; 136 | handler.sendRequest(message); 137 | _message = ""; 138 | enable = false; 139 | } 140 | } 141 | 142 | void _clearMessage() { 143 | showDialog( 144 | context: context, 145 | builder: (BuildContext context) => SimpleDialog( 146 | title: Text("警告"), 147 | children: [ 148 | Column( 149 | children: [ 150 | Row( 151 | mainAxisAlignment: MainAxisAlignment.center, 152 | children: [ 153 | Expanded( 154 | child: Text( 155 | "确定要清空聊天记录?", 156 | textAlign: TextAlign.center, 157 | style: TextStyle(fontSize: 17.0), 158 | ), 159 | ), 160 | ], 161 | ), 162 | Row( 163 | mainAxisAlignment: MainAxisAlignment.center, 164 | children: [ 165 | RaisedButton( 166 | child: Text("取消"), 167 | onPressed: () => Navigator.pop(context), 168 | shape: StadiumBorder( 169 | side: BorderSide(color: Colors.red), 170 | ), 171 | ), 172 | SizedBox( 173 | width: 10.0, 174 | ), 175 | RaisedButton( 176 | child: Text("确定"), 177 | onPressed: () { 178 | handler.clearMessage(widget._name); 179 | Navigator.pop(context); 180 | }, 181 | shape: StadiumBorder( 182 | side: BorderSide(color: Colors.red), 183 | ), 184 | ), 185 | ], 186 | ) 187 | ], 188 | ), 189 | ], 190 | ), 191 | ); 192 | } 193 | 194 | @override 195 | void dispose() { 196 | super.dispose(); 197 | if (_timer != null && _timer.isActive) _timer.cancel(); 198 | if (widget._list.length != handler.obtainMessageNumber(widget._name)) 199 | handler.updateMessageNumber(widget._name, widget._list.length); 200 | if (widget._list.length == 0) handler.clearMessage(widget._name); 201 | } 202 | } 203 | 204 | class MyController extends TextEditingController { 205 | MyController({String text, TextSelection textSelection}) : super(text: text) { 206 | this.text = text; 207 | super.selection = textSelection; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/contacts.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/component/contacts_list_item.dart'; 6 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 7 | as handler; 8 | import 'package:flutter_app/com/navigation/page/subpage/contacts_search.dart'; 9 | 10 | /// 11 | /// 联系人界面 12 | /// 13 | /// 14 | class Contacts extends StatefulWidget { 15 | @override 16 | ContactsState createState() => ContactsState(); 17 | } 18 | 19 | class ContactsState extends State { 20 | Timer _timer; 21 | @override 22 | void initState() { 23 | super.initState(); 24 | _timer = Timer.periodic(Duration(seconds: 1), (event) { 25 | this.setState(() {}); 26 | }); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Tab( 32 | child: RefreshIndicator( 33 | child: Column( 34 | children: [ 35 | GestureDetector( 36 | child: Container( 37 | margin: EdgeInsets.all(3.0), 38 | decoration: BoxDecoration(color: Colors.grey), 39 | child: Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: Row( 42 | mainAxisAlignment: MainAxisAlignment.center, 43 | children: [ 44 | Icon(Icons.search), 45 | Text( 46 | "搜索", 47 | style: TextStyle(fontSize: 18.0), 48 | ), 49 | ], 50 | ), 51 | ), 52 | ), 53 | onTapDown: (e) { 54 | Navigator.of(context).push(MaterialPageRoute( 55 | builder: (BuildContext context) => ContactsSearch())); 56 | }, 57 | ), 58 | Expanded( 59 | child: ListView.builder( 60 | itemBuilder: (BuildContext context, int index) => 61 | ContactItem(handler.contactsList[index]), 62 | itemCount: handler.contactsList.length, 63 | ), 64 | ), 65 | ], 66 | ), 67 | onRefresh: () => TickerFuture.complete(), 68 | )); 69 | } 70 | 71 | @override 72 | void dispose() { 73 | super.dispose(); 74 | if (_timer != null && _timer.isActive) _timer.cancel(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/contacts_search.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_app/com/navigation/component/search_contacts_item.dart'; 5 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 6 | as handler; 7 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 8 | import 'package:flutter_app/com/navigation/utils/application.dart' 9 | as application; 10 | 11 | /// 12 | /// 联系人及聊天信息搜索 13 | /// 14 | /// 15 | class ContactsSearch extends StatefulWidget { 16 | @override 17 | State createState() => ContactsSearchState(); 18 | } 19 | 20 | class ContactsSearchState extends State { 21 | List _list = []; 22 | List _chipItem = []; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Theme( 32 | data: ThemeData( 33 | primaryColor: application.settings["primaryColor"] == null 34 | ? Colors.lightBlue 35 | : Color( 36 | int.parse(application.settings["primaryColor"]), 37 | ), 38 | ), 39 | child: Scaffold( 40 | appBar: AppBar( 41 | automaticallyImplyLeading: false, 42 | title: Container( 43 | child: Row( 44 | children: [ 45 | Expanded( 46 | child: TextField( 47 | decoration: InputDecoration( 48 | filled: true, 49 | prefixIcon: const Icon(Icons.search), 50 | hintText: "搜索"), 51 | textInputAction: TextInputAction.search, 52 | onSubmitted: (value) { 53 | if (value != "") { 54 | _search(value); 55 | _addChip(value); 56 | } else { 57 | showToast("搜索关键字不能为空!"); 58 | } 59 | }, 60 | ), 61 | ), 62 | GestureDetector( 63 | child: Container( 64 | margin: EdgeInsets.only(left: 3.0), 65 | child: Padding( 66 | padding: const EdgeInsets.all(8.0), 67 | child: Text( 68 | "取消", 69 | style: TextStyle(color: Colors.blueAccent), 70 | ), 71 | ), 72 | ), 73 | onTapDown: (e) { 74 | Navigator.pop(context); 75 | }, 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | body: Column( 82 | children: [ 83 | Expanded( 84 | child: _list.length == 0 ? _showCard() : _showContactList(), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | 92 | Widget _showCard() { 93 | return new Wrap( 94 | spacing: 10.0, // gap between adjacent chips 95 | runSpacing: 4.0, // gap between lines 96 | children: _chipItem, 97 | ); 98 | } 99 | 100 | Widget _showContactList() { 101 | return ListView.builder( 102 | itemBuilder: (BuildContext context, int index) => 103 | SearchContactsItem(_list[index]), 104 | itemCount: _list.length, 105 | ); 106 | } 107 | 108 | void _search(String value) async { 109 | if (_list.length > 0) _list.clear(); 110 | bool isExist = false; 111 | handler.contactsList.forEach((entry) { 112 | if (entry.list != null && entry.list.length > 0) { 113 | entry.list.forEach((e) { 114 | if (e.title == value) { 115 | print(e.title); 116 | _list.add(e.title); 117 | isExist = true; 118 | return; 119 | } 120 | }); 121 | } 122 | if (isExist) return; 123 | }); 124 | if (!isExist) showToast("找不到相关信息!"); 125 | this.setState(() {}); 126 | } 127 | 128 | void _addChip(String message) { 129 | InputChip clip = InputChip( 130 | label: Text(message), 131 | avatar: CircleAvatar( 132 | backgroundColor: Colors.blue.shade900, 133 | child: const Text("CL"), 134 | ), 135 | onPressed: () { 136 | _search(message); 137 | }, 138 | ); 139 | _chipItem.add(clip); 140 | } 141 | 142 | @override 143 | void dispose() { 144 | super.dispose(); 145 | if (_list.length > 0) _list.clear(); 146 | if (_chipItem.length > 0) _chipItem.clear(); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/message.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/component/message_list_item.dart'; 6 | import 'package:flutter_app/com/navigation/models/message_list_item_model.dart'; 7 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 8 | as handler; 9 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 10 | 11 | /// 12 | /// 13 | /// 消息界面 14 | /// 15 | /// 16 | class Message extends StatefulWidget { 17 | @override 18 | MessageState createState() => MessageState(); 19 | } 20 | 21 | class MessageState extends State { 22 | List _list = []; 23 | Timer _timer; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | 29 | _timer = Timer.periodic(Duration(seconds: 1), (e) { 30 | this.setState(() {}); 31 | }); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | _initData(); 37 | return Tab( 38 | child: RefreshIndicator( 39 | child: ListView.builder( 40 | itemBuilder: (BuildContext context, int index) => 41 | MessageListItem(_list[index]), 42 | itemCount: _list.length, 43 | ), 44 | onRefresh: () { 45 | return TickerFuture.complete(); 46 | }), 47 | ); 48 | } 49 | 50 | @override 51 | void dispose() { 52 | super.dispose(); 53 | if (_timer != null && _timer.isActive) _timer.cancel(); 54 | if (_list != null) _list.clear(); 55 | } 56 | 57 | /// 58 | /// 59 | /// 初始化数据 60 | /// 61 | /// 62 | void _initData() { 63 | if (_list.length > 0) _list.clear(); 64 | handler.messageList.forEach((key, value) { 65 | _list.add(MessageListItemModel(messags: value, name: key)); 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/new_trend.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/page/subpage/new_trend/artificial_intelligence.dart'; 4 | import 'package:flutter_app/com/navigation/page/subpage/new_trend/block_chain.dart'; 5 | import 'package:flutter_app/com/navigation/utils/application.dart' 6 | as application; 7 | 8 | /// 9 | /// 此页是new_trend的一个精简版界面 10 | /// 11 | /// 12 | class NewTrend extends StatefulWidget { 13 | @override 14 | State createState() => NewTrendState(); 15 | } 16 | 17 | class NewTrendState extends State 18 | with SingleTickerProviderStateMixin { 19 | TabController _tabController; 20 | int _currentIndex = 0; 21 | final List _tabs = [ 22 | ArtificialIntelligence(), 23 | BlockChain(), 24 | ]; 25 | @override 26 | void initState() { 27 | super.initState(); 28 | _tabController = 29 | TabController(initialIndex: 0, length: _tabs.length, vsync: this); 30 | _tabController.addListener(() { 31 | this.setState(() { 32 | this._currentIndex = _tabController.index; 33 | }); 34 | }); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return Theme( 40 | data: ThemeData( 41 | primaryColor: application.settings["primaryColor"] == null 42 | ? Colors.lightBlue 43 | : Color( 44 | int.parse(application.settings["primaryColor"]), 45 | ), 46 | ), 47 | child: Scaffold( 48 | appBar: AppBar( 49 | title: Text("新趋势"), 50 | centerTitle: true, 51 | automaticallyImplyLeading: false, 52 | ), 53 | body: TabBarView( 54 | children: _tabs, 55 | controller: _tabController, 56 | ), 57 | bottomNavigationBar: BottomNavigationBar( 58 | items: [ 59 | BottomNavigationBarItem( 60 | icon: Icon(Icons.threed_rotation), 61 | title: Text("人工智能"), 62 | ), 63 | BottomNavigationBarItem( 64 | icon: Icon(Icons.attach_money), 65 | title: Text("区块链"), 66 | ), 67 | ], 68 | onTap: (index) { 69 | this.setState(() { 70 | _currentIndex = index; 71 | _tabController.index = _currentIndex; 72 | }); 73 | }, 74 | currentIndex: _currentIndex, 75 | ), 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/new_trend/artificial_intelligence.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_app/com/navigation/models/new_trend_model.dart'; 7 | import 'package:flutter_app/com/navigation/page/subpage/webview.dart'; 8 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 9 | import 'package:http/http.dart'; 10 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 11 | import 'package:flutter_app/com/navigation/utils/application.dart' 12 | as application; 13 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 14 | as fileHandler; 15 | 16 | /// 17 | /// 此页是new_trend的一个精简版界面 18 | /// 之人工智能 19 | /// 20 | 21 | List _model = []; 22 | 23 | class ArtificialIntelligence extends StatefulWidget { 24 | @override 25 | State createState() => _ArtificialIntelligenceState(); 26 | } 27 | 28 | class _ArtificialIntelligenceState extends State 29 | with TickerProviderStateMixin { 30 | AnimationController _controller; 31 | Animation _drawerContentsOpacity; 32 | @override 33 | void initState() { 34 | super.initState(); 35 | _loadData(application.aiDate); 36 | _controller = new AnimationController( 37 | vsync: this, 38 | duration: const Duration(milliseconds: 200), 39 | ); 40 | _drawerContentsOpacity = new CurvedAnimation( 41 | parent: new ReverseAnimation(_controller), 42 | curve: Curves.fastOutSlowIn, 43 | ); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return Scaffold( 49 | body: RefreshIndicator( 50 | child: ListView( 51 | children: _model.map((ai) { 52 | return FadeTransition( 53 | opacity: _drawerContentsOpacity, 54 | child: Column( 55 | children: [ 56 | ArtificialIntelligenceItem(ai), 57 | Divider( 58 | height: 3.0, 59 | ), 60 | ], 61 | ), 62 | ); 63 | }).toList(), 64 | ), 65 | onRefresh: () async { 66 | return await _refreshLoadData(); 67 | }), 68 | floatingActionButton: FloatingActionButton( 69 | onPressed: () { 70 | _showSelectDate(); 71 | }, 72 | child: Icon(Icons.date_range), 73 | ), 74 | ); 75 | } 76 | 77 | void _showSelectDate() async { 78 | DateTime date = await showDatePicker( 79 | context: context, 80 | firstDate: DateTime.parse("2018-07-01"), 81 | initialDate: DateTime.tryParse(application.aiDate), 82 | lastDate: DateTime.now()); 83 | if (date != null) { 84 | application.aiDate = date.toString().split(" ")[0]; 85 | _loadData(date.toString().split(" ")[0]); 86 | } 87 | } 88 | 89 | Future _loadData(String date) async { 90 | _model.clear(); 91 | get("http://www.dashixiuxiu.cn/query_aitopics?crawltime=$date") 92 | .then((response) { 93 | if (response.statusCode == 200) { 94 | var result = json.decode(response.body); 95 | if (result[constants.status] == "success") { 96 | var data = result[constants.data]; 97 | if (data.length > 0) 98 | for (var item in data) { 99 | NewTrendModel news = NewTrendModel(item["cn_title"], item["url"], 100 | item["cn_brief"], item["en_brief"]); 101 | _model.add(news); 102 | } 103 | else { 104 | showToast("暂无信息!"); 105 | } 106 | this.setState(() {}); 107 | } else 108 | showToast("获取信息出错!"); 109 | } else { 110 | showToast("连接服务器失败!"); 111 | } 112 | }); 113 | } 114 | 115 | _refreshLoadData() async { 116 | await _loadData(application.aiDate).then((value) { 117 | showToast("刷新成功!"); 118 | }).catchError((error) { 119 | showToast("未知错误!"); 120 | }).whenComplete(() {}); 121 | return TickerFuture.complete(); 122 | } 123 | 124 | @override 125 | void dispose() { 126 | super.dispose(); 127 | _model?.clear(); 128 | } 129 | } 130 | 131 | class ArtificialIntelligenceItem extends StatefulWidget { 132 | final NewTrendModel model; 133 | 134 | ArtificialIntelligenceItem(this.model); 135 | 136 | @override 137 | State createState() => ArtificialIntelligenceItemState(); 138 | } 139 | 140 | class ArtificialIntelligenceItemState 141 | extends State { 142 | bool isStar = false; 143 | @override 144 | void initState() { 145 | super.initState(); 146 | _checkDate(); 147 | } 148 | 149 | @override 150 | Widget build(BuildContext context) { 151 | return ListTile( 152 | leading: CircleAvatar( 153 | backgroundImage: AssetImage("assets/images/new_trend/ai.jpeg"), 154 | ), 155 | title: Text(widget.model.title), 156 | trailing: IconButton( 157 | icon: Icon(!isStar ? Icons.star_border : Icons.star), 158 | onPressed: () { 159 | this.setState(() { 160 | isStar = !isStar; 161 | if (isStar) 162 | fileHandler.insertCollect(widget.model, "AI"); 163 | else 164 | fileHandler.deleteCollects(widget.model.title); 165 | }); 166 | }, 167 | ), 168 | onTap: () { 169 | Navigator.of(context).push( 170 | MaterialPageRoute( 171 | builder: (BuildContext context) => WebViewStateful( 172 | url: widget.model.url, 173 | ), 174 | ), 175 | ); 176 | }, 177 | ); 178 | } 179 | 180 | void _checkDate() async { 181 | bool isExist = await fileHandler.selectCollects(widget.model.title); 182 | if (isExist) { 183 | this.setState(() { 184 | isStar = isExist; 185 | }); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/new_trend/block_chain.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_app/com/navigation/models/new_trend_model.dart'; 7 | import 'package:flutter_app/com/navigation/page/subpage/webview.dart'; 8 | import 'package:flutter_app/com/navigation/utils/application.dart' 9 | as application; 10 | import 'package:http/http.dart'; 11 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 12 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 13 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 14 | as fileHandler; 15 | 16 | /// 17 | /// 此页是new_trend的一个精简版界面 18 | /// 之人区块链 19 | /// 20 | /// 21 | List _model = []; 22 | 23 | class BlockChain extends StatefulWidget { 24 | @override 25 | State createState() => BlockChainState(); 26 | } 27 | 28 | class BlockChainState extends State with TickerProviderStateMixin { 29 | AnimationController _controller; 30 | Animation _drawerContentsOpacity; 31 | @override 32 | void initState() { 33 | super.initState(); 34 | _loadData(application.blockDate); 35 | _controller = new AnimationController( 36 | vsync: this, 37 | duration: const Duration(milliseconds: 200), 38 | ); 39 | _drawerContentsOpacity = new CurvedAnimation( 40 | parent: new ReverseAnimation(_controller), 41 | curve: Curves.fastOutSlowIn, 42 | ); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return Scaffold( 48 | body: RefreshIndicator( 49 | child: ListView( 50 | children: _model.map((block) { 51 | return FadeTransition( 52 | opacity: _drawerContentsOpacity, 53 | child: Column( 54 | children: [ 55 | BlockChainItem(block), 56 | Divider( 57 | height: 3.0, 58 | ), 59 | ], 60 | ), 61 | ); 62 | }).toList(), 63 | ), 64 | onRefresh: () async { 65 | return await _refreshLoadData(); 66 | }), 67 | floatingActionButton: FloatingActionButton( 68 | onPressed: () { 69 | _showSelectDate(); 70 | }, 71 | child: Icon(Icons.date_range), 72 | ), 73 | ); 74 | } 75 | 76 | void _showSelectDate() async { 77 | DateTime date = await showDatePicker( 78 | context: context, 79 | firstDate: DateTime.parse("2018-07-01"), 80 | initialDate: DateTime.tryParse(application.blockDate), 81 | lastDate: DateTime.now()); 82 | if (date != null) { 83 | application.blockDate = date.toString().split(" ")[0]; 84 | _loadData(date.toString().split(" ")[0]); 85 | } 86 | } 87 | 88 | Future _loadData(String date) async { 89 | this.setState(() { 90 | _model.clear(); 91 | }); 92 | print(_model.length); 93 | get("http://www.dashixiuxiu.cn/query_cointelegraph?crawltime=$date") 94 | .then((response) { 95 | if (response.statusCode == 200) { 96 | var result = json.decode(response.body); 97 | if (result[constants.status] == "success") { 98 | var data = result[constants.data]; 99 | if (data.length > 0) 100 | for (var item in data) { 101 | _model.add(NewTrendModel(item["cn_title"], item["url"], 102 | item["cn_brief"], item["en_brief"])); 103 | } 104 | else { 105 | showToast("暂无信息!"); 106 | } 107 | this.setState(() {}); 108 | } else 109 | showToast("获取信息出错!"); 110 | } else { 111 | showToast("连接服务器失败!"); 112 | } 113 | }); 114 | } 115 | 116 | _refreshLoadData() async { 117 | await _loadData(application.blockDate).then((value) { 118 | showToast("刷新成功!"); 119 | }).catchError((error) { 120 | showToast("未知错误!"); 121 | }).whenComplete(() {}); 122 | return TickerFuture.complete(); 123 | } 124 | 125 | @override 126 | void dispose() { 127 | super.dispose(); 128 | _model.clear(); 129 | } 130 | } 131 | 132 | class BlockChainItem extends StatefulWidget { 133 | final NewTrendModel model; 134 | 135 | BlockChainItem(this.model); 136 | 137 | @override 138 | State createState() => BlockChainItemState(); 139 | } 140 | 141 | class BlockChainItemState extends State { 142 | bool isStar = false; 143 | @override 144 | void initState() { 145 | super.initState(); 146 | _checkDate(); 147 | } 148 | 149 | @override 150 | Widget build(BuildContext context) { 151 | return ListTile( 152 | leading: CircleAvatar( 153 | backgroundImage: AssetImage("assets/images/new_trend/block_chain.jpeg"), 154 | ), 155 | title: Text(widget.model.title), 156 | trailing: IconButton( 157 | icon: Icon(!isStar ? Icons.star_border : Icons.star), 158 | onPressed: () { 159 | this.setState(() { 160 | isStar = !isStar; 161 | if (isStar) 162 | fileHandler.insertCollect(widget.model, "BC"); 163 | else 164 | fileHandler.deleteCollects(widget.model.title); 165 | }); 166 | }, 167 | ), 168 | onTap: () { 169 | Navigator.of(context).push( 170 | MaterialPageRoute( 171 | builder: (BuildContext context) => WebViewStateful( 172 | url: widget.model.url, 173 | ), 174 | ), 175 | ); 176 | }, 177 | ); 178 | } 179 | 180 | void _checkDate() async { 181 | bool isExist = await fileHandler.selectCollects(widget.model.title); 182 | if (isExist) { 183 | this.setState(() { 184 | isStar = isExist; 185 | }); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/picture_select.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | /// 7 | /// 图片选择器 8 | /// 9 | class SelectPicture extends StatefulWidget { 10 | final String title; 11 | 12 | SelectPicture(this.title); 13 | 14 | @override 15 | State createState() => SelectPictureState(); 16 | } 17 | 18 | class SelectPictureState extends State { 19 | final List tabs = ["美女", "帅哥", "风景"]; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return DefaultTabController( 24 | length: tabs.length, 25 | child: Scaffold( 26 | appBar: AppBar( 27 | title: Text(widget.title), 28 | centerTitle: true, 29 | bottom: TabBar( 30 | tabs: tabs.map((title) { 31 | return Tab( 32 | text: title, 33 | ); 34 | }).toList()), 35 | ), 36 | body: TabBarView(children: [ 37 | GridView.count( 38 | crossAxisCount: 5, 39 | children: [], 40 | ), 41 | GridView.count( 42 | crossAxisCount: 5, 43 | children: [], 44 | ), 45 | GridView.count( 46 | crossAxisCount: 5, 47 | children: [], 48 | ), 49 | ]), 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/search.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/component/search_item.dart'; 6 | import 'package:flutter_app/com/navigation/page/subpage/sweep_code.dart'; 7 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 8 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 9 | as handler; 10 | import 'package:http/http.dart'; 11 | import 'package:flutter_app/com/navigation/utils/application.dart' 12 | as application; 13 | 14 | class Search extends StatefulWidget { 15 | @override 16 | State createState() => SearchState(); 17 | } 18 | 19 | class SearchState extends State with SingleTickerProviderStateMixin { 20 | GlobalKey key = GlobalKey(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | handler.currentState = this; 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Theme( 31 | data: ThemeData( 32 | primaryColor: application.settings["primaryColor"] == null 33 | ? Colors.lightBlue 34 | : Color( 35 | int.parse(application.settings["primaryColor"]), 36 | ), 37 | ), 38 | child: Scaffold( 39 | key: key, 40 | appBar: AppBar( 41 | title: Text("添加"), 42 | centerTitle: true, 43 | ), 44 | body: Column( 45 | children: [ 46 | Container( 47 | margin: EdgeInsets.only( 48 | top: 10.0, left: 3.0, right: 3.0, bottom: 10.0), 49 | decoration: BoxDecoration( 50 | color: Color.fromRGBO(211, 211, 211, 0.8), 51 | ), 52 | child: Row( 53 | children: [ 54 | Expanded( 55 | child: GestureDetector( 56 | child: Padding( 57 | padding: const EdgeInsets.all(8.0), 58 | child: Container( 59 | child: Row( 60 | children: [ 61 | Icon(Icons.search), 62 | Expanded( 63 | child: Text( 64 | "搜索用户名", 65 | style: TextStyle(fontSize: 20.0), 66 | ), 67 | ), 68 | ], 69 | ), 70 | ), 71 | ), 72 | onTapDown: (event) { 73 | Navigator.of(context).push(MaterialPageRoute( 74 | builder: (BuildContext context) => SearchDialog())); 75 | }, 76 | ), 77 | ), 78 | ], 79 | ), 80 | ), 81 | Expanded( 82 | child: _showMenu(), 83 | ), 84 | ], 85 | ), 86 | ), 87 | ); 88 | } 89 | 90 | Widget _showMenu() { 91 | return ListView( 92 | children: [ 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: GestureDetector( 96 | child: Row( 97 | children: [ 98 | Icon(Icons.phone_iphone), 99 | Expanded( 100 | child: Column( 101 | mainAxisAlignment: MainAxisAlignment.center, 102 | crossAxisAlignment: CrossAxisAlignment.start, 103 | children: [ 104 | Text( 105 | "手机联系人", 106 | style: TextStyle(fontSize: 22.0), 107 | ), 108 | Text("添加或邀请通讯录中的好友"), 109 | ], 110 | ), 111 | ), 112 | ], 113 | ), 114 | onTapDown: (e) { 115 | key.currentState 116 | .showSnackBar(SnackBar(content: Text("该功能将在下一个版本开放!"))); 117 | }, 118 | ), 119 | ), 120 | Padding( 121 | padding: const EdgeInsets.all(8.0), 122 | child: GestureDetector( 123 | child: Row( 124 | children: [ 125 | Icon(Icons.swap_calls), 126 | Expanded( 127 | child: Column( 128 | mainAxisAlignment: MainAxisAlignment.center, 129 | crossAxisAlignment: CrossAxisAlignment.start, 130 | children: [ 131 | Text( 132 | "扫码", 133 | style: TextStyle(fontSize: 22.0), 134 | ), 135 | Text("扫描二维码名片"), 136 | ], 137 | ), 138 | ), 139 | ], 140 | ), 141 | onTapDown: (e) { 142 | Navigator.of(context).push(MaterialPageRoute( 143 | builder: (BuildContext context) => SweepCode())); 144 | }, 145 | ), 146 | ), 147 | ], 148 | ); 149 | } 150 | 151 | @override 152 | void dispose() { 153 | super.dispose(); 154 | } 155 | } 156 | 157 | class SearchDialog extends StatefulWidget { 158 | @override 159 | SearchDialogState createState() => SearchDialogState(); 160 | } 161 | 162 | class SearchDialogState extends State { 163 | List list = []; 164 | String _keyword = ""; 165 | GlobalKey key = GlobalKey(); 166 | 167 | @override 168 | Widget build(BuildContext context) { 169 | return Theme( 170 | data: ThemeData( 171 | primaryColor: application.settings["primaryColor"] == null 172 | ? Colors.lightBlue 173 | : Color(int.parse(application.settings["primaryColor"]))), 174 | child: Scaffold( 175 | key: key, 176 | appBar: AppBar( 177 | title: TextField( 178 | style: TextStyle(fontSize: 20.0), 179 | decoration: InputDecoration(hintText: "搜索用户名"), 180 | onSubmitted: (value) { 181 | _keyword = value; 182 | _search(); 183 | }, 184 | ), 185 | leading: IconButton( 186 | icon: Icon(Icons.chevron_left), 187 | onPressed: () => Navigator.pop(context), 188 | padding: EdgeInsets.all(0.0), 189 | ), 190 | /* backgroundColor: Colors.lightBlue,*/ 191 | ), 192 | body: ListView.builder( 193 | itemBuilder: (BuildContext context, int index) => 194 | UserItem(list[index]), 195 | itemCount: list.length, 196 | ), 197 | ), 198 | ); 199 | } 200 | 201 | void _search() { 202 | if (_keyword != null && _keyword.trim() != "") { 203 | list.clear(); 204 | var message = { 205 | constants.type: constants.search, 206 | constants.subtype: constants.info, 207 | constants.id: handler.userId, 208 | constants.password: handler.password, 209 | constants.keyword: _keyword, 210 | constants.version: constants.currentVersion 211 | }; 212 | put("${constants.http}${constants.domain}/${constants.search}/${constants.info}", 213 | body: json.encode(message) + constants.end).then((response) { 214 | if (response.statusCode == 200) { 215 | var _result = json.decode(utf8.decode(response.bodyBytes)); 216 | if (_result["user"] != null) { 217 | list.clear(); 218 | list.add(_result["user"]["id"]); 219 | } else { 220 | key.currentState.showSnackBar(SnackBar(content: Text("该用户不存在"))); 221 | } 222 | this.setState(() {}); 223 | } else { 224 | key.currentState.showSnackBar(SnackBar(content: Text("服务器错误"))); 225 | } 226 | }); 227 | _keyword = ""; 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/sweep_code.dart: -------------------------------------------------------------------------------- 1 | /// 2 | /// 扫码添加好友界面 3 | /// 4 | import 'package:flutter/material.dart'; 5 | import 'package:camera/camera.dart'; 6 | 7 | class SweepCode extends StatefulWidget { 8 | @override 9 | State createState() => _SweepCodeState(); 10 | } 11 | 12 | class _SweepCodeState extends State { 13 | CameraController controller; 14 | List cameras; 15 | bool _isInitial = false; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _initCamera(); 21 | } 22 | 23 | @override 24 | void dispose() { 25 | controller?.dispose(); 26 | super.dispose(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | if (!_isInitial) { 32 | return Center( 33 | child: Column( 34 | mainAxisAlignment: MainAxisAlignment.center, 35 | children: [CircularProgressIndicator(), Text("相机正在初始化在...")], 36 | ), 37 | ); 38 | } else { 39 | if (!controller.value.isInitialized) { 40 | return new Container(); 41 | } 42 | return new AspectRatio( 43 | aspectRatio: controller.value.aspectRatio, 44 | child: new CameraPreview(controller)); 45 | } 46 | } 47 | 48 | void _initCamera() async { 49 | cameras = await availableCameras(); 50 | controller = new CameraController(cameras[0], ResolutionPreset.medium); 51 | controller.initialize().then((_) { 52 | if (!mounted) { 53 | return; 54 | } 55 | setState(() { 56 | _isInitial = true; 57 | }); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/system_inform.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_app/com/navigation/component/system_propel.dart'; 6 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 7 | as handler; 8 | import 'package:flutter_app/com/navigation/utils/application.dart' 9 | as application; 10 | 11 | /// 12 | /// 13 | ///系统推送界面 14 | /// 15 | /// 16 | 17 | class SystemInform extends StatefulWidget { 18 | @override 19 | State createState() => SystemInformState(); 20 | } 21 | 22 | class SystemInformState extends State { 23 | @override 24 | void initState() { 25 | super.initState(); 26 | handler.currentState = this; 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Theme( 32 | data: ThemeData( 33 | primaryColor: application.settings["primaryColor"] == null 34 | ? Colors.lightBlue 35 | : Color(int.parse(application.settings["primaryColor"]))), 36 | child: Scaffold( 37 | appBar: AppBar( 38 | title: Text("系统消息"), 39 | centerTitle: true, 40 | ), 41 | body: handler.systemPropel.length > 0 ? _showPropel() : _showEmpty(), 42 | ), 43 | ); 44 | } 45 | 46 | Widget _showPropel() { 47 | return ListView.builder( 48 | itemBuilder: (BuildContext context, int index) => 49 | SystemPropel(handler.systemPropel[index]), 50 | itemCount: handler.systemPropel.length, 51 | ); 52 | } 53 | 54 | Widget _showEmpty() { 55 | return Container( 56 | margin: EdgeInsets.only( 57 | top: (ui.window.physicalSize.height / ui.window.devicePixelRatio) * 58 | 0.2), 59 | alignment: Alignment.center, 60 | child: Column( 61 | children: [ 62 | Image.asset("assets/images/icon.png"), 63 | Text( 64 | "暂无系统消息", 65 | style: TextStyle(fontSize: 20.0), 66 | ), 67 | ], 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/user_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/com/navigation/component/new_trend_star.dart'; 4 | import 'package:flutter_app/com/navigation/component/user_info_item.dart'; 5 | import 'package:flutter_app/com/navigation/page/subpage/picture_select.dart'; 6 | import 'package:flutter_app/com/navigation/utils/application.dart' 7 | as application; 8 | 9 | class UserInfo extends StatefulWidget { 10 | final String userName; 11 | 12 | UserInfo(this.userName); 13 | 14 | @override 15 | State createState() => UserInfoState(); 16 | } 17 | 18 | class TabItem { 19 | String title; 20 | IconData icon; 21 | 22 | TabItem({this.icon, this.title}); 23 | } 24 | 25 | class UserInfoState extends State 26 | with SingleTickerProviderStateMixin { 27 | final List menuItem = ["更换背景"]; 28 | final List _tabs = [ 29 | TabItem(title: "个人信息", icon: Icons.info), 30 | TabItem(title: "收藏", icon: Icons.collections) 31 | ]; 32 | TabController _controller; 33 | 34 | Color primaryColor = Colors.lightBlue; 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | primaryColor = application.settings["primaryColor"] == null 40 | ? Colors.lightBlue 41 | : Color( 42 | int.parse(application.settings["primaryColor"]), 43 | ); 44 | _controller = TabController(length: 2, vsync: this); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return Theme( 50 | data: ThemeData(primaryColor: primaryColor), 51 | child: Scaffold( 52 | appBar: AppBar( 53 | flexibleSpace: Stack( 54 | fit: StackFit.expand, 55 | children: [ 56 | application.images["user_background"], 57 | ], 58 | ), 59 | bottom: TabBar( 60 | tabs: _tabs.map((tab) { 61 | return Tab( 62 | text: tab.title, 63 | icon: Icon(tab.icon), 64 | ); 65 | }).toList(), 66 | controller: _controller, 67 | ), 68 | ), 69 | body: TabBarView( 70 | children: [ 71 | ListView( 72 | children: [ 73 | UserInfoItem(), 74 | ], 75 | ), 76 | NewTrendStar(), 77 | ], 78 | controller: _controller, 79 | ), 80 | ), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/com/navigation/page/subpage/webview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 6 | import 'package:flutter_app/com/navigation/utils/application.dart' 7 | as application; 8 | import 'package:url_launcher/url_launcher.dart'; 9 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 10 | 11 | class WebViewStateful extends StatefulWidget { 12 | final String _url; 13 | 14 | WebViewStateful({String url}) : this._url = url; 15 | 16 | @override 17 | State createState() => WebViewState(); 18 | } 19 | 20 | class WebViewState extends State { 21 | final FlutterWebviewPlugin manager = FlutterWebviewPlugin(); 22 | String _title = "loading......"; 23 | String _url; 24 | @override 25 | void initState() { 26 | super.initState(); 27 | initWebView(); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Theme( 33 | data: ThemeData( 34 | primaryColor: application.settings["primaryColor"] == null 35 | ? Colors.lightBlue 36 | : Color( 37 | int.parse(application.settings["primaryColor"]), 38 | ), 39 | ), 40 | child: WebviewScaffold( 41 | url: widget._url, 42 | appBar: AppBar( 43 | title: Container( 44 | alignment: Alignment.center, 45 | width: (window.physicalSize.width / window.devicePixelRatio) * 0.6, 46 | child: Text( 47 | _title, 48 | ), 49 | ), 50 | centerTitle: true, 51 | actions: [ 52 | Tooltip( 53 | message: "外部浏览器打开", 54 | child: IconButton( 55 | icon: Icon(Icons.zoom_out_map), 56 | onPressed: () { 57 | _openOutBrowser(); 58 | }, 59 | ), 60 | ) 61 | ], 62 | ), 63 | ), 64 | ); 65 | } 66 | 67 | void initWebView() async { 68 | manager.onUrlChanged.listen((url) { 69 | _url = url; 70 | manager.evalJavascript("document.title").then((title) { 71 | title = title.replaceAll("\"", ""); 72 | this.setState(() { 73 | _title = title; 74 | }); 75 | }); 76 | }); 77 | } 78 | 79 | void _openOutBrowser() async { 80 | String url = _url ?? widget._url; 81 | if (await canLaunch(url)) { 82 | await launch(url); 83 | } else { 84 | showToast("调起系统浏览器失败!"); 85 | } 86 | } 87 | 88 | @override 89 | void dispose() { 90 | super.dispose(); 91 | manager.close(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/com/navigation/page/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_app/com/navigation/page/subpage/about_program.dart'; 5 | import 'package:flutter_app/com/navigation/page/subpage/application_min.dart'; 6 | import 'package:flutter_app/com/navigation/page/subpage/application_setting.dart'; 7 | import 'package:flutter_app/com/navigation/page/subpage/contacts.dart'; 8 | import 'package:flutter_app/com/navigation/page/subpage/message.dart'; 9 | import 'package:flutter_app/com/navigation/page/subpage/search.dart'; 10 | import 'package:flutter_app/com/navigation/page/login.dart'; 11 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 12 | as handler; 13 | import 'package:flutter_app/com/navigation/page/subpage/system_inform.dart'; 14 | import 'package:flutter_app/com/navigation/page/subpage/user_info.dart'; 15 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 16 | import 'package:flutter_app/com/navigation/utils/application.dart' 17 | as application; 18 | 19 | class UserCenter extends StatefulWidget { 20 | @override 21 | UserCenterState createState() => UserCenterState(); 22 | } 23 | 24 | class UserCenterState extends State 25 | with SingleTickerProviderStateMixin { 26 | static final List _tabs = [ 27 | Message(), 28 | Contacts(), 29 | MinApplication() 30 | ]; 31 | TabController _tabController; 32 | int _currentIndex = 0; 33 | final List _titles = ["消息", "联系人", "小应用"]; 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Theme( 38 | data: ThemeData( 39 | primaryColor: application.settings["primaryColor"] == null 40 | ? Colors.lightBlue 41 | : Color(int.parse(application.settings["primaryColor"]))), 42 | child: WillPopScope( 43 | child: Scaffold( 44 | appBar: AppBar( 45 | centerTitle: true, 46 | title: new Text(_titles[_currentIndex]), 47 | actions: [ 48 | IconButton( 49 | icon: Icon(Icons.add), 50 | onPressed: () { 51 | Navigator.push( 52 | context, 53 | MaterialPageRoute( 54 | builder: (BuildContext context) => Search())); 55 | }, 56 | ), 57 | IconButton( 58 | icon: Icon(Icons.notifications), 59 | onPressed: () => Navigator.of(context).push(MaterialPageRoute( 60 | builder: (BuildContext context) => SystemInform())), 61 | ), 62 | ], 63 | ), 64 | drawer: Drawer( 65 | child: DrawerItems(), 66 | ), 67 | body: TabBarView( 68 | children: _tabs, 69 | controller: _tabController, 70 | ), 71 | bottomNavigationBar: BottomNavigationBar( 72 | items: [ 73 | BottomNavigationBarItem( 74 | icon: Icon(Icons.message), title: Text("消息")), 75 | BottomNavigationBarItem( 76 | icon: Icon(Icons.perm_contact_calendar), title: Text("联系人")), 77 | BottomNavigationBarItem( 78 | icon: Icon(Icons.streetview), title: Text("小应用")), 79 | ], 80 | onTap: (index) { 81 | this.setState(() { 82 | _currentIndex = index; 83 | }); 84 | _tabController.animateTo(index); 85 | }, 86 | currentIndex: _currentIndex, 87 | ), 88 | ), 89 | onWillPop: () { 90 | SystemNavigator.pop(); 91 | }, 92 | ), 93 | ); 94 | } 95 | 96 | @override 97 | void initState() { 98 | super.initState(); 99 | SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]); 100 | handler.currentState = this; 101 | _tabController = 102 | TabController(initialIndex: 0, length: _tabs.length, vsync: this); 103 | _tabController.addListener(() { 104 | this.setState(() { 105 | this._currentIndex = _tabController.index; 106 | }); 107 | }); 108 | } 109 | 110 | @override 111 | void dispose() { 112 | super.dispose(); 113 | _tabController.dispose(); 114 | } 115 | } 116 | 117 | class DrawerItems extends StatefulWidget { 118 | @override 119 | State createState() => DrawerItemsState(); 120 | } 121 | 122 | class DrawerItemsState extends State 123 | with TickerProviderStateMixin { 124 | AnimationController _controller; 125 | Animation _drawerContentsOpacity; 126 | @override 127 | void initState() { 128 | super.initState(); 129 | _controller = new AnimationController( 130 | vsync: this, 131 | duration: const Duration(milliseconds: 200), 132 | ); 133 | _drawerContentsOpacity = new CurvedAnimation( 134 | parent: new ReverseAnimation(_controller), 135 | curve: Curves.fastOutSlowIn, 136 | ); 137 | } 138 | 139 | @override 140 | Widget build(BuildContext context) { 141 | return ListView( 142 | children: [ 143 | Row( 144 | children: [ 145 | Expanded( 146 | child: Container( 147 | alignment: Alignment.centerLeft, 148 | child: Column( 149 | children: [ 150 | Row( 151 | mainAxisAlignment: MainAxisAlignment.start, 152 | crossAxisAlignment: CrossAxisAlignment.center, 153 | children: [ 154 | Expanded( 155 | child: UserAccountsDrawerHeader( 156 | accountName: Text(handler.userId), 157 | accountEmail: const Text("752544765@qqcom"), 158 | currentAccountPicture: GestureDetector( 159 | child: const CircleAvatar( 160 | backgroundImage: 161 | AssetImage("assets/images/head.png"), 162 | ), 163 | onTapDown: (e) { 164 | Navigator.of(context).push( 165 | MaterialPageRoute( 166 | builder: (BuildContext context) => 167 | UserInfo(handler.userId), 168 | ), 169 | ); 170 | }, 171 | ), 172 | otherAccountsPictures: [], 173 | ), 174 | ), 175 | ], 176 | ), 177 | ], 178 | ), 179 | ), 180 | ), 181 | ], 182 | ), 183 | FadeTransition( 184 | opacity: _drawerContentsOpacity, 185 | child: Column( 186 | children: [ 187 | ListTile( 188 | leading: Icon(Icons.settings), 189 | title: Text( 190 | "设置", 191 | style: TextStyle(fontSize: 20.0), 192 | ), 193 | onTap: () => Navigator.of(context).push( 194 | MaterialPageRoute( 195 | builder: (BuildContext context) => ApplicationSetting(), 196 | ), 197 | ), 198 | ), 199 | ListTile( 200 | leading: Icon(Icons.info), 201 | title: Text( 202 | "关于", 203 | style: TextStyle(fontSize: 20.0), 204 | ), 205 | onTap: () => Navigator.of(context).push( 206 | MaterialPageRoute( 207 | builder: (BuildContext context) => About(), 208 | ), 209 | ), 210 | ), 211 | ListTile( 212 | leading: Icon(Icons.share), 213 | title: Text( 214 | "分享", 215 | style: TextStyle(fontSize: 20.0), 216 | ), 217 | onTap: () => showToast("该功能正在开发中,敬请期待!"), 218 | ), 219 | ListTile( 220 | leading: Icon(Icons.exit_to_app), 221 | title: Text( 222 | "注销", 223 | style: TextStyle(fontSize: 20.0), 224 | ), 225 | onTap: () => _logout(), 226 | ), 227 | ], 228 | ), 229 | ), 230 | ], 231 | ); 232 | } 233 | 234 | void _logout() { 235 | showDialog( 236 | context: context, 237 | builder: (BuildContext context) => SimpleDialog( 238 | title: Text("消息"), 239 | children: [ 240 | Row( 241 | mainAxisAlignment: MainAxisAlignment.center, 242 | children: [Text("你确定要退出当前账号?")], 243 | ), 244 | SizedBox( 245 | height: 10.0, 246 | ), 247 | Row( 248 | mainAxisAlignment: MainAxisAlignment.center, 249 | children: [ 250 | RaisedButton( 251 | child: Text("取消"), 252 | shape: StadiumBorder( 253 | side: BorderSide(color: Colors.red), 254 | ), 255 | onPressed: () => Navigator.pop(context), 256 | ), 257 | SizedBox( 258 | width: 10.0, 259 | height: 0.0, 260 | ), 261 | RaisedButton( 262 | child: Text("确定"), 263 | shape: StadiumBorder( 264 | side: BorderSide(color: Colors.red), 265 | ), 266 | onPressed: () { 267 | handler.dispose(); 268 | Navigator.of(context).push(MaterialPageRoute( 269 | builder: (BuildContext context) => Login())); 270 | }, 271 | ), 272 | ], 273 | ), 274 | ], 275 | ), 276 | ); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /lib/com/navigation/utils/application.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:sqflite/sqflite.dart'; 3 | 4 | /// 5 | /// 存放整个app的全局变量 6 | /// 7 | 8 | /// 整个应用数据存储路径 9 | String dir = ""; 10 | 11 | ///数据库文件路径 12 | String dbPath = ""; 13 | 14 | ///sqlite数据库操纵实例 15 | Database dataBases; 16 | 17 | ///人工智能时间查询 18 | String aiDate = "2018-07-29"; 19 | 20 | ///区块链查询时间 21 | String blockDate = "2018-07-29"; 22 | 23 | ///账号集合 24 | List> counts = []; 25 | 26 | /// 当前app设置信息 27 | Map settings = {}; 28 | 29 | /// 缓存app所需的图片资源 30 | Map images = {}; 31 | 32 | /// 33 | /// 根据传过来的用户名查找密码 34 | /// 35 | String findUser(String userId) { 36 | if (counts.length == 0) return null; 37 | String password; 38 | counts.forEach((count) { 39 | if (count.containsValue(userId)) { 40 | password = count["password"]; 41 | return; 42 | } 43 | if (password != "") return; 44 | }); 45 | return password ?? null; 46 | } 47 | -------------------------------------------------------------------------------- /lib/com/navigation/utils/constant.dart: -------------------------------------------------------------------------------- 1 | const double currentVersion = 0.2; 2 | const String http = "http://"; 3 | const String domain = "polyglot.net.cn"; 4 | const int tcpPort = 7373; 5 | const String type = "type"; 6 | const String subtype = "subtype"; 7 | const String message = "message"; 8 | const String search = "search"; 9 | const String friend = "friend"; 10 | const String messages = "messages"; 11 | const String friends = "friends"; 12 | const String user = "user"; 13 | const String request = "request"; 14 | const String response = "response"; 15 | const String delete = "delete"; 16 | const String login = "login"; 17 | const String register = "register"; 18 | const String version = "version"; 19 | const String from = "from"; 20 | const String to = "to"; 21 | const String info = "info"; 22 | const String nickname = "nickname"; 23 | const String id = "id"; 24 | const String password = "password"; 25 | const String dir = "dir"; 26 | const String host = "host"; 27 | const String body = "body"; 28 | const String accept = "accept"; 29 | const String text = "text"; 30 | const String tcp_port = "tcp-port"; 31 | const String http_port = "http-port"; 32 | const String keyword = "keyword"; 33 | const String offline = "offiline"; 34 | 35 | const String end = "\r\n"; 36 | const String messageOwn = "{[|@#\$%|]}"; 37 | const String carefree = "carefree"; 38 | const String phone = "phone"; 39 | const String mail = "mail"; 40 | const String address = "address"; 41 | const String website = "website"; 42 | const String company = "company"; 43 | const String brief = "brief"; 44 | const String status = "status"; 45 | const String data = "data"; 46 | const String createUserTable = "CREATE TABLE IF NOT EXISTS " 47 | "user(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," 48 | "userId VARCHAR NOT NULL," 49 | "userName VARCHAR," 50 | "password VARCHAR NOT NULL," 51 | "sign VARCHAR," 52 | "mail VARCHAR," 53 | "phone VARCHAR," 54 | "website VARCHAR)"; 55 | const String obtainUser = "SELECT*FROM user"; 56 | const String addUser = " INSERT INTO user(userId,password) VALUES(?,?)"; 57 | const String createCollectionTable = "CREATE TABLE IF NOT EXISTS collects(" 58 | "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," 59 | "title VARCHAR NOT NULL," 60 | "url VARCHAR NOT NULL," 61 | "en_brief VARCHAR NOT NULL," 62 | "cn_brief VARCHAR NOT NULL," 63 | "type VARCHAR NOT NULL," 64 | "owner VARCHAR NOT NULL)"; 65 | const String insertCollect = 66 | "INSERT INTO collects(title,url,en_brief,cn_brief,type,owner) VALUES(?,?,?,?,?,?)"; 67 | const String selectCollect = "" 68 | "SELECT title FROM collects WHERE title=? AND owner =?"; 69 | const String deleteCollect = 70 | "DELETE FROM collects WHERE title = ? AND owner= ?"; 71 | const String loadCollect = "SELECT*FROM collects WHERE owner =?"; 72 | -------------------------------------------------------------------------------- /lib/com/navigation/utils/file_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:flutter_app/com/navigation/models/new_trend_model.dart'; 6 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 9 | import 'package:flutter_app/com/navigation/netwok/socket_handler.dart' 10 | as handler; 11 | import 'package:flutter_app/com/navigation/utils/application.dart' 12 | as application; 13 | import 'package:sqflite/sqflite.dart'; 14 | import 'package:flutter_app/com/navigation/utils/constant.dart' as constants; 15 | import 'package:flutter/material.dart'; 16 | 17 | /// 18 | /// 19 | /// 20 | ///初始化文件状态 21 | /// 22 | Future initFileState() async { 23 | ///获取app文件路径 24 | application.dir = (await getApplicationDocumentsDirectory()).path; 25 | Directory directory = Directory("${application.dir}/db"); 26 | bool isExist = await directory.exists(); 27 | if (!isExist) { 28 | directory.createSync(); 29 | if (!(await File("${application.dir}/db/flutter_im.db").exists())) 30 | File("${application.dir}/db/flutter_im.db").createSync(); 31 | } 32 | application.dbPath = "${application.dir}/db/flutter_im.db"; 33 | } 34 | 35 | /// 36 | ///读取配置文件application.json 37 | /// 38 | Future readConfig() async { 39 | File configDir = File("${application.dir}/application.json"); 40 | if (!await configDir.exists()) return; 41 | var config = json.decode(configDir.readAsStringSync()); 42 | if (config["primaryColor"] != null) 43 | application.settings["primaryColor"] = config["primaryColor"]; 44 | if (config["voiceSwitch"] != null) 45 | application.settings["voiceSwitch"] = config["voiceSwitch"]; 46 | } 47 | 48 | /// 49 | /// 加载app所需图片素材 50 | /// 51 | Future loadImageFile() async { 52 | application.images["user"] = Image.asset( 53 | "assets/images/user.png", 54 | width: 30.0, 55 | height: 30.0, 56 | ); 57 | application.images["password"] = Image.asset( 58 | "assets/images/password.png", 59 | width: 30.0, 60 | height: 30.0, 61 | ); 62 | application.images["head"] = Image.asset( 63 | "assets/images/head.png", 64 | width: 30.0, 65 | height: 30.0, 66 | ); 67 | application.images["sender"] = Image.asset( 68 | "assets/images/sender.png", 69 | width: 100.0, 70 | height: 100.0, 71 | ); 72 | application.images["receiver"] = Image.asset( 73 | "assets/images/receiver.png", 74 | width: 100.0, 75 | height: 100.0, 76 | ); 77 | application.images["clear"] = Image.asset( 78 | "assets/images/clear.png", 79 | width: 40.0, 80 | height: 40.0, 81 | ); 82 | application.images["user_background"] = Image.asset( 83 | "assets/images/user_background.jpeg", 84 | fit: BoxFit.cover, 85 | ); 86 | application.images["favorite"] = Image.asset("assets/images/favorites.png"); 87 | } 88 | 89 | /// 90 | ///创建app所需Table 91 | ///@user 用户信息表(id userName password brief) 92 | /// 93 | /// 94 | Future initDataBases() async { 95 | ///实例化数据库操作对象 96 | application.dataBases = await openDatabase(application.dbPath, version: 1); 97 | 98 | ///创建用户表 99 | await application.dataBases.execute(constants.createUserTable); 100 | 101 | ///创建收藏表 102 | await application.dataBases.execute(constants.createCollectionTable); 103 | } 104 | 105 | /// 106 | ///查取用户列表 107 | /// 108 | Future> obtainUsers({String userId}) async { 109 | await application.dataBases.rawQuery(constants.obtainUser).then((value) { 110 | application.counts = value; 111 | }); 112 | Map user = {}; 113 | if (userId != null) { 114 | application.counts.forEach((count) { 115 | if (count["userId"] == userId) { 116 | user = count; 117 | return user; 118 | } 119 | }); 120 | } 121 | return user; 122 | } 123 | 124 | /// 125 | /// 添加用户到数据库 126 | /// 127 | void addUser(String userId, String password) async { 128 | bool isExist = false; 129 | application.counts.forEach((element) { 130 | element.forEach((key, value) { 131 | if (value == userId && key == "userId") { 132 | isExist = true; 133 | return; 134 | } 135 | }); 136 | }); 137 | if (!isExist) { 138 | await application.dataBases.rawQuery(constants.addUser, [userId, password]); 139 | obtainUsers(); 140 | } 141 | } 142 | 143 | /// 144 | /// 插入NewTrend到数据库收藏 145 | /// 146 | Future insertCollect(NewTrendModel model, String type) async { 147 | var result = await application.dataBases 148 | .rawQuery(constants.selectCollect, [model.title, handler.userId]); 149 | if (result.length == 0) { 150 | await application.dataBases.execute(constants.insertCollect, [ 151 | model.title, 152 | model.url, 153 | model.enbrief, 154 | model.cnbrief, 155 | type, 156 | handler.userId 157 | ]); 158 | showToast("收藏成功!"); 159 | } else 160 | showToast("数据已经存在了!"); 161 | } 162 | 163 | /// 164 | ///查询某一条记录是否存在 165 | /// 166 | Future selectCollects(String title) async { 167 | var result = await application.dataBases 168 | .rawQuery(constants.selectCollect, [title, handler.userId]); 169 | if (result.length > 0) return true; 170 | 171 | return false; 172 | } 173 | 174 | /// 175 | ///删除某一条收藏数据 176 | /// 177 | Future deleteCollects(String title) async { 178 | var result = await application.dataBases 179 | .rawDelete(constants.deleteCollect, [title, handler.userId]); 180 | if (result > 0) { 181 | showToast("移除成功!"); 182 | } else { 183 | showToast("移除失败!"); 184 | } 185 | return result; 186 | } 187 | 188 | Future> loadCollects({String type}) async { 189 | List list = []; 190 | var data = await application.dataBases 191 | .rawQuery(constants.loadCollect, [handler.userId]); 192 | data.forEach((element) { 193 | NewTrendModel model = NewTrendModel(element["title"], element["url"], 194 | element["cnbrief"], element["cnbrief"]); 195 | list.add(model); 196 | }); 197 | return list; 198 | } 199 | 200 | /// 201 | /// 更新/写入app设置选项 202 | /// 203 | void updateConfig() async { 204 | File config = File("${application.dir}/application.json"); 205 | if (!await config.exists()) config.createSync(); 206 | config.writeAsStringSync(json.encode(application.settings)); 207 | } 208 | -------------------------------------------------------------------------------- /lib/com/navigation/utils/system_announce.dart: -------------------------------------------------------------------------------- 1 | import 'package:audioplayers/audio_cache.dart'; 2 | 3 | /// 4 | /// 系统通知(包括一些提示音) 5 | /// 6 | /// 7 | 8 | void playFriendMention() { 9 | AudioCache player = new AudioCache(prefix: "audio/"); 10 | player.play('request.mp3'); 11 | } 12 | 13 | void playMessageMention() { 14 | AudioCache player = new AudioCache(prefix: "audio/"); 15 | player.play('message.wav'); 16 | } 17 | -------------------------------------------------------------------------------- /lib/com/navigation/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:convert/convert.dart'; 3 | import 'package:crypto/crypto.dart' as crypto; 4 | import 'package:fluttertoast/fluttertoast.dart'; 5 | 6 | String md5(String data) { 7 | var content = Utf8Encoder().convert(data); 8 | var md5 = crypto.md5; 9 | var digest = md5.convert(content); 10 | var password = hex.encode(digest.bytes); 11 | return password; 12 | } 13 | 14 | void showToast(String msg) { 15 | Fluttertoast.showToast( 16 | msg: msg, 17 | toastLength: Toast.LENGTH_SHORT, 18 | gravity: ToastGravity.BOTTOM, 19 | bgcolor: "#e74c3c", 20 | textcolor: '#ffffff'); 21 | } 22 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_app/splash_screne.dart'; 4 | import 'package:flutter_app/com/navigation/utils/application.dart' 5 | as application; 6 | 7 | void main() => runApp(Application()); 8 | 9 | class Application extends StatelessWidget { 10 | @override 11 | Widget build(BuildContext context) => MaterialApp( 12 | home: ApplicationHome(), 13 | title: "畅聊", 14 | ); 15 | } 16 | 17 | class ApplicationHome extends StatefulWidget { 18 | @override 19 | HomeState createState() => HomeState(); 20 | } 21 | 22 | class HomeState extends State { 23 | @override 24 | Widget build(BuildContext context) { 25 | return SplashScreen( 26 | seconds: 1, 27 | image: Image.asset("assets/images/icon.png"), 28 | photoSize: 70.0, 29 | title: Text( 30 | "畅聊", 31 | style: TextStyle(fontSize: 25.0), 32 | ), 33 | ); 34 | } 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | SystemChrome.setEnabledSystemUIOverlays([]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/splash_screne.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_app/com/navigation/page/login.dart'; 5 | import 'package:flutter_app/com/navigation/utils/file_handler.dart' 6 | as fileHandler; 7 | import 'package:flutter_app/com/navigation/utils/utils.dart'; 8 | import 'package:flutter/services.dart'; 9 | 10 | class SplashScreen extends StatefulWidget { 11 | final int seconds; 12 | final Text title; 13 | final Color backgroundColor; 14 | final TextStyle styleTextUnderTheLoader; 15 | final double photoSize; 16 | final dynamic onClick; 17 | final Color loaderColor; 18 | final Image image; 19 | SplashScreen( 20 | {this.loaderColor, 21 | @required this.seconds, 22 | this.photoSize, 23 | this.onClick, 24 | this.title = const Text('畅聊'), 25 | this.backgroundColor = Colors.white, 26 | this.styleTextUnderTheLoader = const TextStyle( 27 | fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black), 28 | this.image}); 29 | @override 30 | _SplashScreenState createState() => _SplashScreenState(); 31 | } 32 | 33 | class _SplashScreenState extends State { 34 | @override 35 | void initState() { 36 | super.initState(); 37 | _loadData(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Scaffold( 43 | backgroundColor: widget.backgroundColor, 44 | body: new InkWell( 45 | onTap: widget.onClick, 46 | child: new Stack( 47 | fit: StackFit.expand, 48 | children: [ 49 | new Container( 50 | decoration: BoxDecoration(color: widget.backgroundColor), 51 | ), 52 | new Column( 53 | mainAxisAlignment: MainAxisAlignment.start, 54 | children: [ 55 | new Expanded( 56 | flex: 2, 57 | child: new Container( 58 | child: new Column( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: [ 61 | new CircleAvatar( 62 | backgroundColor: Colors.transparent, 63 | child: new Container(child: widget.image), 64 | radius: widget.photoSize, 65 | ), 66 | new Padding( 67 | padding: const EdgeInsets.only(top: 10.0), 68 | ), 69 | widget.title 70 | ], 71 | )), 72 | ), 73 | Expanded( 74 | flex: 1, 75 | child: Column( 76 | mainAxisAlignment: MainAxisAlignment.center, 77 | ), 78 | ), 79 | ], 80 | ), 81 | ], 82 | ), 83 | ), 84 | ); 85 | } 86 | 87 | /// 88 | /// 程序初始化 89 | /// 90 | /// 91 | void _loadData() async { 92 | try { 93 | await fileHandler.initFileState(); 94 | await fileHandler.initDataBases(); 95 | await fileHandler.obtainUsers(); 96 | await fileHandler.readConfig(); 97 | await fileHandler.loadImageFile(); 98 | Navigator 99 | .of(context) 100 | .push(MaterialPageRoute(builder: (BuildContext context) => Login())); 101 | } catch (e) { 102 | showToast("程序出错了,正在退出....."); 103 | Timer(Duration(seconds: 2), () { 104 | SystemNavigator.pop(); 105 | }); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_app 2 | description: A new Flutter application. 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 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | audioplayers: ^0.7.5 16 | path_provider: ^0.4.1 17 | fluttertoast: ^2.0.7 18 | flutter_webview_plugin: 0.2.1+2 19 | camera: 0.2.3 20 | sqflite: any 21 | url_launcher: ^3.0.3 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 | 27 | dev_dependencies: 28 | flutter_test: 29 | sdk: flutter 30 | 31 | 32 | # For information on the generic Dart part of this file, see the 33 | # following page: https://www.dartlang.org/tools/pub/pubspec 34 | 35 | # The following section is specific to Flutter. 36 | flutter: 37 | 38 | # The following line ensures that the Material Icons font is 39 | # included with your application, so that you can use the icons in 40 | # the material Icons class. 41 | uses-material-design: true 42 | assets: 43 | 44 | # 图片素材 45 | 46 | - assets/images/splash.jpeg 47 | - assets/images/user.png 48 | - assets/images/password.png 49 | - assets/images/head.png 50 | - assets/images/message.png 51 | - assets/images/icon.png 52 | - assets/images/sender.png 53 | - assets/images/receiver.png 54 | - assets/images/person.png 55 | - assets/images/clear.png 56 | - assets/images/user_background.jpeg 57 | - assets/images/favorites.png 58 | - assets/images/hachker.jpg 59 | 60 | # 新趋向图片素材 61 | - assets/images/new_trend/ai.jpeg 62 | - assets/images/new_trend/block_chain.jpeg 63 | 64 | 65 | #音频素材 66 | 67 | - assets/audio/request.mp3 68 | - assets/audio/message.wav 69 | 70 | 71 | # To add assets to your application, add an assets section, like this: 72 | # assets: 73 | # - images/a_dot_burr.jpeg 74 | # - images/a_dot_ham.jpeg 75 | 76 | # An image asset can refer to one or more resolution-specific "variants", see 77 | # https://flutter.io/assets-and-images/#resolution-aware. 78 | 79 | # For details regarding adding assets from package dependencies, see 80 | # https://flutter.io/assets-and-images/#from-packages 81 | 82 | # To add custom fonts to your application, add a fonts section here, 83 | # in this "flutter" section. Each entry in this list should have a 84 | # "family" key with the font family name, and a "fonts" key with a 85 | # list giving the asset and other descriptors for the font. For 86 | # example: 87 | # fonts: 88 | # - family: Schyler 89 | # fonts: 90 | # - asset: fonts/Schyler-Regular.ttf 91 | # - asset: fonts/Schyler-Italic.ttf 92 | # style: italic 93 | # - family: Trajan Pro 94 | # fonts: 95 | # - asset: fonts/TrajanPro.ttf 96 | # - asset: fonts/TrajanPro_Bold.ttf 97 | # weight: 700 98 | # 99 | # For details regarding fonts from package dependencies, 100 | # see https://flutter.io/custom-fonts/#from-packages 101 | -------------------------------------------------------------------------------- /res/images/beauty/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/res/images/beauty/1.jpeg -------------------------------------------------------------------------------- /res/images/beauty/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/res/images/beauty/2.jpeg -------------------------------------------------------------------------------- /res/images/beauty/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZYangKui/ChangLiao-Mobile/a74ca543c63048d174b8996de46c9ff2952378c3/res/images/beauty/3.jpeg -------------------------------------------------------------------------------- /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:flutter_app/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new Application()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | --------------------------------------------------------------------------------