├── .analysis_options
├── .gitignore
├── .travis.yml
├── Flutter
├── AppFrameworkInfo.plist
├── Debug.xcconfig
└── Release.xcconfig
├── README.md
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ ├── com
│ │ │ └── flitter
│ │ │ │ └── MainActivity.java
│ │ └── io
│ │ │ └── flutter
│ │ │ └── plugins
│ │ │ └── GeneratedPluginRegistrant.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
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── assets
├── html
│ └── success.html
└── images
│ └── banner.jpg
├── flitter.iml
├── generate_main_from_env.dart
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ └── contents.xcworkspacedata
└── 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
│ ├── GeneratedPluginRegistrant.h
│ ├── GeneratedPluginRegistrant.m
│ ├── Info.plist
│ └── main.m
├── lib
├── app.dart
├── intl
│ └── messages_all.dart
├── redux
│ ├── actions.dart
│ ├── flitter_app_reducer.dart
│ ├── flitter_app_state.dart
│ └── store.dart
├── services
│ ├── flitter_auth.dart
│ ├── flitter_config.dart
│ ├── flitter_request.dart
│ └── flutter_gitter_auth.dart
└── widgets
│ ├── common
│ ├── chat_room.dart
│ ├── drawer.dart
│ ├── list_room.dart
│ ├── search.dart
│ └── utils.dart
│ └── routes
│ ├── group.dart
│ ├── home.dart
│ ├── login.dart
│ ├── people.dart
│ ├── room.dart
│ └── settings.dart
├── pubspec.yaml
├── screenshots
├── flutter_01.png
├── flutter_02.png
├── flutter_03.png
├── flutter_04.png
├── flutter_05.png
├── flutter_06.png
└── flutter_07.png
└── test
├── utils.dart
└── widgets
├── drawer_test.dart
├── group_test.dart
├── home_test.dart
├── people_test.dart
└── splash_test.dart
/.analysis_options:
--------------------------------------------------------------------------------
1 | analyzer:
2 | strong-mode: true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .atom/
3 | .idea
4 | .packages
5 | .pub/
6 | build/
7 | ios/.generated/
8 | packages
9 | pubspec.lock
10 | .flutter-plugins
11 | .dart_tool
12 | config.yaml
13 | lib/main.dart
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | matrix:
2 | # This causes the build to complete immediately upon first failure or once
3 | # required jobs are green.
4 | fast_finish: true
5 |
6 | # Building APK/IPA takes a long time; do not wait for them to finish.
7 | allow_failures:
8 | - env: JOB=APK
9 | - env: JOB=IPA
10 |
11 | include:
12 | # Runs unit tests without emulators.
13 | - env: JOB=PR
14 | os: linux
15 | language: generic
16 | sudo: false
17 | addons:
18 | apt:
19 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
20 | sources:
21 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
22 | packages:
23 | - libstdc++6
24 | - fonts-droid
25 | before_script:
26 | - git clone https://github.com/flutter/flutter.git -b beta --depth 1
27 | script:
28 | - ./flutter/bin/flutter test
29 |
30 | # Builds an APK.
31 | - env: JOB=APK
32 | os: linux
33 | language: android
34 | licenses:
35 | - 'android-sdk-preview-license-.+'
36 | - 'android-sdk-license-.+'
37 | - 'google-gdk-license-.+'
38 | android:
39 | components:
40 | - tools
41 | - platform-tools
42 | - build-tools-27.0.3
43 | - android-27
44 | - sys-img-armeabi-v7a-google_apis-25
45 | - extra-android-m2repository
46 | - extra-google-m2repository
47 | - extra-google-android-support
48 | jdk: oraclejdk8
49 | sudo: false
50 | addons:
51 | apt:
52 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
53 | sources:
54 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
55 | packages:
56 | - libstdc++6
57 | - fonts-droid
58 | before_script:
59 | - wget http://services.gradle.org/distributions/gradle-3.5-bin.zip
60 | - unzip -qq gradle-3.5-bin.zip
61 | - export GRADLE_HOME=$PWD/gradle-3.5
62 | - export PATH=$GRADLE_HOME/bin:$PATH
63 | - git clone https://github.com/flutter/flutter.git -b beta --depth 1
64 | script:
65 | - ./flutter/bin/internal/update_dart_sdk.sh
66 | - ./flutter/bin/cache/dart-sdk/bin/dart generate_main_from_env.dart
67 | - ./flutter/bin/flutter -v build apk
68 |
69 | # Builds an IPA.
70 | - env: JOB=IPA
71 | os: osx
72 | language: generic
73 | osx_image: xcode8.3
74 | before_script:
75 | - pip install six
76 | - brew update
77 | - brew install --HEAD libimobiledevice
78 | - brew install ideviceinstaller
79 | - brew install ios-deploy
80 | - git clone https://github.com/flutter/flutter.git -b beta --depth 1
81 | script:
82 | - ./flutter/bin/internal/update_dart_sdk.sh
83 | - ./flutter/bin/cache/dart-sdk/bin/dart generate_main_from_env.dart
84 | - ./flutter/bin/flutter -v build ios --no-codesign
85 |
86 | cache:
87 | directories:
88 | - $HOME/.pub-cache
--------------------------------------------------------------------------------
/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 | UIRequiredDeviceCapabilities
24 |
25 | arm64
26 |
27 | MinimumOSVersion
28 | 8.0
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flitter
2 |
3 | Gitter Client for Mobile made with Flutter
4 |
5 | [](https://travis-ci.org/dart-flitter/flitter)
6 |
7 |
20 |
33 |
34 | ## Getting Started
35 |
36 | For help getting started with Flutter, view our online
37 | [documentation](http://flutter.io/).
38 |
39 |
40 | ## Configuration
41 |
42 | Create a `main.dart` inside `lib` folder with the following content.
43 |
44 | ```dart
45 | import 'package:flitter/app.dart' as flitter;
46 | import 'package:flitter/services/flitter_config.dart';
47 |
48 | main() {
49 | Config.init(gitter: const GitterConfig(
50 | appId: "",
51 | appSecret: "",
52 | redirectionUrl: ""));
53 |
54 | flitter.run();
55 | }
56 | ```
57 |
58 | As `GITTER_REDIRECTION_URL` value use "http://localhost:8080".
59 |
60 | [More Infos](https://developer.gitter.im/docs/welcome)
61 |
--------------------------------------------------------------------------------
/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/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 | apply plugin: 'com.android.application'
15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
16 |
17 | android {
18 | compileSdkVersion 27
19 |
20 | lintOptions {
21 | disable 'InvalidPackage'
22 | }
23 |
24 | defaultConfig {
25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
26 | applicationId "com.flitter"
27 | minSdkVersion 16
28 | targetSdkVersion 27
29 | versionCode 1
30 | versionName "1.0"
31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
32 | }
33 |
34 | buildTypes {
35 | release {
36 | // TODO: Add your own signing config for the release build.
37 | // Signing with the debug keys for now, so `flutter run --release` works.
38 | signingConfig signingConfigs.debug
39 | }
40 | }
41 | }
42 |
43 | flutter {
44 | source '../..'
45 | }
46 |
47 | dependencies {
48 | testImplementation 'junit:junit:4.12'
49 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
51 | }
52 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
22 |
26 |
33 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/flitter/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.flitter;
2 |
3 | import android.os.Bundle;
4 |
5 | import io.flutter.app.FlutterActivity;
6 | import io.flutter.plugins.GeneratedPluginRegistrant;
7 |
8 | public class MainActivity extends FlutterActivity {
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | GeneratedPluginRegistrant.registerWith(this);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java:
--------------------------------------------------------------------------------
1 | package io.flutter.plugins;
2 |
3 | import io.flutter.plugin.common.PluginRegistry;
4 | import com.flutter_webview_plugin.FlutterWebviewPlugin;
5 | import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin;
6 | import io.flutter.plugins.urllauncher.UrlLauncherPlugin;
7 |
8 | /**
9 | * Generated file. Do not edit.
10 | */
11 | public final class GeneratedPluginRegistrant {
12 | public static void registerWith(PluginRegistry registry) {
13 | if (alreadyRegisteredWith(registry)) {
14 | return;
15 | }
16 | FlutterWebviewPlugin.registerWith(registry.registrarFor("com.flutter_webview_plugin.FlutterWebviewPlugin"));
17 | SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin"));
18 | UrlLauncherPlugin.registerWith(registry.registrarFor("io.flutter.plugins.urllauncher.UrlLauncherPlugin"));
19 | }
20 |
21 | private static boolean alreadyRegisteredWith(PluginRegistry registry) {
22 | final String key = GeneratedPluginRegistrant.class.getCanonicalName();
23 | if (registry.hasPlugin(key)) {
24 | return true;
25 | }
26 | registry.registrarFor(key);
27 | return false;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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.0.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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.1-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/html/success.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Success
6 |
7 |
8 |
9 |
12 |
--------------------------------------------------------------------------------
/assets/images/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/assets/images/banner.jpg
--------------------------------------------------------------------------------
/flitter.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/generate_main_from_env.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | main() {
4 | File file = new File("lib/main.dart");
5 | if (!file.existsSync()) {
6 | file.createSync(recursive: true);
7 | file.writeAsStringSync(
8 | _generate(Platform.environment["GITTER_APP_ID"],
9 | Platform.environment["GITTER_APP_SECRET"]));
10 | }
11 | }
12 |
13 |
14 | String _generate(String appId, String appSecret) =>
15 | '''
16 | import 'package:flitter/app.dart' as flitter;
17 | import 'package:flitter/services/flitter_config.dart';
18 |
19 | main() {
20 | Config.init(
21 | gitter: const GitterConfig(
22 | appId: "$appId",
23 | appSecret: "$appSecret",
24 | redirectionUrl: "http://localhost:8080/"));
25 |
26 | flitter.run();
27 | }
28 |
29 | ''';
--------------------------------------------------------------------------------
/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 | *.pbxuser
16 | *.mode1v3
17 | *.mode2v3
18 | *.perspectivev3
19 |
20 | !default.pbxuser
21 | !default.mode1v3
22 | !default.mode2v3
23 | !default.perspectivev3
24 |
25 | xcuserdata
26 |
27 | *.moved-aside
28 |
29 | *.pyc
30 | *sync/
31 | Icon?
32 | .tags*
33 |
34 | /Flutter/app.flx
35 | /Flutter/app.zip
36 | /Flutter/flutter_assets/
37 | /Flutter/App.framework
38 | /Flutter/Flutter.framework
39 | /Flutter/Generated.xcconfig
40 | /ServiceDefinitions.json
41 |
42 | Pods/
43 |
--------------------------------------------------------------------------------
/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 | UIRequiredDeviceCapabilities
24 |
25 | arm64
26 |
27 | MinimumOSVersion
28 | 8.0
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | if ENV['FLUTTER_FRAMEWORK_DIR'] == nil
5 | abort('Please set FLUTTER_FRAMEWORK_DIR to the directory containing Flutter.framework')
6 | end
7 |
8 | target 'Runner' do
9 | use_frameworks!
10 |
11 | # Pods for Runner
12 |
13 | # Flutter Pods
14 | pod 'Flutter', :path => ENV['FLUTTER_FRAMEWORK_DIR']
15 |
16 | if File.exists? '../.flutter-plugins'
17 | flutter_root = File.expand_path('..')
18 | File.foreach('../.flutter-plugins') { |line|
19 | plugin = line.split(pattern='=')
20 | if plugin.length == 2
21 | name = plugin[0].strip()
22 | path = plugin[1].strip()
23 | resolved_path = File.expand_path("#{path}/ios", flutter_root)
24 | pod name, :path => resolved_path
25 | else
26 | puts "Invalid plugin specification: #{line}"
27 | end
28 | }
29 | end
30 | end
31 |
32 | post_install do |installer|
33 | installer.pods_project.targets.each do |target|
34 | target.build_configurations.each do |config|
35 | config.build_settings['ENABLE_BITCODE'] = 'NO'
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_webview_plugin (0.0.1):
4 | - Flutter
5 | - shared_preferences (0.0.1):
6 | - Flutter
7 | - url_launcher (0.0.1):
8 | - Flutter
9 |
10 | DEPENDENCIES:
11 | - Flutter (from `/Users/izambasiron/Workspace/flutter/bin/cache/artifacts/engine/ios`)
12 | - flutter_webview_plugin (from `/Users/izambasiron/.pub-cache/hosted/pub.dartlang.org/flutter_webview_plugin-0.1.2/ios`)
13 | - shared_preferences (from `/Users/izambasiron/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.3.3/ios`)
14 | - url_launcher (from `/Users/izambasiron/.pub-cache/hosted/pub.dartlang.org/url_launcher-2.0.2/ios`)
15 |
16 | EXTERNAL SOURCES:
17 | Flutter:
18 | :path: /Users/izambasiron/Workspace/flutter/bin/cache/artifacts/engine/ios
19 | flutter_webview_plugin:
20 | :path: /Users/izambasiron/.pub-cache/hosted/pub.dartlang.org/flutter_webview_plugin-0.1.2/ios
21 | shared_preferences:
22 | :path: /Users/izambasiron/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.3.3/ios
23 | url_launcher:
24 | :path: /Users/izambasiron/.pub-cache/hosted/pub.dartlang.org/url_launcher-2.0.2/ios
25 |
26 | SPEC CHECKSUMS:
27 | Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296
28 | flutter_webview_plugin: 116575b48572029304775b768e9f15ebfc316274
29 | shared_preferences: 5a1d487c427ee18fcd3ea1f2a131569481834b53
30 | url_launcher: 92b89c1029a0373879933c21642958c874539095
31 |
32 | PODFILE CHECKSUM: da0a90142b97ff5764bbe022af2a7676460e1454
33 |
34 | COCOAPODS: 1.4.0
35 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
12 | 3826EC1C73D033A8D054855E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69DC8EB3C3C4F8DEB111E68 /* Pods_Runner.framework */; };
13 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
14 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
15 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
17 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
19 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
20 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
21 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
22 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
23 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
24 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXCopyFilesBuildPhase section */
28 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
29 | isa = PBXCopyFilesBuildPhase;
30 | buildActionMask = 2147483647;
31 | dstPath = "";
32 | dstSubfolderSpec = 10;
33 | files = (
34 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
35 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
36 | );
37 | name = "Embed Frameworks";
38 | runOnlyForDeploymentPostprocessing = 0;
39 | };
40 | /* End PBXCopyFilesBuildPhase section */
41 |
42 | /* Begin PBXFileReference section */
43 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
44 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
45 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
46 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
47 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
48 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
49 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
50 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
53 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
54 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
55 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
56 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
57 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
58 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
59 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
60 | F69DC8EB3C3C4F8DEB111E68 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
61 | /* End PBXFileReference section */
62 |
63 | /* Begin PBXFrameworksBuildPhase section */
64 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
65 | isa = PBXFrameworksBuildPhase;
66 | buildActionMask = 2147483647;
67 | files = (
68 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
69 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
70 | 3826EC1C73D033A8D054855E /* Pods_Runner.framework in Frameworks */,
71 | );
72 | runOnlyForDeploymentPostprocessing = 0;
73 | };
74 | /* End PBXFrameworksBuildPhase section */
75 |
76 | /* Begin PBXGroup section */
77 | 5D36FEDFF9D30CC5AE9379AF /* Frameworks */ = {
78 | isa = PBXGroup;
79 | children = (
80 | F69DC8EB3C3C4F8DEB111E68 /* Pods_Runner.framework */,
81 | );
82 | name = Frameworks;
83 | sourceTree = "";
84 | };
85 | 9740EEB11CF90186004384FC /* Flutter */ = {
86 | isa = PBXGroup;
87 | children = (
88 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
89 | 3B80C3931E831B6300D905FE /* App.framework */,
90 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
91 | 9740EEBA1CF902C7004384FC /* Flutter.framework */,
92 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
93 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
94 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
95 | );
96 | name = Flutter;
97 | sourceTree = "";
98 | };
99 | 97C146E51CF9000F007C117D = {
100 | isa = PBXGroup;
101 | children = (
102 | 9740EEB11CF90186004384FC /* Flutter */,
103 | 97C146F01CF9000F007C117D /* Runner */,
104 | 97C146EF1CF9000F007C117D /* Products */,
105 | CECB9306FD4080E41371CE13 /* Pods */,
106 | 5D36FEDFF9D30CC5AE9379AF /* Frameworks */,
107 | );
108 | sourceTree = "";
109 | };
110 | 97C146EF1CF9000F007C117D /* Products */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 97C146EE1CF9000F007C117D /* Runner.app */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 97C146F01CF9000F007C117D /* Runner */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
122 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
123 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
124 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
125 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
126 | 97C147021CF9000F007C117D /* Info.plist */,
127 | 97C146F11CF9000F007C117D /* Supporting Files */,
128 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
129 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
130 | );
131 | path = Runner;
132 | sourceTree = "";
133 | };
134 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 97C146F21CF9000F007C117D /* main.m */,
138 | );
139 | name = "Supporting Files";
140 | sourceTree = "";
141 | };
142 | CECB9306FD4080E41371CE13 /* Pods */ = {
143 | isa = PBXGroup;
144 | children = (
145 | );
146 | name = Pods;
147 | sourceTree = "";
148 | };
149 | /* End PBXGroup section */
150 |
151 | /* Begin PBXNativeTarget section */
152 | 97C146ED1CF9000F007C117D /* Runner */ = {
153 | isa = PBXNativeTarget;
154 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
155 | buildPhases = (
156 | A0274BCFFF2EFA15188D1EEB /* [CP] Check Pods Manifest.lock */,
157 | 9740EEB61CF901F6004384FC /* Run Script */,
158 | 97C146EA1CF9000F007C117D /* Sources */,
159 | 97C146EB1CF9000F007C117D /* Frameworks */,
160 | 97C146EC1CF9000F007C117D /* Resources */,
161 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
162 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
163 | B6BB1EDD7B45F95EFCB73BA8 /* [CP] Embed Pods Frameworks */,
164 | D65495B808B50135AED36BDB /* [CP] Copy Pods Resources */,
165 | );
166 | buildRules = (
167 | );
168 | dependencies = (
169 | );
170 | name = Runner;
171 | productName = Runner;
172 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
173 | productType = "com.apple.product-type.application";
174 | };
175 | /* End PBXNativeTarget section */
176 |
177 | /* Begin PBXProject section */
178 | 97C146E61CF9000F007C117D /* Project object */ = {
179 | isa = PBXProject;
180 | attributes = {
181 | LastUpgradeCheck = 0910;
182 | ORGANIZATIONNAME = "The Chromium Authors";
183 | TargetAttributes = {
184 | 97C146ED1CF9000F007C117D = {
185 | CreatedOnToolsVersion = 7.3.1;
186 | };
187 | };
188 | };
189 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
190 | compatibilityVersion = "Xcode 3.2";
191 | developmentRegion = English;
192 | hasScannedForEncodings = 0;
193 | knownRegions = (
194 | en,
195 | Base,
196 | );
197 | mainGroup = 97C146E51CF9000F007C117D;
198 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
199 | projectDirPath = "";
200 | projectRoot = "";
201 | targets = (
202 | 97C146ED1CF9000F007C117D /* Runner */,
203 | );
204 | };
205 | /* End PBXProject section */
206 |
207 | /* Begin PBXResourcesBuildPhase section */
208 | 97C146EC1CF9000F007C117D /* Resources */ = {
209 | isa = PBXResourcesBuildPhase;
210 | buildActionMask = 2147483647;
211 | files = (
212 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
213 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
214 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
215 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
216 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
217 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXShellScriptBuildPhase section */
225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | inputPaths = (
231 | );
232 | name = "Thin Binary";
233 | outputPaths = (
234 | );
235 | runOnlyForDeploymentPostprocessing = 0;
236 | shellPath = /bin/sh;
237 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
238 | };
239 | 9740EEB61CF901F6004384FC /* Run Script */ = {
240 | isa = PBXShellScriptBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | );
244 | inputPaths = (
245 | );
246 | name = "Run Script";
247 | outputPaths = (
248 | );
249 | runOnlyForDeploymentPostprocessing = 0;
250 | shellPath = /bin/sh;
251 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
252 | };
253 | A0274BCFFF2EFA15188D1EEB /* [CP] Check Pods Manifest.lock */ = {
254 | isa = PBXShellScriptBuildPhase;
255 | buildActionMask = 2147483647;
256 | files = (
257 | );
258 | inputPaths = (
259 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
260 | "${PODS_ROOT}/Manifest.lock",
261 | );
262 | name = "[CP] Check Pods Manifest.lock";
263 | outputPaths = (
264 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
265 | );
266 | runOnlyForDeploymentPostprocessing = 0;
267 | shellPath = /bin/sh;
268 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
269 | showEnvVarsInLog = 0;
270 | };
271 | B6BB1EDD7B45F95EFCB73BA8 /* [CP] Embed Pods Frameworks */ = {
272 | isa = PBXShellScriptBuildPhase;
273 | buildActionMask = 2147483647;
274 | files = (
275 | );
276 | inputPaths = (
277 | "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
278 | "${PODS_ROOT}/../../../flutter/bin/cache/artifacts/engine/ios/Flutter.framework",
279 | "${BUILT_PRODUCTS_DIR}/flutter_webview_plugin/flutter_webview_plugin.framework",
280 | "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework",
281 | "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
282 | );
283 | name = "[CP] Embed Pods Frameworks";
284 | outputPaths = (
285 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
286 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_webview_plugin.framework",
287 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework",
288 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
289 | );
290 | runOnlyForDeploymentPostprocessing = 0;
291 | shellPath = /bin/sh;
292 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
293 | showEnvVarsInLog = 0;
294 | };
295 | D65495B808B50135AED36BDB /* [CP] Copy Pods Resources */ = {
296 | isa = PBXShellScriptBuildPhase;
297 | buildActionMask = 2147483647;
298 | files = (
299 | );
300 | inputPaths = (
301 | );
302 | name = "[CP] Copy Pods Resources";
303 | outputPaths = (
304 | );
305 | runOnlyForDeploymentPostprocessing = 0;
306 | shellPath = /bin/sh;
307 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
308 | showEnvVarsInLog = 0;
309 | };
310 | /* End PBXShellScriptBuildPhase section */
311 |
312 | /* Begin PBXSourcesBuildPhase section */
313 | 97C146EA1CF9000F007C117D /* Sources */ = {
314 | isa = PBXSourcesBuildPhase;
315 | buildActionMask = 2147483647;
316 | files = (
317 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
318 | 97C146F31CF9000F007C117D /* main.m in Sources */,
319 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
320 | );
321 | runOnlyForDeploymentPostprocessing = 0;
322 | };
323 | /* End PBXSourcesBuildPhase section */
324 |
325 | /* Begin PBXVariantGroup section */
326 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
327 | isa = PBXVariantGroup;
328 | children = (
329 | 97C146FB1CF9000F007C117D /* Base */,
330 | );
331 | name = Main.storyboard;
332 | sourceTree = "";
333 | };
334 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
335 | isa = PBXVariantGroup;
336 | children = (
337 | 97C147001CF9000F007C117D /* Base */,
338 | );
339 | name = LaunchScreen.storyboard;
340 | sourceTree = "";
341 | };
342 | /* End PBXVariantGroup section */
343 |
344 | /* Begin XCBuildConfiguration section */
345 | 97C147031CF9000F007C117D /* Debug */ = {
346 | isa = XCBuildConfiguration;
347 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
348 | buildSettings = {
349 | ALWAYS_SEARCH_USER_PATHS = NO;
350 | CLANG_ANALYZER_NONNULL = YES;
351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
352 | CLANG_CXX_LIBRARY = "libc++";
353 | CLANG_ENABLE_MODULES = YES;
354 | CLANG_ENABLE_OBJC_ARC = YES;
355 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
356 | CLANG_WARN_BOOL_CONVERSION = YES;
357 | CLANG_WARN_COMMA = YES;
358 | CLANG_WARN_CONSTANT_CONVERSION = YES;
359 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
360 | CLANG_WARN_EMPTY_BODY = YES;
361 | CLANG_WARN_ENUM_CONVERSION = YES;
362 | CLANG_WARN_INFINITE_RECURSION = YES;
363 | CLANG_WARN_INT_CONVERSION = YES;
364 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
365 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
367 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
368 | CLANG_WARN_STRICT_PROTOTYPES = YES;
369 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
370 | CLANG_WARN_UNREACHABLE_CODE = YES;
371 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
372 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
373 | COPY_PHASE_STRIP = NO;
374 | DEBUG_INFORMATION_FORMAT = dwarf;
375 | ENABLE_STRICT_OBJC_MSGSEND = YES;
376 | ENABLE_TESTABILITY = YES;
377 | GCC_C_LANGUAGE_STANDARD = gnu99;
378 | GCC_DYNAMIC_NO_PIC = NO;
379 | GCC_NO_COMMON_BLOCKS = YES;
380 | GCC_OPTIMIZATION_LEVEL = 0;
381 | GCC_PREPROCESSOR_DEFINITIONS = (
382 | "DEBUG=1",
383 | "$(inherited)",
384 | );
385 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
386 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
387 | GCC_WARN_UNDECLARED_SELECTOR = YES;
388 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
389 | GCC_WARN_UNUSED_FUNCTION = YES;
390 | GCC_WARN_UNUSED_VARIABLE = YES;
391 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
392 | MTL_ENABLE_DEBUG_INFO = YES;
393 | ONLY_ACTIVE_ARCH = YES;
394 | SDKROOT = iphoneos;
395 | TARGETED_DEVICE_FAMILY = "1,2";
396 | };
397 | name = Debug;
398 | };
399 | 97C147041CF9000F007C117D /* Release */ = {
400 | isa = XCBuildConfiguration;
401 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
402 | buildSettings = {
403 | ALWAYS_SEARCH_USER_PATHS = NO;
404 | CLANG_ANALYZER_NONNULL = YES;
405 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
406 | CLANG_CXX_LIBRARY = "libc++";
407 | CLANG_ENABLE_MODULES = YES;
408 | CLANG_ENABLE_OBJC_ARC = YES;
409 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
410 | CLANG_WARN_BOOL_CONVERSION = YES;
411 | CLANG_WARN_COMMA = YES;
412 | CLANG_WARN_CONSTANT_CONVERSION = YES;
413 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
414 | CLANG_WARN_EMPTY_BODY = YES;
415 | CLANG_WARN_ENUM_CONVERSION = YES;
416 | CLANG_WARN_INFINITE_RECURSION = YES;
417 | CLANG_WARN_INT_CONVERSION = YES;
418 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
419 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
420 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
421 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
422 | CLANG_WARN_STRICT_PROTOTYPES = YES;
423 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
424 | CLANG_WARN_UNREACHABLE_CODE = YES;
425 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
426 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
427 | COPY_PHASE_STRIP = NO;
428 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
429 | ENABLE_NS_ASSERTIONS = NO;
430 | ENABLE_STRICT_OBJC_MSGSEND = YES;
431 | GCC_C_LANGUAGE_STANDARD = gnu99;
432 | GCC_NO_COMMON_BLOCKS = YES;
433 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
434 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
435 | GCC_WARN_UNDECLARED_SELECTOR = YES;
436 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
437 | GCC_WARN_UNUSED_FUNCTION = YES;
438 | GCC_WARN_UNUSED_VARIABLE = YES;
439 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
440 | MTL_ENABLE_DEBUG_INFO = NO;
441 | SDKROOT = iphoneos;
442 | TARGETED_DEVICE_FAMILY = "1,2";
443 | VALIDATE_PRODUCT = YES;
444 | };
445 | name = Release;
446 | };
447 | 97C147061CF9000F007C117D /* Debug */ = {
448 | isa = XCBuildConfiguration;
449 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
450 | buildSettings = {
451 | ARCHS = arm64;
452 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
453 | ENABLE_BITCODE = NO;
454 | FRAMEWORK_SEARCH_PATHS = (
455 | "$(inherited)",
456 | "$(PROJECT_DIR)/Flutter",
457 | );
458 | INFOPLIST_FILE = Runner/Info.plist;
459 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
460 | LIBRARY_SEARCH_PATHS = (
461 | "$(inherited)",
462 | "$(PROJECT_DIR)/Flutter",
463 | );
464 | PRODUCT_BUNDLE_IDENTIFIER = com.flitter;
465 | PRODUCT_NAME = "$(TARGET_NAME)";
466 | };
467 | name = Debug;
468 | };
469 | 97C147071CF9000F007C117D /* Release */ = {
470 | isa = XCBuildConfiguration;
471 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
472 | buildSettings = {
473 | ARCHS = arm64;
474 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
475 | ENABLE_BITCODE = NO;
476 | FRAMEWORK_SEARCH_PATHS = (
477 | "$(inherited)",
478 | "$(PROJECT_DIR)/Flutter",
479 | );
480 | INFOPLIST_FILE = Runner/Info.plist;
481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
482 | LIBRARY_SEARCH_PATHS = (
483 | "$(inherited)",
484 | "$(PROJECT_DIR)/Flutter",
485 | );
486 | PRODUCT_BUNDLE_IDENTIFIER = com.flitter;
487 | PRODUCT_NAME = "$(TARGET_NAME)";
488 | };
489 | name = Release;
490 | };
491 | /* End XCBuildConfiguration section */
492 |
493 | /* Begin XCConfigurationList section */
494 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
495 | isa = XCConfigurationList;
496 | buildConfigurations = (
497 | 97C147031CF9000F007C117D /* Debug */,
498 | 97C147041CF9000F007C117D /* Release */,
499 | );
500 | defaultConfigurationIsVisible = 0;
501 | defaultConfigurationName = Release;
502 | };
503 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
504 | isa = XCConfigurationList;
505 | buildConfigurations = (
506 | 97C147061CF9000F007C117D /* Debug */,
507 | 97C147071CF9000F007C117D /* Release */,
508 | );
509 | defaultConfigurationIsVisible = 0;
510 | defaultConfigurationName = Release;
511 | };
512 | /* End XCConfigurationList section */
513 | };
514 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
515 | }
516 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner/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 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
7 | [GeneratedPluginRegistrant registerWithRegistry:self];
8 | // Override point for customization after application launch.
9 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/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/GeneratedPluginRegistrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | #ifndef GeneratedPluginRegistrant_h
6 | #define GeneratedPluginRegistrant_h
7 |
8 | #import
9 |
10 | @interface GeneratedPluginRegistrant : NSObject
11 | + (void)registerWithRegistry:(NSObject*)registry;
12 | @end
13 |
14 | #endif /* GeneratedPluginRegistrant_h */
15 |
--------------------------------------------------------------------------------
/ios/Runner/GeneratedPluginRegistrant.m:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | #import "GeneratedPluginRegistrant.h"
6 | #import
7 | #import
8 | #import
9 |
10 | @implementation GeneratedPluginRegistrant
11 |
12 | + (void)registerWithRegistry:(NSObject*)registry {
13 | [FlutterWebviewPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterWebviewPlugin"]];
14 | [FLTSharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharedPreferencesPlugin"]];
15 | [FLTUrlLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTUrlLauncherPlugin"]];
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/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 | flitter
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | arm64
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UIViewControllerBasedStatusBarAppearance
47 |
48 | NSAppTransportSecurity
49 |
50 | NSAllowsArbitraryLoads
51 |
52 | NSExceptionDomains
53 |
54 | localhost
55 |
56 | NSExceptionAllowsInsecureHTTPLoads
57 |
58 | NSIncludesSubdomains
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/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/app.dart:
--------------------------------------------------------------------------------
1 | library flitter.app;
2 |
3 | import 'dart:async';
4 | import 'package:flitter/redux/actions.dart';
5 | import 'package:flitter/redux/flitter_app_state.dart';
6 | import 'package:flitter/redux/store.dart';
7 | import 'package:flitter/services/flitter_auth.dart';
8 | import 'package:flitter/services/flitter_request.dart';
9 | import 'package:flitter/widgets/routes/group.dart';
10 | import 'package:flitter/widgets/routes/home.dart';
11 | import 'package:flitter/widgets/routes/login.dart';
12 | import 'package:flitter/widgets/routes/people.dart';
13 | import 'package:flitter/widgets/routes/settings.dart';
14 | import 'package:flutter/material.dart';
15 | import 'package:shared_preferences/shared_preferences.dart';
16 |
17 | const appName = "Flitter";
18 |
19 | class Splash extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return new MaterialApp(
23 | home: new Scaffold(
24 | body: new Column(children: [
25 | new Center(
26 | child: new FlutterLogo(
27 | colors: themeStore?.state?.theme?.accentColor ?? Colors.pink,
28 | size: 80.0)),
29 | new Center(
30 | child: new Text(appName, style: new TextStyle(fontSize: 32.0))),
31 | new Center(
32 | child:
33 | new Text("for Gitter", style: new TextStyle(fontSize: 16.0)))
34 | ], mainAxisAlignment: MainAxisAlignment.center)),
35 | theme: themeStore?.state?.theme);
36 | }
37 | }
38 |
39 | class LoadingView extends StatelessWidget {
40 | @override
41 | Widget build(BuildContext context) =>
42 | new Center(child: new CircularProgressIndicator());
43 | }
44 |
45 | class App extends StatefulWidget {
46 | @override
47 | _AppState createState() => new _AppState();
48 | }
49 |
50 | class _AppState extends State {
51 | var _subscription;
52 | var _themeSubscription;
53 |
54 | _AppState() {
55 | _subscription = gitterStore.onChange.listen((_) async {
56 | setState(() {});
57 | });
58 | _themeSubscription = themeStore.onChange.listen((_) {
59 | setState(() {});
60 | });
61 | }
62 |
63 | @override
64 | void dispose() {
65 | super.dispose();
66 | _subscription.cancel();
67 | _themeSubscription.cancel();
68 | }
69 |
70 | @override
71 | Widget build(BuildContext context) {
72 | if (gitterApi == null || gitterToken == null) {
73 | return new LoginView();
74 | }
75 |
76 | return new MaterialApp(
77 | theme: themeStore.state.theme,
78 | title: appName,
79 | routes: {
80 | HomeView.path: (BuildContext context) => new HomeView(),
81 | PeopleView.path: (BuildContext context) => new PeopleView(),
82 | GroupView.path: (BuildContext context) => new GroupView(),
83 | SettingsView.path: (BuildContext context) => new SettingsView()
84 | });
85 | }
86 | }
87 |
88 | Future run() async {
89 | runApp(new Splash());
90 |
91 | await _init();
92 |
93 | runApp(new App());
94 | }
95 |
96 | Future _init() async {
97 | gitterStore = new GitterStore();
98 | themeStore = new ThemeStore();
99 | final token = await FlitterAuth.getToken();
100 | if (token != null) {
101 | await initStores(token);
102 | }
103 |
104 | SharedPreferences prefs = await SharedPreferences.getInstance();
105 |
106 | bool bright = prefs.getBool(ThemeState.kBrightnessKey);
107 | int primary = prefs.getInt(ThemeState.kPrimaryColorKey);
108 | int accent = prefs.getInt(ThemeState.kAccentColorKey);
109 |
110 | themeStore.dispatch(new ChangeThemeAction(
111 | brightness: bright == true ? Brightness.dark : Brightness.light,
112 | primaryColor:
113 | primary != null && primary >= 0 && primary > Colors.primaries.length
114 | ? Colors.primaries[primary]
115 | : null,
116 | accentColor:
117 | accent != null && accent >= 0 && accent > Colors.accents.length
118 | ? Colors.accents[accent]
119 | : null));
120 | }
121 |
--------------------------------------------------------------------------------
/lib/intl/messages_all.dart:
--------------------------------------------------------------------------------
1 | import 'package:intl/intl.dart';
2 |
3 | String allConversations() =>
4 | Intl.message("All Conversations", name: "allConversations", args: []);
5 |
6 | String people() => Intl.message("People", name: "people", args: []);
7 |
8 | String typeChatMessage() => Intl.message("Touch here to type a chat message.",
9 | name: "typeChatMessage", args: []);
10 |
11 | String communities() =>
12 | Intl.message("Communities", name: "communities", args: []);
13 |
14 | String logout() => Intl.message("Logout", name: "logout", args: []);
15 |
16 | String theme() => Intl.message("Theme", name: "theme", args: []);
17 |
18 | String settings() => Intl.message("Settings", name: "settings", args: []);
19 |
20 | String darkMode() => Intl.message("Dark mode", name: "darkMode", args: []);
21 |
22 | String primaryColor() =>
23 | Intl.message("Primary Color", name: "primaryColor", args: []);
24 |
25 | String accentColor() =>
26 | Intl.message("Accent Color", name: "accentColor", args: []);
27 |
--------------------------------------------------------------------------------
/lib/redux/actions.dart:
--------------------------------------------------------------------------------
1 | library flitter.redux.actions;
2 |
3 | import 'package:gitter/gitter.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:gitter/src/faye.dart';
6 |
7 | abstract class FlitterAction {
8 | FlitterAction();
9 |
10 | String toString() => '$runtimeType';
11 | }
12 |
13 | class AuthGitterAction extends FlitterAction {
14 | final GitterToken token;
15 | final GitterFayeSubscriber subscriber;
16 |
17 | AuthGitterAction(this.token, this.subscriber);
18 | }
19 |
20 | class FetchRoomsAction extends FlitterAction {
21 | final Iterable rooms;
22 |
23 | FetchRoomsAction(this.rooms);
24 | }
25 |
26 | class FetchGroupsAction extends FlitterAction {
27 | final Iterable groups;
28 |
29 | FetchGroupsAction(this.groups);
30 | }
31 |
32 | class LogoutAction extends FlitterAction {
33 | LogoutAction();
34 | }
35 |
36 | class FetchUser extends FlitterAction {
37 | final User user;
38 |
39 | FetchUser(this.user);
40 | }
41 |
42 | class SelectRoomAction extends FlitterAction {
43 | final Room room;
44 |
45 | SelectRoomAction(this.room);
46 | }
47 |
48 | class FetchMessagesForCurrentRoomAction extends FlitterAction {
49 | final Iterable messages;
50 |
51 | FetchMessagesForCurrentRoomAction(this.messages);
52 | }
53 |
54 | class OnMessagesForCurrentRoom extends FlitterAction {
55 | final Iterable messages;
56 |
57 | OnMessagesForCurrentRoom(this.messages);
58 | }
59 |
60 | class OnMessageForCurrentRoom extends FlitterAction {
61 | final Message message;
62 |
63 | OnMessageForCurrentRoom(this.message);
64 | }
65 |
66 | class JoinRoomAction extends FlitterAction {
67 | final Room room;
68 |
69 | JoinRoomAction(this.room);
70 | }
71 |
72 | class LeaveRoomAction extends FlitterAction {
73 | final Room room;
74 |
75 | LeaveRoomAction(this.room);
76 | }
77 |
78 | class OnSendMessage extends FlitterAction {
79 | final Message message;
80 |
81 | OnSendMessage(this.message);
82 | }
83 |
84 | class FetchRoomsOfGroup extends FlitterAction {
85 | final Iterable rooms;
86 |
87 | FetchRoomsOfGroup(this.rooms);
88 | }
89 |
90 | class SelectGroupAction extends FlitterAction {
91 | final Group group;
92 |
93 | SelectGroupAction(this.group);
94 | }
95 |
96 | class ShowSearchBarAction extends FlitterAction {
97 | ShowSearchBarAction();
98 | }
99 |
100 | class StartSearchAction extends FlitterAction {
101 | StartSearchAction();
102 | }
103 |
104 | class EndSearchAction extends FlitterAction {
105 | EndSearchAction();
106 | }
107 |
108 | class FetchSearchAction extends FlitterAction {
109 | final Iterable result;
110 |
111 | FetchSearchAction(this.result);
112 | }
113 |
114 | class ChangeThemeAction extends FlitterAction {
115 | final Brightness brightness;
116 | final MaterialColor primaryColor;
117 | final MaterialAccentColor accentColor;
118 |
119 | ChangeThemeAction({this.brightness, this.primaryColor, this.accentColor});
120 | }
121 |
122 | class UnreadMessagesForRoom extends FlitterAction {
123 | final String roomId;
124 | final num addMessage;
125 | final num removeMessage;
126 |
127 | UnreadMessagesForRoom(
128 | {this.roomId, this.addMessage: 0, this.removeMessage: 0});
129 | }
130 |
--------------------------------------------------------------------------------
/lib/redux/flitter_app_reducer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flitter/redux/actions.dart';
2 | import 'package:flitter/redux/flitter_app_state.dart';
3 | import 'package:flitter/redux/store.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:gitter/gitter.dart';
6 | import 'package:redux/redux.dart' as redux;
7 |
8 | T orElseNull() => null;
9 |
10 | class ThemeReducer extends redux.ReducerClass {
11 | final _mapper = const {ChangeThemeAction: _changeThemeAction};
12 |
13 | @override
14 | ThemeState call(ThemeState state, action) {
15 | Function reducer = _mapper[action.runtimeType];
16 | return reducer != null ? reducer(state, action) : state;
17 | }
18 | }
19 |
20 | ThemeState _changeThemeAction(ThemeState state, ChangeThemeAction action) {
21 | return state.apply(
22 | primaryColor: action.primaryColor,
23 | brightness: action.brightness,
24 | accentColor: action.accentColor);
25 | }
26 |
27 | class FlitterLoggingMiddleware
28 | implements redux.MiddlewareClass {
29 | const FlitterLoggingMiddleware();
30 |
31 | call(redux.Store store, action, next) {
32 | debugPrint('${new DateTime.now()}: $action');
33 | next(action);
34 | }
35 | }
36 |
37 | class GitterLoggingMiddleware
38 | implements redux.MiddlewareClass {
39 | const GitterLoggingMiddleware();
40 |
41 | call(redux.Store store, action, next) {
42 | debugPrint('${new DateTime.now()}: $action');
43 | next(action);
44 | }
45 | }
46 |
47 | class FlitterAppReducer extends redux.ReducerClass {
48 | final _mapper = const {
49 | FetchRoomsAction: _fetchRooms,
50 | FetchGroupsAction: _fetchGroups,
51 | FetchUser: _fetchUser,
52 | SelectRoomAction: _selectRoom,
53 | OnMessagesForCurrentRoom: _onMessages,
54 | OnSendMessage: _onSendMessage,
55 | FetchMessagesForCurrentRoomAction: _fetchMessages,
56 | JoinRoomAction: _joinRoom,
57 | LeaveRoomAction: _leaveRoom,
58 | SelectGroupAction: _selectGroup,
59 | FetchRoomsOfGroup: _fetchRoomsOfGroup,
60 | ShowSearchBarAction: _showSearchBar,
61 | StartSearchAction: _startSearch,
62 | EndSearchAction: _endSearch,
63 | FetchSearchAction: _fetchSearch,
64 | OnMessageForCurrentRoom: _onMessageForCurrentRoom,
65 | UnreadMessagesForRoom: _unreadMessageForRoom
66 | };
67 |
68 | @override
69 | FlitterAppState call(FlitterAppState state, action) {
70 | Function reducer = _mapper[action.runtimeType];
71 | return reducer != null ? reducer(state, action) : state;
72 | }
73 | }
74 |
75 | FlitterAppState _unreadMessageForRoom(
76 | FlitterAppState state, UnreadMessagesForRoom action) {
77 | if (action.roomId != null) {
78 | Room room = state.rooms.firstWhere((Room room) => room.id == action.roomId,
79 | orElse: orElseNull);
80 | room.unreadItems += action.addMessage;
81 | room.unreadItems -= action.removeMessage;
82 |
83 | List rooms =
84 | state.rooms.where((Room room) => room.id != action.roomId).toList();
85 | rooms.add(room);
86 |
87 | return state.apply(rooms: _sortRooms(rooms));
88 | }
89 | return state;
90 | }
91 |
92 | FlitterAppState _showSearchBar(
93 | FlitterAppState state, ShowSearchBarAction action) {
94 | return state.apply(search: state.search.apply(searching: true, result: []));
95 | }
96 |
97 | FlitterAppState _startSearch(FlitterAppState state, StartSearchAction action) {
98 | return state.apply(
99 | search:
100 | state.search.apply(searching: true, requesting: true, result: []));
101 | }
102 |
103 | FlitterAppState _fetchSearch(FlitterAppState state, FetchSearchAction action) {
104 | return state.apply(
105 | search: state.search
106 | .apply(searching: true, requesting: false, result: action.result));
107 | }
108 |
109 | FlitterAppState _endSearch(FlitterAppState state, EndSearchAction action) {
110 | return state.apply(
111 | search:
112 | state.search.apply(searching: false, requesting: false, result: []));
113 | }
114 |
115 | List _sortRooms(List rooms) {
116 | final unreadRooms = rooms.where((Room r) => r.unreadItems > 0).toList();
117 | unreadRooms.sort((Room a, Room b) {
118 | return b.unreadItems - a.unreadItems;
119 | });
120 |
121 | final readedRooms = rooms.where((Room r) => r.unreadItems == 0).toList();
122 | readedRooms.sort((Room a, Room b) {
123 | if (a.lastAccessTime != null && b.lastAccessTime != null) {
124 | DateTime lA = parseLastAccessTime(a.lastAccessTime);
125 | DateTime lB = parseLastAccessTime(b.lastAccessTime);
126 | return lB.millisecondsSinceEpoch - lA.millisecondsSinceEpoch;
127 | }
128 | return 0;
129 | });
130 |
131 | final _rooms = [];
132 | _rooms.addAll(unreadRooms);
133 | _rooms.addAll(readedRooms);
134 |
135 | return _rooms;
136 | }
137 |
138 | FlitterAppState _fetchRooms(FlitterAppState state, FetchRoomsAction action) {
139 | return state.apply(rooms: _sortRooms(action.rooms));
140 | }
141 |
142 | FlitterAppState _fetchGroups(FlitterAppState state, FetchGroupsAction action) {
143 | return state.apply(groups: action.groups);
144 | }
145 |
146 | FlitterAppState _fetchUser(FlitterAppState state, FetchUser action) {
147 | return state.apply(user: action.user);
148 | }
149 |
150 | FlitterAppState _selectRoom(FlitterAppState state, SelectRoomAction action) {
151 | CurrentRoomState current =
152 | new CurrentRoomState(room: action.room, messages: null);
153 | return state.apply(selectedRoom: current);
154 | }
155 |
156 | FlitterAppState _fetchMessages(
157 | FlitterAppState state, FetchMessagesForCurrentRoomAction action) {
158 | final currentRoom = state.selectedRoom?.apply(messages: action.messages);
159 | return state.apply(selectedRoom: currentRoom);
160 | }
161 |
162 | FlitterAppState _onMessages(
163 | FlitterAppState state, OnMessagesForCurrentRoom action) {
164 | final messages = new List.from(action.messages);
165 | final messagesRooms =
166 | new List.from(state.selectedRoom.messages ?? []);
167 | messages.addAll(messagesRooms ?? []);
168 | final currentRoom = state.selectedRoom?.apply(messages: messages);
169 | return state.apply(selectedRoom: currentRoom);
170 | }
171 |
172 | FlitterAppState _joinRoom(FlitterAppState state, JoinRoomAction action) {
173 | final rooms = new List.from(state.rooms);
174 | rooms.add(action.room);
175 | return state.apply(rooms: rooms);
176 | }
177 |
178 | FlitterAppState _leaveRoom(FlitterAppState state, LeaveRoomAction action) {
179 | final rooms = new List.from(state.rooms);
180 | rooms.removeWhere((Room room) => room.id == action.room.id);
181 | return state.apply(rooms: rooms);
182 | }
183 |
184 | FlitterAppState _onSendMessage(FlitterAppState state, OnSendMessage action) {
185 | Iterable messages = _addOrUpdateMessage(state, action.message);
186 | CurrentRoomState currentRoom = state.selectedRoom?.apply(messages: messages);
187 | return state.apply(selectedRoom: currentRoom);
188 | }
189 |
190 | FlitterAppState _selectGroup(FlitterAppState state, SelectGroupAction action) {
191 | CurrentGroupState current = new CurrentGroupState(group: action.group);
192 | return state.apply(selectedGroup: current);
193 | }
194 |
195 | FlitterAppState _fetchRoomsOfGroup(
196 | FlitterAppState state, FetchRoomsOfGroup action) {
197 | CurrentGroupState current = new CurrentGroupState(
198 | group: state.selectedGroup.group, rooms: action.rooms);
199 | return state.apply(selectedGroup: current);
200 | }
201 |
202 | FlitterAppState _onMessageForCurrentRoom(
203 | FlitterAppState state, OnMessageForCurrentRoom action) {
204 | Iterable messages = _addOrUpdateMessage(state, action.message);
205 |
206 | final currentRoom = state.selectedRoom?.apply(messages: messages);
207 | return state.apply(selectedRoom: currentRoom);
208 | }
209 |
210 | Iterable _addOrUpdateMessage(FlitterAppState state, Message message) {
211 | List messages = new List.from(state.selectedRoom.messages ?? []);
212 |
213 | final exist =
214 | messages.firstWhere((msg) => msg.id == message.id, orElse: orElseNull);
215 |
216 | if (exist != null) {
217 | final idx = messages.indexOf(exist);
218 | messages[idx] = message;
219 | } else {
220 | messages.add(message);
221 | }
222 | return messages;
223 | }
224 |
225 | class GitterReducer extends redux.ReducerClass {
226 | final _mapper = const {
227 | AuthGitterAction: _initGitter,
228 | LogoutAction: _logout
229 | };
230 |
231 | @override
232 | GitterState call(GitterState state, action) {
233 | Function reducer = _mapper[action.runtimeType];
234 | return reducer != null ? reducer(state, action) : state;
235 | }
236 | }
237 |
238 | GitterState _initGitter(GitterState state, AuthGitterAction action) {
239 | GitterApi api;
240 | if (action.token != null) {
241 | api = new GitterApi(action.token);
242 | }
243 | return state.apply(
244 | api: api, token: action.token, subscriber: action.subscriber);
245 | }
246 |
247 | GitterState _logout(GitterState state, LogoutAction action) {
248 | state.subscriber?.close();
249 | flitterStore = null;
250 | return new GitterState.initial();
251 | }
252 |
--------------------------------------------------------------------------------
/lib/redux/flitter_app_state.dart:
--------------------------------------------------------------------------------
1 | import 'package:gitter/gitter.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:gitter/src/faye.dart';
4 |
5 | class ThemeState {
6 | static final kBrightnessKey = "BrightnessKey";
7 | static final kPrimaryColorKey = "PrimaryColorKey";
8 | static final kAccentColorKey = "kAccentColorKey";
9 |
10 | final ThemeData _theme;
11 | final Brightness brightness;
12 | final MaterialColor primaryColor;
13 | final MaterialAccentColor accentColor;
14 |
15 | ThemeData get theme => _theme;
16 |
17 | ThemeState({this.brightness, this.primaryColor, this.accentColor})
18 | : _theme = new ThemeData(
19 | brightness: brightness,
20 | primarySwatch: primaryColor,
21 | accentColor: accentColor);
22 |
23 | factory ThemeState.initial() => new ThemeState(
24 | brightness: Brightness.light,
25 | primaryColor: Colors.indigo,
26 | accentColor: Colors.pinkAccent);
27 |
28 | ThemeState apply(
29 | {Brightness brightness,
30 | MaterialColor primaryColor,
31 | MaterialAccentColor accentColor}) {
32 | return new ThemeState(
33 | brightness: brightness ?? this.brightness,
34 | primaryColor: primaryColor ?? this.primaryColor,
35 | accentColor: accentColor ?? this.accentColor);
36 | }
37 | }
38 |
39 | class SearchState {
40 | final Iterable result;
41 | final bool requesting;
42 | final bool searching;
43 |
44 | SearchState({this.result, this.requesting, this.searching});
45 |
46 | SearchState.initial()
47 | : result = [],
48 | requesting = false,
49 | searching = false;
50 |
51 | SearchState apply({Iterable result, bool requesting, bool searching}) {
52 | return new SearchState(
53 | result: result ?? this.result,
54 | requesting: requesting ?? this.requesting,
55 | searching: searching ?? this.searching);
56 | }
57 | }
58 |
59 | class CurrentRoomState {
60 | final Room room;
61 | final Iterable messages;
62 |
63 | CurrentRoomState({this.room, this.messages});
64 |
65 | CurrentRoomState apply({Room room, Iterable messages}) {
66 | return new CurrentRoomState(
67 | room: room ?? this.room, messages: messages ?? this.messages);
68 | }
69 | }
70 |
71 | class CurrentGroupState {
72 | final Group group;
73 | final Iterable rooms;
74 |
75 | CurrentGroupState({this.group, this.rooms});
76 |
77 | CurrentGroupState apply({Group group, Iterable rooms}) {
78 | return new CurrentGroupState(
79 | group: group ?? this.group, rooms: rooms ?? this.rooms);
80 | }
81 | }
82 |
83 | class FlitterAppState {
84 | final Iterable rooms;
85 | final Iterable groups;
86 | final User user;
87 | final CurrentRoomState selectedRoom;
88 | final CurrentGroupState selectedGroup;
89 | final SearchState search;
90 |
91 | FlitterAppState(
92 | {this.rooms,
93 | this.groups,
94 | this.user,
95 | this.search,
96 | this.selectedRoom,
97 | this.selectedGroup});
98 |
99 | FlitterAppState.initial()
100 | : rooms = null,
101 | groups = null,
102 | user = null,
103 | selectedRoom = null,
104 | search = new SearchState.initial(),
105 | selectedGroup = null;
106 |
107 | FlitterAppState apply(
108 | {Iterable rooms,
109 | Iterable groups,
110 | User user,
111 | bool init,
112 | CurrentRoomState selectedRoom,
113 | SearchState search,
114 | CurrentGroupState selectedGroup,
115 | GitterApi api,
116 | GitterToken token}) {
117 | return new FlitterAppState(
118 | rooms: rooms ?? this.rooms,
119 | groups: groups ?? this.groups,
120 | user: user ?? this.user,
121 | selectedRoom: selectedRoom ?? this.selectedRoom,
122 | search: search ?? this.search,
123 | selectedGroup: selectedGroup ?? this.selectedGroup);
124 | }
125 | }
126 |
127 | class GitterState {
128 | final GitterApi api;
129 | final GitterToken token;
130 | final GitterFayeSubscriber subscriber;
131 |
132 | GitterState({this.api, this.token, this.subscriber});
133 |
134 | GitterState apply(
135 | {GitterApi api, GitterToken token, GitterFayeSubscriber subscriber}) {
136 | return new GitterState(
137 | api: api ?? this.api,
138 | token: token ?? this.token,
139 | subscriber: subscriber ?? this.subscriber);
140 | }
141 |
142 | GitterState.initial()
143 | : api = null,
144 | token = null,
145 | subscriber = null;
146 | }
147 |
--------------------------------------------------------------------------------
/lib/redux/store.dart:
--------------------------------------------------------------------------------
1 | library flitter.redux.store;
2 |
3 | import 'package:flitter/redux/flitter_app_reducer.dart';
4 | import 'package:flitter/redux/flitter_app_state.dart';
5 | import 'package:gitter/gitter.dart';
6 | import 'package:gitter/src/faye.dart';
7 | import 'package:redux/redux.dart' as redux;
8 |
9 | class FlitterStore extends redux.Store {
10 | FlitterStore(
11 | {FlitterAppState initialState,
12 | redux.Reducer reducer,
13 | List middlewares: const [
14 | const FlitterLoggingMiddleware()
15 | ]})
16 | : super(reducer ?? new FlitterAppReducer(),
17 | initialState: initialState ?? new FlitterAppState.initial(),
18 | middleware: middlewares);
19 | }
20 |
21 | class ThemeStore extends redux.Store {
22 | ThemeStore(
23 | {ThemeState initialState,
24 | redux.Reducer reducer})
25 | : super(reducer ?? new ThemeReducer(),
26 | initialState: initialState ?? new ThemeState.initial());
27 | }
28 |
29 | class GitterStore extends redux.Store {
30 | GitterStore(
31 | {GitterState initialState,
32 | redux.Reducer reducer,
33 | List middlewares: const [
34 | const GitterLoggingMiddleware()
35 | ]})
36 | : super(reducer ?? new GitterReducer(),
37 | initialState: initialState ?? new GitterState.initial(),
38 | middleware: middlewares);
39 | }
40 |
41 | FlitterStore flitterStore;
42 | GitterStore gitterStore;
43 | ThemeStore themeStore;
44 |
45 | GitterApi get gitterApi => gitterStore.state.api;
46 | GitterToken get gitterToken => gitterStore.state.token;
47 | GitterFayeSubscriber get gitterSubscriber => gitterStore.state.subscriber;
48 |
--------------------------------------------------------------------------------
/lib/services/flitter_auth.dart:
--------------------------------------------------------------------------------
1 | library flitter.auth;
2 |
3 | import 'dart:async';
4 | import 'dart:convert';
5 |
6 | import 'package:flitter/redux/store.dart';
7 | import 'package:flitter/services/flitter_config.dart';
8 | import 'package:gitter/gitter.dart';
9 | import 'package:flitter/services/flutter_gitter_auth.dart';
10 | import 'package:flitter/redux/actions.dart';
11 | import 'package:gitter/src/oauth/oauth.dart';
12 | import 'package:shared_preferences/shared_preferences.dart';
13 |
14 | class FlitterAuth {
15 | static const _tokenKey = "gitter_token";
16 |
17 | static Future getToken() async {
18 | final prefs = await SharedPreferences.getInstance();
19 | final tokenJson = prefs.getString(_tokenKey);
20 | if (tokenJson == null) {
21 | return null;
22 | }
23 | return new GitterToken.fromJson(JSON.decode(tokenJson));
24 | }
25 |
26 | static Future saveToken(GitterToken token) async {
27 | final prefs = await SharedPreferences.getInstance();
28 | prefs.setString(
29 | _tokenKey, token == null ? null : JSON.encode(token?.toMap()));
30 | return prefs.commit();
31 | }
32 |
33 | static Future auth() async {
34 | final cfg = Config.getInstance();
35 | final GitterOAuth gitterOAuth = new FlutterGitterOAuth(new AppInformations(
36 | cfg.gitter.appId,
37 | cfg.gitter.appSecret,
38 | cfg.gitter.redirectionUrl,
39 | ));
40 | GitterToken token = await gitterOAuth.signIn();
41 | await saveToken(token);
42 | return token;
43 | }
44 |
45 | static Future logout() async {
46 | saveToken(null);
47 | gitterStore.dispatch(new LogoutAction());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/services/flitter_config.dart:
--------------------------------------------------------------------------------
1 | import 'package:meta/meta.dart';
2 |
3 | class Config {
4 | final GitterConfig gitter;
5 |
6 | static Config _instance;
7 |
8 | Config._({this.gitter});
9 |
10 | static init({@required GitterConfig gitter}) =>
11 | _instance ??= new Config._(gitter: gitter);
12 |
13 | static Config getInstance() => _instance;
14 | }
15 |
16 | class GitterConfig {
17 | final String appId;
18 | final String appSecret;
19 | final String redirectionUrl;
20 |
21 | const GitterConfig(
22 | {@required this.appId,
23 | @required this.appSecret,
24 | @required this.redirectionUrl});
25 | }
26 |
--------------------------------------------------------------------------------
/lib/services/flitter_request.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:flitter/redux/actions.dart';
3 | import 'package:flitter/redux/store.dart';
4 | import 'package:gitter/gitter.dart';
5 | import 'package:gitter/src/faye.dart';
6 | import 'package:gitter/src/models/faye_message.dart';
7 |
8 | Future> fetchRooms() async {
9 | final rooms = await gitterApi.user.me.rooms();
10 | flitterStore.dispatch(new FetchRoomsAction(rooms));
11 | subscribeToUnreadMessages(rooms);
12 | return rooms;
13 | }
14 |
15 | Future fetchUser() async {
16 | final user = await gitterApi.user.me.get();
17 | flitterStore.dispatch(new FetchUser(user));
18 | return user;
19 | }
20 |
21 | Future> fetchGroups() async {
22 | final groups = await gitterApi.group.get();
23 | flitterStore.dispatch(new FetchGroupsAction(groups));
24 | return groups;
25 | }
26 |
27 | Future> fetchRoomsOfGroup() async {
28 | final groupId = flitterStore.state.selectedGroup.group.id;
29 | final rooms = await gitterApi.group.suggestedRoomsOf(groupId);
30 | flitterStore.dispatch(new FetchRoomsOfGroup(rooms));
31 | subscribeToUnreadMessages(rooms);
32 | return rooms;
33 | }
34 |
35 | Future> fetchMessagesOfRoom(
36 | String roomId, String beforeId) async {
37 | final messages =
38 | await gitterApi.room.messagesFromRoomId(roomId, beforeId: beforeId);
39 | flitterStore.dispatch(new OnMessagesForCurrentRoom(messages));
40 | return messages;
41 | }
42 |
43 | Future leaveRoom(Room room) async {
44 | final success =
45 | await gitterApi.room.removeUserFrom(room.id, flitterStore.state.user.id);
46 | if (success == true) {
47 | flitterStore.dispatch(new LeaveRoomAction(room));
48 | }
49 | return success;
50 | }
51 |
52 | Future joinRoom(Room room) async {
53 | final joinedRoom =
54 | await gitterApi.user.userJoinRoom(flitterStore.state.user.id, room.id);
55 | flitterStore.dispatch(new JoinRoomAction(room));
56 | return joinedRoom;
57 | }
58 |
59 | Future sendMessage(String value, Room room) async {
60 | final message = await gitterApi.room.sendMessageToRoomId(room.id, value);
61 | flitterStore.dispatch(new OnSendMessage(message));
62 | return message;
63 | }
64 |
65 | Future search(String query) async {
66 | flitterStore.dispatch(new StartSearchAction());
67 | final result = [];
68 | result.addAll(await gitterApi.user.search(query, limit: 5));
69 | result.addAll(await gitterApi.room.search(query, limit: 10));
70 | flitterStore.dispatch(new FetchSearchAction(result));
71 | return result;
72 | }
73 |
74 | initStores(GitterToken token) async {
75 | flitterStore = new FlitterStore();
76 | gitterStore
77 | .dispatch(new AuthGitterAction(token, await initWebSocket(token.access)));
78 | fetchRooms();
79 | fetchGroups();
80 | }
81 |
82 | List _mapperUnreads;
83 |
84 | Future initWebSocket(String token) async {
85 | gitterSubscriber?.close();
86 | _mapperUnreads = [];
87 | GitterFayeSubscriber subscriber = new GitterFayeSubscriber(token);
88 | await subscriber.connect();
89 | flitterStore.dispatch(new FetchUser(subscriber.user));
90 | return subscriber;
91 | }
92 |
93 | subscribeToUnreadMessages(List rooms) {
94 | final newRooms =
95 | rooms.map((r) => r.id).where((r) => !_mapperUnreads.contains(r)).toList();
96 | _mapperUnreads.addAll(newRooms);
97 | for (String roomId in newRooms) {
98 | gitterSubscriber.subscribeToUserRoomUnreadItems(
99 | roomId, gitterSubscriber.user.id, (List messages) {
100 | for (GitterFayeMessage msg in messages) {
101 | if (msg.data != null &&
102 | msg.data["notification"] == GitterFayeNotifications.unreadItems) {
103 | flitterStore.dispatch(new UnreadMessagesForRoom(
104 | roomId: roomId, addMessage: msg.data["items"]["chat"].length));
105 | } else if (msg.data != null &&
106 | msg.data["notification"] ==
107 | GitterFayeNotifications.unreadItemsRemoved) {
108 | flitterStore.dispatch(new UnreadMessagesForRoom(
109 | roomId: roomId, removeMessage: msg.data["items"]["chat"].length));
110 | }
111 | }
112 | });
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/lib/services/flutter_gitter_auth.dart:
--------------------------------------------------------------------------------
1 | library gitter.flutter.auth;
2 |
3 | import 'dart:async';
4 | import 'dart:io';
5 |
6 | import 'package:flutter/services.dart';
7 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
8 | import 'package:gitter/gitter.dart';
9 | import 'package:gitter/src/oauth/oauth.dart';
10 |
11 | Future getContent() async {
12 | final content = await rootBundle.loadString("assets/html/success.html");
13 | if (content.isNotEmpty) {
14 | return content;
15 | }
16 | return """
17 |
18 |
19 | You can now close this page !
20 |
21 |
22 | """;
23 | }
24 |
25 | class FlutterGitterOAuth extends GitterOAuth {
26 | final StreamController _onCode = new StreamController();
27 |
28 | final FlutterWebviewPlugin flutterWebviewPlugin = new FlutterWebviewPlugin();
29 |
30 | var _isOpen = false;
31 | var _server;
32 | var _onCodeStream;
33 |
34 | Stream get onCode =>
35 | _onCodeStream ??= _onCode.stream.asBroadcastStream();
36 |
37 | FlutterGitterOAuth(AppInformations appInformations, {bool force: false})
38 | : super(appInformations, force: force);
39 |
40 | @override
41 | Future requestCode() async {
42 | if (shouldRequestCode() && !_isOpen) {
43 | // close any open browser (happen on hot reload)
44 | // FIXME: await flutterWebviewPlugin.close();
45 | _isOpen = true;
46 |
47 | // init server
48 | _server = await _createServer();
49 | _listenCode(_server);
50 |
51 | // construct url
52 | final String urlParams = constructUrlParams();
53 |
54 | // catch onDestroy event of WebView
55 | flutterWebviewPlugin.onDestroy.first.then((_) {
56 | _close();
57 | });
58 |
59 | // launch url inside webview
60 | flutterWebviewPlugin.launch("${codeInformations.url}?$urlParams",
61 | clearCookies: true);
62 |
63 | code = await onCode.first;
64 | _close();
65 | }
66 | return code;
67 | }
68 |
69 | void _close([_]) {
70 | if (_isOpen) {
71 | // close server
72 | _server.close(force: true);
73 |
74 | flutterWebviewPlugin.close();
75 | }
76 | _isOpen = false;
77 | }
78 |
79 | Future _createServer() async {
80 | final server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 8080,
81 | shared: true);
82 | return server;
83 | }
84 |
85 | _listenCode(HttpServer server) {
86 | server.listen((HttpRequest request) async {
87 | final uri = request.uri;
88 | request.response
89 | ..statusCode = 200
90 | ..headers.set("Content-Type", ContentType.HTML.mimeType)
91 | ..write(await getContent());
92 |
93 | final code = uri.queryParameters["code"];
94 | final error = uri.queryParameters["error"];
95 | await request.response.close();
96 | if (code != null && error == null) {
97 | _onCode.add(code);
98 | } else if (error != null) {
99 | _onCode.add(null);
100 | _onCode.addError(error);
101 | }
102 | });
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/lib/widgets/common/chat_room.dart:
--------------------------------------------------------------------------------
1 | library flitter.common.chat_room_widget;
2 |
3 | import 'dart:async';
4 | import 'package:flitter/redux/store.dart';
5 | import 'package:flitter/services/flitter_request.dart';
6 | import 'package:gitter/gitter.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter/services.dart';
9 | import 'package:meta/meta.dart';
10 | import 'package:flitter/intl/messages_all.dart' as intl;
11 | import 'package:intl/intl.dart';
12 | import 'package:flutter_markdown/flutter_markdown.dart';
13 | import 'package:url_launcher/url_launcher.dart' as url_launcher;
14 |
15 | final _dateFormat = new DateFormat.MMMd()..add_Hm();
16 |
17 | class ChatRoom extends StatefulWidget {
18 | final Iterable messages;
19 | final Room room;
20 | final _onNeedData;
21 |
22 | @override
23 | _ChatRoomWidgetState createState() => new _ChatRoomWidgetState();
24 |
25 | ChatRoom({@required this.messages, @required this.room})
26 | : _onNeedData = new StreamController();
27 |
28 | Stream get onNeedDataStream => onNeedDataController.stream;
29 |
30 | StreamController get onNeedDataController => _onNeedData;
31 | }
32 |
33 | class _ChatRoomWidgetState extends State {
34 | bool get _userHasJoined =>
35 | flitterStore.state.rooms.any((Room r) => r.id == widget.room.id);
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | var children = [
40 | new Flexible(
41 | child: new ListView.builder(
42 | reverse: true,
43 | itemCount: widget.messages.length,
44 | itemBuilder: _buildListItem,
45 | )),
46 | ];
47 |
48 | if (_userHasJoined) {
49 | children.addAll([
50 | new Divider(height: 1.0),
51 | new Container(
52 | decoration: new BoxDecoration(color: Theme.of(context).cardColor),
53 | child: _buildChatInput())
54 | ]);
55 | }
56 |
57 | return new Column(children: children);
58 | }
59 |
60 | Widget _buildChatInput() => new ChatInput(
61 | onSubmit: (String value) async {
62 | sendMessage(value, widget.room);
63 | },
64 | );
65 |
66 | _shouldMergeMessages(Message message, int index) =>
67 | index != widget.messages.length - 1 &&
68 | widget.messages.elementAt(index + 1).fromUser.id == message.fromUser.id &&
69 | message.sent
70 | .difference(widget.messages.elementAt(index + 1).sent)
71 | .inMinutes <=
72 | 10;
73 |
74 | _buildListItem(BuildContext context, int index) {
75 | final message = widget.messages.elementAt(index);
76 |
77 | if (widget.messages.length >= 50 && index == widget.messages.length - 5) {
78 | widget.onNeedDataController.add(null);
79 | }
80 |
81 | if (_shouldMergeMessages(message, index)) {
82 | return new ChatMessage(
83 | withDivider: false,
84 | withAvatar: false,
85 | withTitle: false,
86 | message: message,
87 | atBottom: index == 0);
88 | }
89 |
90 | return new ChatMessage(message: message, atBottom: index == 0);
91 | }
92 | }
93 |
94 | class ChatInput extends StatefulWidget {
95 | final ValueChanged onSubmit;
96 |
97 | ChatInput({@required this.onSubmit});
98 |
99 | @override
100 | _ChatInputState createState() => new _ChatInputState();
101 | }
102 |
103 | class _ChatInputState extends State {
104 | final _textController = new TextEditingController();
105 |
106 | @override
107 | Widget build(BuildContext context) {
108 | return new Container(
109 | padding: new EdgeInsets.all(8.0),
110 | decoration: new BoxDecoration(color: Theme.of(context).cardColor),
111 | child: new IconTheme(
112 | data: new IconThemeData(color: Theme.of(context).accentColor),
113 | child: new Container(
114 | margin: const EdgeInsets.symmetric(horizontal: 8.0),
115 | child: new Row(children: [
116 | new Flexible(
117 | child: new TextField(
118 | onSubmitted: (_) => _handleSubmitted(),
119 | controller: _textController,
120 | decoration: new InputDecoration.collapsed(
121 | hintText: intl.typeChatMessage()),
122 | maxLines: 3,
123 | ),
124 | ),
125 | new Container(
126 | margin: new EdgeInsets.symmetric(horizontal: 4.0),
127 | child: new IconButton(
128 | icon: new Icon(Icons.send),
129 | onPressed: _handleSubmitted)),
130 | ]))));
131 | }
132 |
133 | _handleSubmitted() {
134 | String value = _textController.text;
135 | _textController.clear();
136 | if (value.isNotEmpty) {
137 | widget.onSubmit(value);
138 | }
139 | }
140 | }
141 |
142 | class ChatMessage extends StatelessWidget {
143 | final Message message;
144 | final bool withDivider;
145 | final bool withAvatar;
146 | final bool withTitle;
147 | final bool atBottom;
148 |
149 | ChatMessage(
150 | {@required this.message,
151 | this.withDivider: true,
152 | this.withAvatar: true,
153 | this.withTitle: true,
154 | this.atBottom: false});
155 |
156 | @override
157 | Widget build(BuildContext context) {
158 | final row = [];
159 |
160 | if (withAvatar) {
161 | row.add(new ChatMessageAvatar(
162 | avatar: new NetworkImage(message.fromUser.avatarUrlSmall)));
163 | } else {
164 | row.add(new Container(width: 54.0));
165 | }
166 |
167 | row.add(new Expanded(
168 | child: new ChatMessageContent(message: message, withTitle: withTitle)));
169 |
170 | final column = [];
171 |
172 | if (withDivider) {
173 | column.add(new Divider(color: Colors.grey[200]));
174 | }
175 |
176 | column.add(new Padding(
177 | child: new Row(
178 | children: row, crossAxisAlignment: CrossAxisAlignment.start),
179 | padding: new EdgeInsets.only(
180 | bottom: withTitle || atBottom ? 8.0 : 0.0, right: 12.0)));
181 |
182 | return new Column(children: column);
183 | }
184 | }
185 |
186 | class ChatMessageAvatar extends StatelessWidget {
187 | final ImageProvider avatar;
188 |
189 | ChatMessageAvatar({@required this.avatar});
190 |
191 | @override
192 | Widget build(BuildContext context) {
193 | return new Column(children: [
194 | new Container(
195 | margin: new EdgeInsets.only(left: 12.0, right: 12.0, top: 12.0),
196 | width: 30.0,
197 | child: new CircleAvatar(
198 | backgroundImage: avatar, backgroundColor: Colors.grey[200]),
199 | )
200 | ], crossAxisAlignment: CrossAxisAlignment.start);
201 | }
202 | }
203 |
204 | class ChatMessageContent extends StatelessWidget {
205 | final Message message;
206 | final bool withTitle;
207 |
208 | ChatMessageContent({@required this.message, this.withTitle: true});
209 |
210 | TextStyle _titleTextStyle() {
211 | return new TextStyle(color: Colors.grey);
212 | }
213 |
214 | @override
215 | Widget build(BuildContext context) {
216 | final column = [];
217 |
218 | if (message.fromUser.displayName != null) {
219 | column.add(new AnimatedDefaultTextStyle(
220 | style: _titleTextStyle(),
221 | duration: kThemeChangeDuration,
222 | child: new Container(
223 | padding: new EdgeInsets.only(bottom: 6.0),
224 | child: withTitle
225 | ? new Row(children: [
226 | new Expanded(
227 | child: new Text(message.fromUser.displayName,
228 | softWrap: true)),
229 | new Text(_dateFormat.format(message.sent))
230 | ])
231 | : null)));
232 | }
233 |
234 | column.add(new MarkdownBody(
235 | data: message.text,
236 | //fixme does not seem to work
237 | onTapLink: (String url) async {
238 | bool can = await url_launcher.canLaunch(url);
239 | if (can) {
240 | url_launcher.launch(url);
241 | }
242 | }));
243 |
244 | return new Column(
245 | mainAxisSize: MainAxisSize.min,
246 | crossAxisAlignment: CrossAxisAlignment.start,
247 | children: column);
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/lib/widgets/common/drawer.dart:
--------------------------------------------------------------------------------
1 | library flitter.common.drawer;
2 |
3 | import 'package:flitter/redux/actions.dart';
4 | import 'package:flitter/redux/store.dart';
5 | import 'package:flitter/services/flitter_auth.dart';
6 | import 'package:flitter/widgets/routes/settings.dart';
7 | import 'package:flutter/src/rendering/sliver.dart';
8 | import 'package:flutter/src/rendering/sliver_grid.dart';
9 | import 'package:gitter/gitter.dart';
10 | import 'package:flitter/widgets/routes/group.dart';
11 | import 'package:flutter/material.dart';
12 | import 'package:meta/meta.dart';
13 | import 'package:flutter/painting.dart';
14 | import 'package:flitter/intl/messages_all.dart' as intl;
15 |
16 | class FlitterDrawer extends StatefulWidget {
17 | final VoidCallback onTapAllConversation;
18 | final VoidCallback onTapPeoples;
19 | final VoidCallback onTapSettings;
20 |
21 | FlitterDrawer(
22 | {@required this.onTapAllConversation,
23 | @required this.onTapPeoples,
24 | @required this.onTapSettings});
25 |
26 | @override
27 | _FlitterDrawerState createState() => new _FlitterDrawerState();
28 | }
29 |
30 | class _FlitterDrawerState extends State {
31 | var _subscription;
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | if (flitterStore.state.user != null) {
36 | return new Drawer(
37 | child: new FlitterDrawerContent(
38 | onTapAllConversation: widget.onTapAllConversation,
39 | onTapPeoples: widget.onTapPeoples,
40 | onTapSettings: widget.onTapSettings));
41 | }
42 | return new Center(child: new CircularProgressIndicator());
43 | }
44 |
45 | ////////
46 |
47 | @override
48 | void initState() {
49 | super.initState();
50 | _subscription = flitterStore.onChange.listen((_) {
51 | setState(() {});
52 | });
53 | }
54 |
55 | @override
56 | void dispose() {
57 | super.dispose();
58 | _subscription.cancel();
59 | }
60 | }
61 |
62 | class FlitterDrawerContent extends StatelessWidget {
63 | final VoidCallback onTapAllConversation;
64 | final VoidCallback onTapPeoples;
65 | final VoidCallback onTapSettings;
66 |
67 | FlitterDrawerContent(
68 | {@required this.onTapAllConversation,
69 | @required this.onTapPeoples,
70 | @required this.onTapSettings});
71 |
72 | @override
73 | Widget build(BuildContext context) {
74 | final child = [
75 | new FlitterDrawerHeader(),
76 | new ListTile(
77 | leading: new Icon(Icons.home),
78 | title: new Text(intl.allConversations()),
79 | onTap: onTapAllConversation),
80 | new ListTile(
81 | leading: new Icon(Icons.person),
82 | title: new Text(intl.people()),
83 | onTap: onTapPeoples),
84 | new ListTile(
85 | leading: new Icon(Icons.settings),
86 | title: new Text(intl.settings()),
87 | onTap: onTapSettings)
88 | ];
89 | child.addAll(_drawerCommunities(context));
90 | child.addAll(_drawerFooter(context));
91 | return new ListView(children: child);
92 | }
93 |
94 | Iterable _drawerCommunities(BuildContext context) {
95 | final communities = [
96 | new Divider(),
97 | new ListTile(title: new Text(intl.communities()), dense: true)
98 | ];
99 |
100 | communities.addAll(_buildCommunities(context));
101 | return communities;
102 | }
103 |
104 | Iterable _drawerFooter(BuildContext context) =>
105 | [new Divider(), new FlitterDrawerFooter()];
106 |
107 | Iterable _buildCommunities(BuildContext context) {
108 | if (flitterStore.state.groups == null) {
109 | return [];
110 | }
111 | return flitterStore.state.groups.map((group) {
112 | return new FlitterDrawerCommunityTile(group: group);
113 | }).toList();
114 | }
115 | }
116 |
117 | class FlitterDrawerHeader extends StatelessWidget {
118 | @override
119 | Widget build(BuildContext context) {
120 | return new UserAccountsDrawerHeader(
121 | accountName: new Text(flitterStore.state.user.username),
122 | accountEmail: new Text(flitterStore.state.user.displayName),
123 | currentAccountPicture: new CircleAvatar(
124 | backgroundImage:
125 | new NetworkImage(flitterStore.state.user.avatarUrlMedium)),
126 | decoration: new BoxDecoration(
127 | image: new DecorationImage(
128 | image: new AssetImage('assets/images/banner.jpg'),
129 | fit: BoxFit.cover)));
130 | }
131 | }
132 |
133 | class FlitterDrawerFooter extends StatelessWidget {
134 | @override
135 | Widget build(BuildContext context) {
136 | return new ListTile(
137 | leading: new Icon(Icons.exit_to_app),
138 | title: new Text(intl.logout()),
139 | onTap: () {
140 | Navigator.of(context).pop();
141 | FlitterAuth.logout();
142 | });
143 | }
144 | }
145 |
146 | class FlitterDrawerCommunityTile extends StatelessWidget {
147 | final Group group;
148 |
149 | FlitterDrawerCommunityTile({@required this.group});
150 |
151 | @override
152 | Widget build(BuildContext context) {
153 | return new ListTile(
154 | dense: false,
155 | title: new Text(group.name),
156 | leading: new CircleAvatar(
157 | backgroundImage: new NetworkImage(group.avatarUrl),
158 | backgroundColor: Theme.of(context).canvasColor),
159 | trailing: null,
160 | //TODO: unread inside roomsOf(group)
161 | onTap: () {
162 | flitterStore.dispatch(new SelectGroupAction(group));
163 | GroupView.go(context, group);
164 | });
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/lib/widgets/common/list_room.dart:
--------------------------------------------------------------------------------
1 | library flitter.common.list_room;
2 |
3 | import 'package:flitter/widgets/common/utils.dart';
4 | import 'package:meta/meta.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flitter/redux/store.dart';
7 | import 'package:gitter/gitter.dart';
8 | import 'package:flitter/widgets/routes/room.dart';
9 | import 'package:flitter/redux/actions.dart';
10 |
11 | class ListRoom extends StatelessWidget {
12 | final GlobalKey _refreshIndicatorKey =
13 | new GlobalKey();
14 |
15 | final Iterable rooms;
16 | final RefreshCallback onRefresh;
17 |
18 | ListRoom({@required this.rooms, @required this.onRefresh});
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return new RefreshIndicator(
23 | child: new ListView.builder(
24 | physics: const AlwaysScrollableScrollPhysics(),
25 | key: _refreshIndicatorKey,
26 | itemCount: rooms.length,
27 | itemBuilder: _buildListTile,
28 | ),
29 | onRefresh: onRefresh);
30 | }
31 |
32 | Widget _buildListTile(BuildContext context, int index) {
33 | return new RoomTile(room: rooms.elementAt(index));
34 | }
35 | }
36 |
37 | class RoomTile extends StatelessWidget {
38 | final Room room;
39 |
40 | RoomTile({@required this.room});
41 |
42 | @override
43 | Widget build(BuildContext context) {
44 | return new ListTile(
45 | dense: false,
46 | title: new Text(room.name),
47 | leading: new CircleAvatar(
48 | backgroundImage:
49 | room.avatarUrl != null ? new NetworkImage(room.avatarUrl) : null,
50 | backgroundColor: Theme.of(context).canvasColor),
51 | trailing: room?.unreadItems != null && room.unreadItems > 0
52 | ? new MessageCount(room.unreadItems)
53 | : null,
54 | onTap: () {
55 | flitterStore.dispatch(new SelectRoomAction(room));
56 | materialNavigateTo(context, new RoomView(), path: RoomView.path);
57 | },
58 | );
59 | }
60 | }
61 |
62 | class UserTile extends StatelessWidget {
63 | final User user;
64 |
65 | UserTile({@required this.user});
66 |
67 | @override
68 | Widget build(BuildContext context) {
69 | return new ListTile(
70 | dense: false,
71 | title: new Text(user.username),
72 | leading: new CircleAvatar(
73 | backgroundImage: new NetworkImage(user.avatarUrlSmall),
74 | backgroundColor: Theme.of(context).canvasColor),
75 | onTap: () async {
76 | final Room room = await gitterApi.room.roomFromUri(user.url);
77 | flitterStore.dispatch(new SelectRoomAction(room));
78 | materialNavigateTo(context, new RoomView(), path: RoomView.path);
79 | },
80 | );
81 | }
82 | }
83 |
84 | class MessageCount extends StatelessWidget {
85 | final int count;
86 |
87 | MessageCount(this.count);
88 |
89 | @override
90 | Widget build(BuildContext context) {
91 | return new Row(children: [
92 | new Container(
93 | decoration: new BoxDecoration(
94 | color: Theme.of(context).accentColor,
95 | borderRadius: new BorderRadius.circular(16.0)),
96 | padding:
97 | new EdgeInsets.only(left: 6.0, right: 6.0, top: 4.0, bottom: 4.0),
98 | child: new DefaultTextStyle(
99 | style: new TextStyle(fontSize: 10.0), child: new Text("$count")))
100 | ]);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib/widgets/common/search.dart:
--------------------------------------------------------------------------------
1 | library flitter.common.search;
2 |
3 | import 'package:flitter/widgets/common/list_room.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:gitter/gitter.dart';
6 | import 'package:meta/meta.dart';
7 | import 'package:flitter/redux/actions.dart';
8 | import 'package:flitter/redux/store.dart';
9 | import 'package:flitter/services/flitter_request.dart';
10 |
11 | class SearchBar extends StatelessWidget {
12 | final VoidCallback onSearchEnd;
13 | final ValueChanged onChange;
14 | final TextEditingController controller;
15 | final String hintText;
16 |
17 | SearchBar({this.onSearchEnd, this.controller, this.hintText, this.onChange});
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return buildSearchBar(context, hintText,
22 | onSearchEnd: onSearchEnd, onChange: onChange);
23 | }
24 |
25 | //todo: remove this when Scaffold will be less restrictive for appBar
26 | static Widget buildSearchBar(BuildContext context, String hintText,
27 | {TextEditingController controller,
28 | ValueChanged onChange,
29 | VoidCallback onSearchEnd}) =>
30 | new AppBar(
31 | leading: new IconButton(
32 | icon: const Icon(Icons.arrow_back),
33 | color: Theme.of(context).accentColor,
34 | onPressed: onSearchEnd),
35 | title: new TextField(
36 | controller: controller,
37 | onChanged: onChange,
38 | autofocus: true,
39 | decoration: new InputDecoration(
40 | hintText: hintText,
41 | ),
42 | ),
43 | backgroundColor: Theme.of(context).canvasColor,
44 | );
45 | }
46 |
47 | class ListSearchResult extends StatelessWidget {
48 | final Iterable results;
49 |
50 | ListSearchResult(this.results);
51 |
52 | @override
53 | Widget build(BuildContext context) => new ListView.builder(
54 | physics: const AlwaysScrollableScrollPhysics(),
55 | itemCount: results.length,
56 | itemBuilder: _buildListTile,
57 | );
58 |
59 | _buildListTile(BuildContext context, int index) =>
60 | new SearchResultTile(result: results.elementAt(index));
61 | }
62 |
63 | class SearchResultTile extends StatelessWidget {
64 | final result;
65 |
66 | SearchResultTile({@required this.result});
67 |
68 | @override
69 | Widget build(BuildContext context) {
70 | if (result is Room) {
71 | return new RoomTile(room: result);
72 | } else if (result is User) {
73 | return new UserTile(user: result);
74 | }
75 | return new ListTile();
76 | }
77 | }
78 |
79 | class ScaffoldWithSearchbar extends StatefulWidget {
80 | final Widget body;
81 | final String title;
82 | final Widget drawer;
83 |
84 | ScaffoldWithSearchbar(
85 | {@required this.body, @required this.title, this.drawer});
86 |
87 | @override
88 | _ScaffoldWithSearchbarState createState() =>
89 | new _ScaffoldWithSearchbarState();
90 | }
91 |
92 | class _ScaffoldWithSearchbarState extends State {
93 | @override
94 | Widget build(BuildContext context) {
95 | var body;
96 | if (flitterStore.state.search.searching == true) {
97 | if (flitterStore.state.search.requesting == true) {
98 | body = new Container(child: new LinearProgressIndicator());
99 | } else {
100 | body = _buildSearchResult();
101 | }
102 | } else {
103 | body = widget.body;
104 | }
105 |
106 | return new Scaffold(
107 | appBar: flitterStore.state.search.searching == true
108 | ? _buildSearchBar()
109 | : _buildAppBar(),
110 | drawer: widget.drawer,
111 | body: body);
112 | }
113 |
114 | void _handleSearchBegin() {
115 | ModalRoute.of(context).addLocalHistoryEntry(new LocalHistoryEntry(
116 | onRemove: () {
117 | flitterStore.dispatch(new EndSearchAction());
118 | },
119 | ));
120 | flitterStore.dispatch(new ShowSearchBarAction());
121 | }
122 |
123 | void _handleSearchEnd() {
124 | Navigator.pop(context);
125 | }
126 |
127 | _handleSearchChange(String query) async {
128 | if (query.length > 3) {
129 | search(query);
130 | }
131 | }
132 |
133 | _buildSearchResult() =>
134 | new ListSearchResult(flitterStore.state.search.result);
135 |
136 | Widget _buildAppBar() => new AppBar(title: new Text(widget.title), actions: [
137 | new IconButton(
138 | icon: new Icon(Icons.search), onPressed: _handleSearchBegin)
139 | ]);
140 |
141 | Widget _buildSearchBar() {
142 | return SearchBar.buildSearchBar(context, 'Search', //todo: intl
143 | onSearchEnd: _handleSearchEnd,
144 | onChange: _handleSearchChange);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/lib/widgets/common/utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | void navigateTo(BuildContext context, Widget widget,
4 | {String path: '', bool replace: false}) {
5 | final builder = new PageRouteBuilder(
6 | settings: path.isNotEmpty ? new RouteSettings(name: path) : null,
7 | pageBuilder: (_, __, ___) {
8 | return widget;
9 | },
10 | );
11 | if (replace) {
12 | Navigator.of(context).pushReplacement(builder);
13 | } else {
14 | Navigator.of(context).push(builder);
15 | }
16 | }
17 |
18 | void materialNavigateTo(BuildContext context, Widget widget,
19 | {String path: '', bool replace: false}) {
20 | final route = new MaterialPageRoute(
21 | settings: path.isNotEmpty ? new RouteSettings(name: path) : null,
22 | builder: (BuildContext context) => widget,
23 | );
24 | if (replace) {
25 | Navigator.pushReplacement(context, route);
26 | } else {
27 | Navigator.push(context, route);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/widgets/routes/group.dart:
--------------------------------------------------------------------------------
1 | library flitter.routes.group_room;
2 |
3 | import 'package:flitter/widgets/common/drawer.dart';
4 | import 'package:flitter/widgets/common/list_room.dart';
5 | import 'package:flitter/widgets/common/utils.dart';
6 | import 'package:flitter/widgets/routes/settings.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flitter/redux/flitter_app_state.dart';
9 | import 'package:flitter/redux/store.dart';
10 | import 'package:flitter/services/flitter_request.dart';
11 | import 'package:gitter/gitter.dart';
12 | import 'package:flitter/widgets/routes/home.dart';
13 | import 'package:flitter/widgets/routes/people.dart';
14 | import 'package:flitter/app.dart';
15 |
16 | class GroupView extends StatefulWidget {
17 | static const path = "/group";
18 |
19 | static go(BuildContext context, Group group, {bool replace: true}) {
20 | fetchRoomsOfGroup();
21 | materialNavigateTo(context, new GroupView(),
22 | path: GroupView.path, replace: replace);
23 | }
24 |
25 | @override
26 | _GroupRoomViewState createState() => new _GroupRoomViewState();
27 | }
28 |
29 | class _GroupRoomViewState extends State {
30 | var _subscription;
31 |
32 | CurrentGroupState get groupState => flitterStore.state.selectedGroup;
33 |
34 | @override
35 | void initState() {
36 | super.initState();
37 | _subscription = flitterStore.onChange.listen((_) {
38 | setState(() {});
39 | });
40 | }
41 |
42 | @override
43 | void dispose() {
44 | super.dispose();
45 | _subscription.cancel();
46 | }
47 |
48 | @override
49 | Widget build(BuildContext context) {
50 | var body;
51 |
52 | if (groupState?.rooms != null) {
53 | body = new ListView(
54 | children: groupState.rooms
55 | .map((room) => new RoomTile(room: room))
56 | .toList());
57 | } else {
58 | body = new LoadingView();
59 | }
60 |
61 | return new Scaffold(
62 | appBar: new AppBar(title: new Text(groupState?.group?.name ?? "")),
63 | body: body,
64 | drawer: new FlitterDrawer(onTapAllConversation: () {
65 | HomeView.go(context);
66 | }, onTapPeoples: () {
67 | PeopleView.go(context);
68 | }, onTapSettings: () {
69 | SettingsView.go(context);
70 | }),
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib/widgets/routes/home.dart:
--------------------------------------------------------------------------------
1 | library flitter.routes.home;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:flitter/redux/store.dart';
6 | import 'package:flitter/services/flitter_request.dart';
7 | import 'package:flitter/widgets/common/drawer.dart';
8 | import 'package:flitter/widgets/common/list_room.dart';
9 | import 'package:flitter/widgets/common/search.dart';
10 | import 'package:flitter/widgets/common/utils.dart';
11 | import 'package:flitter/widgets/routes/people.dart';
12 | import 'package:flitter/widgets/routes/settings.dart';
13 | import 'package:flutter/material.dart';
14 | import 'package:flitter/app.dart';
15 | import 'package:flitter/intl/messages_all.dart' as intl;
16 |
17 | class HomeView extends StatefulWidget {
18 | static final String path = "/";
19 | final RefreshCallback onRefresh;
20 |
21 | static void go(BuildContext context, {bool replace: true}) {
22 | fetchRooms();
23 | materialNavigateTo(context, new HomeView(),
24 | path: HomeView.path, replace: replace);
25 | }
26 |
27 | HomeView({this.onRefresh});
28 |
29 | @override
30 | _HomeViewState createState() => new _HomeViewState();
31 | }
32 |
33 | class _HomeViewState extends State {
34 | StreamSubscription _subscription;
35 |
36 | @override
37 | void initState() {
38 | super.initState();
39 | _subscription = flitterStore.onChange.listen((_) {
40 | setState(() {});
41 | });
42 | }
43 |
44 | @override
45 | void dispose() {
46 | super.dispose();
47 | _subscription.cancel();
48 | }
49 |
50 | @override
51 | Widget build(BuildContext context) {
52 | var body;
53 |
54 | final drawer = new FlitterDrawer(onTapAllConversation: () {
55 | Navigator.pop(context);
56 | }, onTapPeoples: () {
57 | PeopleView.go(context);
58 | }, onTapSettings: () {
59 | SettingsView.go(context);
60 | });
61 |
62 | if (flitterStore.state.rooms != null) {
63 | body = new ListRoom(
64 | rooms: flitterStore.state.rooms,
65 | onRefresh: () {
66 | if (widget.onRefresh != null) {
67 | return widget.onRefresh();
68 | }
69 | return fetchRooms();
70 | });
71 | } else {
72 | body = new LoadingView();
73 | }
74 |
75 | return new ScaffoldWithSearchbar(
76 | body: body, title: intl.allConversations(), drawer: drawer);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/widgets/routes/login.dart:
--------------------------------------------------------------------------------
1 | library flitter.routes.login;
2 |
3 | import 'package:flitter/redux/store.dart';
4 | import 'package:flitter/services/flitter_auth.dart';
5 | import 'package:flitter/services/flitter_request.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter/services.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 | import 'package:gitter/gitter.dart';
10 |
11 | typedef void OnLoginCallback(GitterToken token);
12 |
13 | class LoginView extends StatefulWidget {
14 | @override
15 | _LoginViewState createState() => new _LoginViewState();
16 | }
17 |
18 | class _LoginViewState extends State {
19 | var _subscription;
20 |
21 | // FIXME: final FlutterWebviewPlugin flutterWebviewPlugin = new FlutterWebviewPlugin();
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | _subscription = gitterStore.onChange.listen((_) {
27 | setState(() {});
28 | });
29 | }
30 |
31 | @override
32 | void dispose() {
33 | super.dispose();
34 | _subscription.cancel();
35 | }
36 |
37 | Future _auth() async {
38 | GitterToken token = await FlitterAuth.auth();
39 | initStores(token);
40 | }
41 |
42 | Future _catchOnBackPressed() async {
43 | // catch onBackPressed for Android
44 | // FIXME: await flutterWebviewPlugin.onBackPressed.first;
45 | return SystemNavigator.pop();
46 | }
47 |
48 | @override
49 | Widget build(BuildContext context) {
50 | if (gitterToken == null) {
51 | _auth();
52 |
53 | // FIXME: _catchOnBackPressed();
54 | }
55 | return new MaterialApp(
56 | home: new Scaffold(
57 | body: new Center(child: new CircularProgressIndicator())),
58 | theme: themeStore.state.theme);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/widgets/routes/people.dart:
--------------------------------------------------------------------------------
1 | library flitter.routes.people;
2 |
3 | import 'package:flitter/widgets/common/list_room.dart';
4 | import 'package:flitter/widgets/common/search.dart';
5 | import 'package:flitter/widgets/routes/settings.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flitter/redux/store.dart';
8 | import 'package:flitter/services/flitter_request.dart';
9 | import 'package:gitter/gitter.dart';
10 | import 'package:flitter/widgets/common/drawer.dart';
11 | import 'package:flitter/widgets/common/utils.dart';
12 | import 'package:flitter/widgets/routes/home.dart';
13 | import 'package:flitter/intl/messages_all.dart' as intl;
14 | import 'package:flitter/app.dart';
15 |
16 | class PeopleView extends StatefulWidget {
17 | static const String path = "/people";
18 | final RefreshCallback onRefresh;
19 |
20 | static void go(BuildContext context, {bool replace: true}) {
21 | fetchRooms();
22 | materialNavigateTo(context, new PeopleView(),
23 | path: PeopleView.path, replace: replace);
24 | }
25 |
26 | PeopleView({this.onRefresh});
27 |
28 | @override
29 | _PeopleViewState createState() => new _PeopleViewState();
30 | }
31 |
32 | class _PeopleViewState extends State {
33 | var _subscription;
34 |
35 | @override
36 | void initState() {
37 | super.initState();
38 | _subscription = flitterStore.onChange.listen((_) {
39 | setState(() {});
40 | });
41 | }
42 |
43 | @override
44 | void dispose() {
45 | super.dispose();
46 | _subscription.cancel();
47 | }
48 |
49 | @override
50 | Widget build(BuildContext context) {
51 | var body;
52 |
53 | final drawer = new FlitterDrawer(onTapAllConversation: () {
54 | HomeView.go(context);
55 | }, onTapPeoples: () {
56 | Navigator.pop(context);
57 | }, onTapSettings: () {
58 | SettingsView.go(context);
59 | });
60 |
61 | if (flitterStore.state.rooms != null) {
62 | body = new ListRoom(
63 | rooms: flitterStore.state.rooms
64 | .where((Room room) => room.oneToOne)
65 | .toList(),
66 | onRefresh: () {
67 | if (widget.onRefresh != null) {
68 | return widget.onRefresh();
69 | }
70 | return fetchRooms();
71 | });
72 | } else {
73 | body = new LoadingView();
74 | }
75 |
76 | return new ScaffoldWithSearchbar(
77 | body: body, title: intl.people(), drawer: drawer);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/widgets/routes/room.dart:
--------------------------------------------------------------------------------
1 | library flitter.routes.room;
2 |
3 | import 'dart:async';
4 | import 'package:flitter/redux/actions.dart';
5 | import 'package:flitter/redux/store.dart';
6 | import 'package:flitter/services/flitter_request.dart';
7 | import 'package:gitter/gitter.dart';
8 | import 'package:flitter/widgets/common/chat_room.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:flitter/app.dart';
11 | import 'package:gitter/src/models/faye_message.dart';
12 |
13 | enum RoomMenuAction { leave }
14 |
15 | class RoomView extends StatefulWidget {
16 | static const path = "/room";
17 |
18 | RoomView();
19 |
20 | @override
21 | _RoomViewState createState() => new _RoomViewState();
22 | }
23 |
24 | class _RoomViewState extends State {
25 | Iterable get messages => flitterStore.state.selectedRoom.messages;
26 |
27 | Room get room => flitterStore.state.selectedRoom.room;
28 |
29 | var _subscription;
30 |
31 | @override
32 | void initState() {
33 | super.initState();
34 | _subscription = flitterStore.onChange.listen((_) {
35 | setState(() {});
36 | });
37 |
38 | _fetchMessages();
39 |
40 | gitterSubscriber.subscribeToChatMessages(room.id, _onMessageHandler);
41 | }
42 |
43 | _onMessageHandler(List msgs) {
44 | for (GitterFayeMessage msg in msgs) {
45 | if (msg.data != null && msg.data["operation"] == "create") {
46 | flitterStore.dispatch(new OnMessageForCurrentRoom(
47 | new Message.fromJson(msg.data["model"])));
48 | }
49 | }
50 | }
51 |
52 | @override
53 | void dispose() {
54 | super.dispose();
55 | _subscription.cancel();
56 | gitterSubscriber.unsubscribeToChatMessages(room.id);
57 | }
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | var body;
62 |
63 | if (messages != null) {
64 | final ChatRoom chatRoom =
65 | new ChatRoom(messages: messages.toList().reversed, room: room);
66 | chatRoom.onNeedDataStream.listen((_) => _fetchMessages());
67 | body = chatRoom;
68 | } else {
69 | body = new LoadingView();
70 | }
71 |
72 | return new Scaffold(
73 | appBar: new AppBar(title: new Text(room.name), actions: [_buildMenu()]),
74 | body: body,
75 | floatingActionButton:
76 | _userHasJoined || messages == null ? null : _joinRoomButton());
77 | }
78 |
79 | _fetchMessages() {
80 | String id = messages?.isNotEmpty == true ? messages.first.id : null;
81 | fetchMessagesOfRoom(room.id, id);
82 | }
83 |
84 | Widget _buildMenu() => new PopupMenuButton(
85 | itemBuilder: (BuildContext context) => >[
86 | new PopupMenuItem(
87 | value: RoomMenuAction.leave,
88 | child: new Text('Leave room')) //todo: intl
89 | ],
90 | onSelected: (RoomMenuAction action) {
91 | switch (action) {
92 | case RoomMenuAction.leave:
93 | _onLeaveRoom();
94 | break;
95 | }
96 | });
97 |
98 | _onLeaveRoom() async {
99 | bool success = await leaveRoom(room);
100 | if (success == true) {
101 | Navigator.of(context).pop();
102 | } else {
103 | // Todo: show error
104 | }
105 | }
106 |
107 | Widget _joinRoomButton() {
108 | return new FloatingActionButton(
109 | child: new Icon(Icons.message), onPressed: _onTapJoinRoom);
110 | }
111 |
112 | void _onTapJoinRoom() {
113 | joinRoom(room);
114 | }
115 |
116 | bool get _userHasJoined =>
117 | flitterStore.state.rooms.any((Room r) => r.id == room.id);
118 | }
119 |
--------------------------------------------------------------------------------
/lib/widgets/routes/settings.dart:
--------------------------------------------------------------------------------
1 | library flitter.routes.settings;
2 |
3 | import 'package:flutter_color_picker/flutter_color_picker.dart';
4 | import 'package:flitter/redux/actions.dart';
5 | import 'package:flitter/redux/flitter_app_state.dart';
6 | import 'package:flitter/widgets/routes/people.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flitter/redux/store.dart';
9 | import 'package:flitter/services/flitter_request.dart';
10 | import 'package:flitter/widgets/common/drawer.dart';
11 | import 'package:flitter/widgets/common/utils.dart';
12 | import 'package:flitter/widgets/routes/home.dart';
13 | import 'package:flitter/intl/messages_all.dart' as intl;
14 | import 'package:shared_preferences/shared_preferences.dart';
15 |
16 | class SettingsView extends StatefulWidget {
17 | static const String path = "/settings";
18 |
19 | static void go(BuildContext context, {bool replace: true}) {
20 | fetchRooms();
21 | materialNavigateTo(context, new SettingsView(),
22 | path: SettingsView.path, replace: replace);
23 | }
24 |
25 | @override
26 | _SettingsViewState createState() => new _SettingsViewState();
27 | }
28 |
29 | class _SettingsViewState extends State {
30 | var _themeSubscription;
31 |
32 | @override
33 | void initState() {
34 | super.initState();
35 | _themeSubscription = themeStore.onChange.listen((_) {
36 | setState(() {});
37 | });
38 | }
39 |
40 | @override
41 | void dispose() {
42 | super.dispose();
43 | _themeSubscription.cancel();
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | final drawer = new FlitterDrawer(onTapAllConversation: () {
49 | HomeView.go(context);
50 | }, onTapPeoples: () {
51 | PeopleView.go(context);
52 | }, onTapSettings: () {
53 | Navigator.pop(context);
54 | });
55 |
56 | return new Scaffold(
57 | body: new ListView(children: [
58 | _buildDarkModeSwitch(),
59 | _buildPrimaryColor(),
60 | _buildAccentColor(),
61 | new Divider()
62 | ]),
63 | appBar: new AppBar(title: new Text(intl.settings())),
64 | drawer: drawer);
65 | }
66 |
67 | Widget _buildDarkModeSwitch() {
68 | final brightness = themeStore.state.theme.brightness != Brightness.light;
69 |
70 | return new ListTile(
71 | title: new Row(children: [
72 | new Expanded(child: new Text(intl.darkMode())),
73 | new Switch(
74 | value: brightness,
75 | onChanged: (bool value) async {
76 | if (value != brightness) {
77 | themeStore.dispatch(new ChangeThemeAction(
78 | brightness:
79 | value == true ? Brightness.dark : Brightness.light));
80 | persistBrightness(value);
81 | }
82 | })
83 | ]));
84 | }
85 |
86 | Widget _buildPrimaryColor() {
87 | return _buildColorTile(intl.primaryColor(), themeStore.state.primaryColor,
88 | () async {
89 | Color color = await showDialog(
90 | context: context,
91 | child: new PrimaryColorPickerDialog(
92 | selected: themeStore.state.primaryColor));
93 | if (color != null) {
94 | themeStore.dispatch(new ChangeThemeAction(primaryColor: color));
95 | persistAccentColor(color);
96 | }
97 | });
98 | }
99 |
100 | Widget _buildAccentColor() {
101 | return _buildColorTile(intl.accentColor(), themeStore.state.accentColor,
102 | () async {
103 | Color color = await showDialog(
104 | context: context,
105 | child: new AccentColorPickerDialog(
106 | selected: themeStore.state.accentColor));
107 | if (color != null) {
108 | themeStore.dispatch(new ChangeThemeAction(accentColor: color));
109 | persistAccentColor(color);
110 | }
111 | });
112 | }
113 |
114 | persistBrightness(bool value) async {
115 | SharedPreferences prefs = await SharedPreferences.getInstance();
116 | prefs.setBool(ThemeState.kBrightnessKey, value);
117 | await prefs.commit();
118 | }
119 |
120 | persistPrimaryColor(ColorSwatch color) async {
121 | SharedPreferences prefs = await SharedPreferences.getInstance();
122 | prefs.setInt(ThemeState.kPrimaryColorKey, Colors.primaries.indexOf(color));
123 | await prefs.commit();
124 | }
125 |
126 | persistAccentColor(ColorSwatch color) async {
127 | SharedPreferences prefs = await SharedPreferences.getInstance();
128 | prefs.setInt(ThemeState.kAccentColorKey, Colors.accents.indexOf(color));
129 | await prefs.commit();
130 | }
131 |
132 | Widget _buildColorTile(String text, ColorSwatch color, VoidCallback onTap) {
133 | return new ListTile(
134 | title: new Row(children: [
135 | new Expanded(child: new Text(text)),
136 | new Padding(
137 | padding: new EdgeInsets.only(right: 14.0),
138 | child: new ColorTile(color: color, size: 40.0, rounded: true)),
139 | ]),
140 | onTap: onTap);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flitter
2 | description: A new flutter project.
3 | version: 0.0.1
4 |
5 | dependencies:
6 | flutter:
7 | sdk: flutter
8 | intl: ^0.14.0
9 | http: ^0.11.3+13
10 | flutter_webview_plugin: ">=0.0.5 <=0.1.2"
11 | redux: ^2.1.1
12 | shared_preferences: ">=0.2.0 <1.0.0"
13 | url_launcher: ">=0.4.0 <=2.0.2"
14 | gitter: ^0.1.0
15 | flutter_markdown: "^0.0.9"
16 | flutter_color_picker: "^0.0.1"
17 |
18 | dev_dependencies:
19 | flutter_test:
20 | sdk: flutter
21 |
22 | flutter:
23 | uses-material-design: true
24 | assets:
25 | - assets/html/success.html
26 | - assets/images/banner.jpg
27 |
--------------------------------------------------------------------------------
/screenshots/flutter_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_01.png
--------------------------------------------------------------------------------
/screenshots/flutter_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_02.png
--------------------------------------------------------------------------------
/screenshots/flutter_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_03.png
--------------------------------------------------------------------------------
/screenshots/flutter_04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_04.png
--------------------------------------------------------------------------------
/screenshots/flutter_05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_05.png
--------------------------------------------------------------------------------
/screenshots/flutter_06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_06.png
--------------------------------------------------------------------------------
/screenshots/flutter_07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-flitter/flitter/369e337768566f3d0b2c947fac23bf7900b24cf5/screenshots/flutter_07.png
--------------------------------------------------------------------------------
/test/utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flitter/redux/actions.dart';
4 | import 'package:flitter/redux/flitter_app_state.dart';
5 | import 'package:flitter/redux/store.dart';
6 | import 'package:gitter/gitter.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:gitter/src/faye.dart';
9 | import 'package:http/http.dart' as http;
10 | import 'package:http/testing.dart' as http;
11 |
12 | class MockableApp extends StatelessWidget {
13 | final Widget drawer;
14 | final Widget body;
15 | final AppBar appBar;
16 | final Widget scaffold;
17 |
18 | MockableApp({this.drawer, this.body, this.appBar, this.scaffold});
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | if (scaffold != null) {
23 | return new MaterialApp(home: scaffold);
24 | }
25 | return new MaterialApp(
26 | home: new Scaffold(drawer: drawer, body: body, appBar: appBar));
27 | }
28 | }
29 |
30 | initStores() {
31 | final token = new GitterToken()
32 | ..access = "xxx"
33 | ..type = "xxx";
34 | final api = new GitterApi(token);
35 | gitterStore = new GitterStore(
36 | initialState: new GitterState(
37 | api: api,
38 | token: token,
39 | subscriber: new GitterFayeSubscriber(token.access)));
40 |
41 | flitterStore = new FlitterStore(
42 | initialState: new FlitterAppState(search: new SearchState.initial()),
43 | middlewares: const []);
44 | }
45 |
46 | fetchUser() {
47 | final user = new User.fromJson({
48 | "id": "53307734c3599d1de448e192",
49 | "username": "malditogeek",
50 | "displayName": "Mauro Pompilio",
51 | "url": "/malditogeek",
52 | "avatarUrlSmall": "https://avatars.githubusercontent.com/u/14751?",
53 | "avatarUrlMedium": "https://avatars.githubusercontent.com/u/14751?"
54 | });
55 | flitterStore.dispatch(new FetchUser(user));
56 | }
57 |
58 | final groups = [
59 | new Group.fromJson({
60 | "id": "57542c12c43b8c601976fa66",
61 | "name": "gitterHQ",
62 | "uri": "gitterHQ",
63 | "backedBy": {"type": "GH_ORG", "linkPath": "gitterHQ"},
64 | "avatarUrl":
65 | "http://gitter.im/api/private/avatars/group/i/577ef7e4e897e2a459b1b881"
66 | }),
67 | new Group.fromJson({
68 | "id": "577faf61a7d5727908337209",
69 | "name": "i-love-cats",
70 | "uri": "i-love-cats",
71 | "backedBy": {"type": null},
72 | "avatarUrl":
73 | "http://gitter.im/api/private/avatars/group/i/577faf61a7d5727908337209"
74 | })
75 | ];
76 |
77 | Iterable fetchCommunities() {
78 | flitterStore.dispatch(new FetchGroupsAction(groups));
79 | return groups;
80 | }
81 |
82 | final rooms = [
83 | new Room.fromJson({
84 | "id": "53307860c3599d1de448e19d",
85 | "name": "Andrew Newdigate",
86 | "topic": "",
87 | "oneToOne": true,
88 | "user": {
89 | "id": "53307831c3599d1de448e19a",
90 | "username": "suprememoocow",
91 | "displayName": "Andrew Newdigate",
92 | "url": "/suprememoocow",
93 | "avatarUrlSmall": "https://avatars.githubusercontent.com/u/594566?",
94 | "avatarUrlMedium": "https://avatars.githubusercontent.com/u/594566?"
95 | },
96 | "unreadItems": 0,
97 | "mentions": 0,
98 | "lurk": false,
99 | "url": "/suprememoocow",
100 | "githubType": "ONETOONE"
101 | }),
102 | new Room.fromJson({
103 | "id": "5330777dc3599d1de448e194",
104 | "name": "gitterHQ",
105 | "topic": "Gitter",
106 | "uri": "gitterHQ",
107 | "oneToOne": false,
108 | "userCount": 2,
109 | "unreadItems": 0,
110 | "mentions": 0,
111 | "lastAccessTime": "2014-03-24T18:22:28.105Z",
112 | "lurk": false,
113 | "url": "/gitterHQ",
114 | "githubType": "ORG",
115 | "v": 1
116 | }),
117 | new Room.fromJson({
118 | "id": "5330780dc3599d1de448e198",
119 | "name": "gitterHQ/devops",
120 | "topic": "",
121 | "uri": "gitterHQ/devops",
122 | "oneToOne": false,
123 | "userCount": 2,
124 | "unreadItems": 0,
125 | "mentions": 0,
126 | "lastAccessTime": "2014-03-24T18:23:10.512Z",
127 | "lurk": false,
128 | "url": "/gitterHQ/devops",
129 | "githubType": "ORG_CHANNEL",
130 | "security": "INHERITED",
131 | "v": 1
132 | }),
133 | new Room.fromJson({
134 | "id": "53307793c3599d1de448e196",
135 | "name": "malditogeek/vmux",
136 | "topic": "VMUX - Plugin-free video calls in your browser using WebRTC",
137 | "uri": "malditogeek/vmux",
138 | "oneToOne": false,
139 | "userCount": 2,
140 | "unreadItems": 0,
141 | "mentions": 0,
142 | "lastAccessTime": "2014-03-24T18:21:08.448Z",
143 | "favourite": 1,
144 | "lurk": false,
145 | "url": "/malditogeek/vmux",
146 | "githubType": "REPO",
147 | "tags": ["javascript", "nodejs"],
148 | "v": 1
149 | })
150 | ];
151 |
152 | Iterable fetchRooms() {
153 | flitterStore.dispatch(new FetchRoomsAction(rooms));
154 | return rooms;
155 | }
156 |
157 | Iterable fetchRoomsOfGroup() {
158 | flitterStore.dispatch(new SelectGroupAction(groups.first));
159 | flitterStore.dispatch(new FetchRoomsOfGroup(rooms));
160 | return rooms;
161 | }
162 |
163 | // Returns a mock HTTP client that responds with an image to all requests.
164 | // See: https://github.com/flutter/flutter/issues/13433
165 | ValueGetter createMockImageHttpClient = () {
166 | return new http.MockClient((http.BaseRequest request) {
167 | return new Future.value(
168 | new http.Response.bytes(_transparentImage, 200, request: request));
169 | });
170 | };
171 |
172 | const List _transparentImage = const [
173 | 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
174 | 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
175 | 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44,
176 | 0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D,
177 | 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
178 | ];
--------------------------------------------------------------------------------
/test/widgets/drawer_test.dart:
--------------------------------------------------------------------------------
1 | import '../utils.dart';
2 | import 'package:flitter/widgets/common/drawer.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_test/flutter_test.dart';
5 | import 'package:flitter/redux/store.dart';
6 | import 'package:flitter/intl/messages_all.dart' as intl;
7 | import 'package:flutter/services.dart' show createHttpClient;
8 |
9 | main() {
10 | group("$FlitterDrawer Widget", () {
11 | setUpAll(() {
12 | initStores();
13 | createHttpClient = createMockImageHttpClient;
14 | });
15 |
16 | testWidgets("No user", (WidgetTester tester) async {
17 | await tester.pumpWidget(new MockableApp(
18 | drawer: new FlitterDrawer(
19 | onTapAllConversation: () {},
20 | onTapPeoples: () {},
21 | onTapSettings: () {})));
22 |
23 | // open drawer
24 | final ScaffoldState state = tester.firstState(find.byType(Scaffold));
25 | state.openDrawer();
26 |
27 | await tester.pump();
28 |
29 | // drawer with a Loader
30 | final progressFinder = find.byType(CircularProgressIndicator);
31 | expect(progressFinder, findsOneWidget);
32 | });
33 |
34 | testWidgets("fetch user", (WidgetTester tester) async {
35 | await tester.pumpWidget(new MockableApp(
36 | drawer: new FlitterDrawer(
37 | onTapAllConversation: () {},
38 | onTapPeoples: () {},
39 | onTapSettings: () {})));
40 |
41 | // open drawer
42 | final ScaffoldState state = tester.firstState(find.byType(Scaffold));
43 | state.openDrawer();
44 |
45 | fetchUser();
46 |
47 | await tester.pump();
48 |
49 | // drawer with user
50 | final drawerFinder = find.byType(FlitterDrawerContent);
51 | expect(drawerFinder, findsOneWidget);
52 | });
53 |
54 | testWidgets("Header", (WidgetTester tester) async {
55 | await tester.pumpWidget(new MockableApp(
56 | drawer: new FlitterDrawer(
57 | onTapAllConversation: () {},
58 | onTapPeoples: () {},
59 | onTapSettings: () {})));
60 |
61 | // open drawer
62 | final ScaffoldState state = tester.firstState(find.byType(Scaffold));
63 | state.openDrawer();
64 |
65 | fetchUser();
66 |
67 | await tester.pump();
68 |
69 | // drawer with header
70 | final userAccountHeaderFinder = find.byType(UserAccountsDrawerHeader);
71 | expect(userAccountHeaderFinder, findsOneWidget);
72 | final userAccountHeader = tester.firstWidget(userAccountHeaderFinder)
73 | as UserAccountsDrawerHeader;
74 | expect((userAccountHeader.accountName as Text).data,
75 | equals(flitterStore.state.user.username));
76 | expect((userAccountHeader.accountEmail as Text).data,
77 | equals(flitterStore.state.user.displayName));
78 | });
79 |
80 | testWidgets("Footer", (WidgetTester tester) async {
81 | await tester.pumpWidget(new MockableApp(
82 | drawer: new FlitterDrawer(
83 | onTapAllConversation: () {},
84 | onTapPeoples: () {},
85 | onTapSettings: () {})));
86 |
87 | // open drawer
88 | final ScaffoldState state = tester.firstState(find.byType(Scaffold));
89 | state.openDrawer();
90 |
91 | fetchUser();
92 |
93 | await tester.pump();
94 |
95 | // drawer with footer
96 | final drawerFooterFinder = find.byType(FlitterDrawerFooter);
97 | expect(drawerFooterFinder, findsOneWidget);
98 |
99 | final logoutButtonFinder = find.descendant(
100 | of: drawerFooterFinder, matching: find.byType(ListTile));
101 | expect(logoutButtonFinder, findsOneWidget);
102 |
103 | final logoutButton = tester.firstWidget(logoutButtonFinder) as ListTile;
104 | expect((logoutButton.leading as Icon).icon, equals(Icons.exit_to_app));
105 | expect((logoutButton.title as Text).data, equals(intl.logout()));
106 | expect(logoutButton.onTap, isNotNull);
107 | });
108 |
109 | testWidgets("Communities", (WidgetTester tester) async {
110 | await tester.pumpWidget(new MockableApp(
111 | drawer: new FlitterDrawer(
112 | onTapAllConversation: () {},
113 | onTapPeoples: () {},
114 | onTapSettings: () {})));
115 |
116 | // open drawer
117 | final ScaffoldState state = tester.firstState(find.byType(Scaffold));
118 | state.openDrawer();
119 |
120 | fetchUser();
121 |
122 | await tester.pump();
123 |
124 | final drawerCommunitiesFinder = find.byType(FlitterDrawerCommunityTile);
125 |
126 | // drawer without Communities
127 | expect(drawerCommunitiesFinder, findsNothing);
128 |
129 | fetchCommunities();
130 |
131 | await tester.pump();
132 |
133 | // drawer with Communities footer
134 | expect(drawerCommunitiesFinder, findsNWidgets(2));
135 |
136 | final tiles = tester.widgetList(drawerCommunitiesFinder)
137 | as Iterable;
138 | for (var tile in tiles) {
139 | expect(flitterStore.state.groups, contains(tile.group));
140 | }
141 | });
142 |
143 | testWidgets("Tap logout", (WidgetTester tester) async {
144 | await tester.pumpWidget(new MockableApp(
145 | drawer: new FlitterDrawer(
146 | onTapAllConversation: () {},
147 | onTapPeoples: () {},
148 | onTapSettings: () {})));
149 |
150 | // open drawer
151 | final ScaffoldState state = tester.firstState(find.byType(Scaffold));
152 | state.openDrawer();
153 |
154 | fetchUser();
155 |
156 | await tester.pump();
157 |
158 | // drawer with footer
159 | final drawerFooterFinder = find.byType(FlitterDrawerFooter);
160 | final logoutButtonFinder = find.descendant(
161 | of: drawerFooterFinder, matching: find.byType(ListTile));
162 |
163 | expect(logoutButtonFinder, findsOneWidget);
164 |
165 | //await tester.tap(logoutButtonFinder);
166 | final logoutButton = tester.firstWidget(logoutButtonFinder) as ListTile;
167 | logoutButton.onTap();
168 |
169 | await tester.pump();
170 | });
171 | });
172 | }
173 |
--------------------------------------------------------------------------------
/test/widgets/group_test.dart:
--------------------------------------------------------------------------------
1 | import '../utils.dart';
2 | import 'package:flitter/app.dart';
3 | import 'package:flitter/widgets/common/list_room.dart';
4 | import 'package:flitter/widgets/routes/group.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_test/flutter_test.dart';
7 |
8 | main() {
9 | group("$GroupView Widget", () {
10 | setUpAll(initStores);
11 |
12 | testWidgets("No Rooms", (WidgetTester tester) async {
13 | await tester.pumpWidget(new MockableApp(scaffold: new GroupView()));
14 |
15 | final scaff = find.byType(Scaffold);
16 |
17 | expect(scaff, findsOneWidget);
18 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
19 | findsOneWidget);
20 | });
21 |
22 | testWidgets("Fetching Rooms", (WidgetTester tester) async {
23 | await tester.pumpWidget(new MockableApp(scaffold: new GroupView()));
24 |
25 | final scaff = find.byType(Scaffold);
26 |
27 | fetchRoomsOfGroup();
28 |
29 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
30 | findsOneWidget);
31 |
32 | await tester.pump();
33 |
34 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
35 | findsNothing);
36 | expect(find.descendant(of: scaff, matching: find.byType(ListView)),
37 | findsOneWidget);
38 | expect(find.descendant(of: scaff, matching: find.byType(RoomTile)),
39 | findsNWidgets(4));
40 | });
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/test/widgets/home_test.dart:
--------------------------------------------------------------------------------
1 | import '../utils.dart';
2 | import 'package:flitter/app.dart';
3 | import 'package:flitter/redux/store.dart';
4 | import 'package:flitter/widgets/common/list_room.dart';
5 | import 'package:flitter/widgets/common/search.dart';
6 | import 'package:flitter/widgets/routes/home.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | main() {
11 | group("$HomeView Widget", () {
12 | setUpAll(initStores);
13 |
14 | testWidgets("No Rooms", (WidgetTester tester) async {
15 | await tester.pumpWidget(new MockableApp(scaffold: new HomeView()));
16 |
17 | final scaff = find.byType(ScaffoldWithSearchbar);
18 |
19 | expect(scaff, findsOneWidget);
20 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
21 | findsOneWidget);
22 | });
23 |
24 | testWidgets("Fetching Rooms", (WidgetTester tester) async {
25 | await tester.pumpWidget(new MockableApp(scaffold: new HomeView()));
26 |
27 | final scaff = find.byType(ScaffoldWithSearchbar);
28 |
29 | fetchRooms();
30 |
31 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
32 | findsOneWidget);
33 |
34 | await tester.pump();
35 |
36 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
37 | findsNothing);
38 | expect(find.descendant(of: scaff, matching: find.byType(ListRoom)),
39 | findsOneWidget);
40 | expect(find.descendant(of: scaff, matching: find.byType(RoomTile)),
41 | findsNWidgets(4));
42 | });
43 |
44 | testWidgets("Pull to Refresh", (WidgetTester tester) async {
45 | flitterStore =
46 | new FlitterStore(initialState: flitterStore.state.apply(rooms: []));
47 |
48 | await tester.pumpWidget(
49 | new MockableApp(scaffold: new HomeView(onRefresh: () async {
50 | fetchRooms();
51 | })));
52 |
53 | final scaff = find.byType(ScaffoldWithSearchbar);
54 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
55 | findsNothing);
56 | expect(find.descendant(of: scaff, matching: find.byType(ListRoom)),
57 | findsOneWidget);
58 | expect(find.descendant(of: scaff, matching: find.byType(RoomTile)),
59 | findsNothing);
60 |
61 | await tester.flingFrom(
62 | const Offset(50.0, 300.0), const Offset(0.0, 300.0), 1000.0);
63 | await tester.pump();
64 | await tester.pump(const Duration(seconds: 1));
65 | await tester.pump(const Duration(seconds: 1));
66 | await tester.pump(const Duration(seconds: 1));
67 |
68 | expect(find.byType(RoomTile), findsNWidgets(4));
69 | });
70 | });
71 | }
72 |
--------------------------------------------------------------------------------
/test/widgets/people_test.dart:
--------------------------------------------------------------------------------
1 | import '../utils.dart';
2 | import 'package:flitter/app.dart';
3 | import 'package:flitter/redux/store.dart';
4 | import 'package:flitter/widgets/common/list_room.dart';
5 | import 'package:flitter/widgets/common/search.dart';
6 | import 'package:flitter/widgets/routes/people.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | main() {
11 | group("$PeopleView Widget", () {
12 | setUpAll(initStores);
13 |
14 | testWidgets("No Rooms", (WidgetTester tester) async {
15 | await tester.pumpWidget(new MockableApp(scaffold: new PeopleView()));
16 |
17 | final scaff = find.byType(ScaffoldWithSearchbar);
18 |
19 | expect(scaff, findsOneWidget);
20 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
21 | findsOneWidget);
22 | });
23 |
24 | testWidgets("Fetching Rooms", (WidgetTester tester) async {
25 | await tester.pumpWidget(new MockableApp(scaffold: new PeopleView()));
26 |
27 | final scaff = find.byType(ScaffoldWithSearchbar);
28 |
29 | fetchRooms();
30 |
31 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
32 | findsOneWidget);
33 |
34 | await tester.pump();
35 |
36 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
37 | findsNothing);
38 | expect(find.descendant(of: scaff, matching: find.byType(ListRoom)),
39 | findsOneWidget);
40 | expect(find.descendant(of: scaff, matching: find.byType(RoomTile)),
41 | findsNWidgets(1));
42 | });
43 |
44 | testWidgets("Pull to Refresh", (WidgetTester tester) async {
45 | flitterStore =
46 | new FlitterStore(initialState: flitterStore.state.apply(rooms: []));
47 |
48 | await tester.pumpWidget(
49 | new MockableApp(scaffold: new PeopleView(onRefresh: () async {
50 | fetchRooms();
51 | })));
52 |
53 | final scaff = find.byType(ScaffoldWithSearchbar);
54 | expect(find.descendant(of: scaff, matching: find.byType(LoadingView)),
55 | findsNothing);
56 | expect(find.descendant(of: scaff, matching: find.byType(ListRoom)),
57 | findsOneWidget);
58 | expect(find.descendant(of: scaff, matching: find.byType(RoomTile)),
59 | findsNothing);
60 |
61 | await tester.flingFrom(
62 | const Offset(50.0, 300.0), const Offset(0.0, 300.0), 1000.0);
63 | await tester.pump();
64 | await tester.pump(const Duration(seconds: 1));
65 | await tester.pump(const Duration(seconds: 1));
66 | await tester.pump(const Duration(seconds: 1));
67 |
68 | expect(find.byType(RoomTile), findsNWidgets(1));
69 | });
70 | });
71 | }
72 |
--------------------------------------------------------------------------------
/test/widgets/splash_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flitter/app.dart';
2 | import 'package:flitter/redux/flitter_app_state.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_test/flutter_test.dart';
5 | import 'package:flitter/redux/store.dart';
6 |
7 | main() {
8 | group("$Splash Widget", () {
9 | testWidgets("Inside Scaffold", (WidgetTester tester) async {
10 | await tester.pumpWidget(new Splash());
11 |
12 | // MaterialApp exist
13 | final appFinder = find.byType(MaterialApp);
14 | expect(appFinder, findsOneWidget);
15 |
16 | // Scaffold exist
17 | final scaffoldFinder =
18 | find.descendant(of: appFinder, matching: find.byType(Scaffold));
19 | expect(scaffoldFinder, findsOneWidget);
20 |
21 | // Text is correct
22 | final titleFinder =
23 | find.descendant(of: scaffoldFinder, matching: find.byType(Text));
24 | expect(titleFinder, findsNWidgets(2));
25 | final title = tester.firstWidget(titleFinder) as Text;
26 | expect(title.data, equals(appName));
27 | });
28 |
29 | testWidgets("Default Color", (WidgetTester tester) async {
30 | await tester.pumpWidget(new Splash());
31 |
32 | final logoFinder = find.byType(FlutterLogo);
33 | expect(logoFinder, findsOneWidget);
34 | final logo = tester.firstWidget(logoFinder) as FlutterLogo;
35 | expect(logo.colors, equals(Colors.pink));
36 | });
37 | });
38 | }
39 |
--------------------------------------------------------------------------------