├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── peerwaya │ └── flutteraccountkit │ ├── FlutterAccountKitPlugin.java │ ├── LoginResultDelegate.java │ └── LoginResults.java ├── coverage └── lcov.info ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── peerwaya │ │ │ │ └── flutteraccountkitexample │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── flutter_account_kit_example.iml ├── flutter_account_kit_example_android.iml ├── 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 │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── lib │ └── main.dart └── pubspec.yaml ├── flutter_account_kit.iml ├── flutter_account_kit_android.iml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── FlutterAccountKitPlugin.h │ ├── FlutterAccountKitPlugin.m │ ├── FlutterAccountKitViewController.h │ └── FlutterAccountKitViewController.m └── flutter_account_kit.podspec ├── lib ├── flutter_account_kit.dart ├── generated │ └── i18n.dart └── src │ ├── access_token.dart │ ├── account.dart │ ├── account_kit.dart │ ├── account_kit_theme.dart │ ├── config.dart │ ├── countries.dart │ ├── login_result.dart │ ├── login_status.dart │ ├── login_type.dart │ ├── phone_number.dart │ ├── response_type.dart │ ├── title_type.dart │ └── utils.dart ├── pubspec.yaml ├── res └── values │ └── strings_en.arb └── test ├── custom_matchers.dart └── flutter_account_kit_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .packages 5 | .dart_tool/ 6 | .pub/ 7 | build/ 8 | ios/.generated/ 9 | packages 10 | pubspec.lock 11 | .coveralls.yml 12 | 13 | # iOS/XCode related 14 | **/ios/**/*.mode1v3 15 | **/ios/**/*.mode2v3 16 | **/ios/**/*.moved-aside 17 | **/ios/**/*.pbxuser 18 | **/ios/**/*.perspectivev3 19 | **/ios/**/*sync/ 20 | **/ios/**/.sconsign.dblite 21 | **/ios/**/.tags* 22 | **/ios/**/.vagrant/ 23 | **/ios/**/DerivedData/ 24 | **/ios/**/Icon? 25 | **/ios/**/Pods/ 26 | **/ios/**/.symlinks/ 27 | **/ios/**/profile 28 | **/ios/**/xcuserdata 29 | **/ios/.generated/ 30 | **/ios/Flutter/App.framework 31 | **/ios/Flutter/Flutter.framework 32 | **/ios/Flutter/Generated.xcconfig 33 | **/ios/Flutter/app.flx 34 | **/ios/Flutter/app.zip 35 | **/ios/Flutter/flutter_assets/ 36 | **/ios/ServiceDefinitions.json 37 | **/ios/Runner/GeneratedPluginRegistrant.* 38 | 39 | # Exceptions to above rules. 40 | !**/ios/**/default.mode1v3 41 | !**/ios/**/default.mode2v3 42 | !**/ios/**/default.pbxuser 43 | !**/ios/**/default.perspectivev3 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | sudo: false 4 | addons: 5 | apt: 6 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18 7 | sources: 8 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version 9 | packages: 10 | - libstdc++6 11 | - fonts-droid 12 | before_script: 13 | - git clone https://github.com/flutter/flutter.git -b beta --depth 1 14 | - ./flutter/bin/flutter doctor 15 | script: 16 | - ./flutter/bin/flutter test 17 | cache: 18 | directories: 19 | - $HOME/.pub-cache -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.7.0 2 | * Update readme 3 | * Update dependencies to support whatsapp login 4 | ## 0.6.6 5 | * Refactor for androidx 6 | ## 0.6.5 7 | * Add static framework for swift based projects 8 | ## 0.6.4 9 | * Fix issue with country whitelisting 10 | * Fix authorization code truncation issue 11 | ## 0.6.3 12 | * Fix null issue with getCurrentAccount on ios 13 | 14 | ## 0.6.2 15 | * Fix issue with initial phone number for android and ios 16 | 17 | ## 0.6.1 18 | * Added input text color to accountkit theme constructor 19 | 20 | ## 0.6.0 21 | * Added button text color to accountkit theme 22 | * Fix issue with color mapping 23 | 24 | ## 0.5.4 25 | 26 | * Test fixes 27 | 28 | ## 0.5.3 29 | 30 | * Fixed issue with initial phone number config 31 | 32 | ## 0.5.2 33 | 34 | * Rename method channel 35 | 36 | ## 0.5.1 37 | 38 | * Fix formatting 39 | 40 | ## 0.5.0 41 | 42 | * Initial release. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Onyemaechi Okafor 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # flutter_account_kit 3 | [![pub package](https://img.shields.io/pub/v/flutter_account_kit.svg)](https://pub.dartlang.org/packages/flutter_account_kit) 4 | [![Build Status](https://travis-ci.org/peerwaya/flutter_account_kit.svg?branch=master)](https://travis-ci.org/peerwaya/flutter_account_kit) 5 | [![Coverage Status](https://coveralls.io/repos/github/peerwaya/flutter_account_kit/badge.svg?branch=master)](https://coveralls.io/github/peerwaya/flutter_account_kit?branch=master) 6 | A Flutter plugin for allowing users to authenticate with the native Android & iOS AccountKit SDKs 7 | 8 | ## How do I use it? 9 | 10 | For complete API documentation, just see the [source code](https://github.com/peerwaya/flutter-account-kit/blob/master/lib/src/account_kit.dart). 11 | 12 | ```dart 13 | import 'package:flutter_account_kit/flutter_account_kit.dart'; 14 | 15 | FlutterAccountKit akt = new FlutterAccountKit(); 16 | LoginResult result = await akt.logInWithPhone(); 17 | 18 | switch (result.status) { 19 | case LoginStatus.loggedIn: 20 | _sendTokenToServer(result.accessToken.token); 21 | _showLoggedInUI(); 22 | break; 23 | case LoginStatus.cancelledByUser: 24 | _showCancelledMessage(); 25 | break; 26 | case LoginStatus.error: 27 | _showErrorOnUI(); 28 | break; 29 | } 30 | ``` 31 | 32 | ## Installation 33 | 34 | To get things up and running, you'll have to declare a pubspec dependency in your Flutter project. 35 | Also some minimal Android & iOS specific configuration must be done, otherwise your app will crash. 36 | 37 | ### On your Flutter project 38 | 39 | See the [installation instructions on pub](https://pub.dartlang.org/packages/flutter_account_kit#-installing-tab-). 40 | 41 | #### Configuration 42 | Find out your _Facebook App ID_ and _AccountKit Client Token_ from Facebook App's dashboard in the Facebook developer console. 43 |
44 | Android 45 |
46 | 1. In **\/android/app/src/main/res/values/strings.xml** 47 | 48 | ```xml 49 | ... 50 | YOUR_FACEBOOK_APP_ID 51 | YOUR_CLIENT_TOKEN 52 | ``` 53 | 54 | 2. In **\/android/app/src/main/AndroidManifest.xml** 55 | 56 | ```xml 57 | ... 58 | 59 | 60 | ... 61 | 64 | 67 | 70 | 71 | ... 72 | ``` 73 | This is the minimal required configuration. Take a look to the [Account Kit documentation for Android](https://developers.facebook.com/docs/accountkit/android) for a more detailed guide. 74 | 75 | #### (Optional) Exclude backup for Access Tokens on Android >= 6.0 76 | 77 | As per this [documentation](https://developers.facebook.com/docs/accountkit/accesstokens), Account Kit does not support automated backup (introduced in Android 6.0). The following steps will exclude automated backup 78 | 79 | 1. Create a file **\/android/app/src/main/res/xml/backup_config.xml** that contains the following: 80 | 81 | ```java 82 | 83 | 84 | 85 | 86 | ``` 87 | 88 | 2. In your `AndroidManifest.xml` add the following to exclude backup of Account Kit's Access Token. 89 | 90 | ```java 91 | 95 | ``` 96 |
97 | 98 |
99 | iOS 100 |
101 | 102 | Add your Facebook credentials to your project's `Info.plist` file 103 | 104 | ```xml 105 | 106 | 107 | ... 108 | FacebookAppID 109 | {your-app-id} 110 | AccountKitClientToken 111 | {your-account-kit-client-token} 112 | CFBundleURLTypes 113 | 114 | 115 | CFBundleURLSchemes 116 | 117 | ak{your-app-id} 118 | 119 | 120 | 121 | ... 122 | 123 | 124 | ``` 125 | 126 | _This is the minimal required configuration. Take a look to the [Account Kit documentation for iOS](https://developers.facebook.com/docs/accountkit/ios) for a more detailed guide._ 127 | 128 |
129 | Done! 130 | 131 | 132 | ## Themes 133 | 134 |
135 | iOS 136 |
137 | 138 | ```dart 139 | import 'package:flutter/material.dart'; 140 | import 'package:flutter_account_kit/flutter_account_kit.dart'; 141 | 142 | final theme = AccountKitTheme( 143 | // Background 144 | backgroundColor: Color.fromARGB(255, 0, 120, 0,), 145 | backgroundImage: 'background.png', 146 | // Button 147 | buttonBackgroundColor: Color.fromARGB(255, 0, 153, 0), 148 | buttonBorderColor: Color.fromARGB(255, 0, 255, 0), 149 | buttonTextColor: Color.fromARGB(255, 0, 255, 0), 150 | // Button disabled 151 | buttonDisabledBackgroundColor: Color.fromARGB(255, 100, 153, 0), 152 | buttonDisabledBorderColor: Color.fromARGB(255, 100, 153, 0), 153 | buttonDisabledTextColor: Color.fromARGB(255, 100, 153, 0), 154 | // Header 155 | headerBackgroundColor: Color.fromARGB(255, 0, 153, 0), 156 | headerButtonTextColor: Color.fromARGB(255, 0, 153, 0), 157 | headerTextColor: Color.fromARGB(255, 0, 255, 0), 158 | // Input 159 | inputBackgroundColor: Color.fromARGB(255, 0, 255, 0), 160 | inputBorderColor: Color.hex('#ccc'), 161 | inputTextColor: Color(0xFFb74093), 162 | // Others 163 | iconColor: Color(0xFFFFFFFF), 164 | textColor: Color(0xFFb74093), 165 | titleColor: Color(0xFFb74093), 166 | // Header 167 | statusBarStyle: StatusBarStyle.lightStyle, // or StatusBarStyle.defaultStyle 168 | ); 169 | FlutterAccountKit akt = new FlutterAccountKit(); 170 | Config cfg = Config() 171 | ..theme = theme; 172 | akt.configure(cfg); 173 | ``` 174 | 175 | > To see the statusBarStyle reflected you must set the **UIViewControllerBasedStatusBarAppearance** property to **true** on your app's _Info.plist_ file. 176 | > You can do it from XCode screen shot 2016-08-02 at 11 44 07 am 177 | 178 |
179 | 180 |
181 | Android 182 | 183 |
184 | 185 | > Check [this commit](https://github.com/underscopeio/react-native-facebook-account-kit/commit/77df35ae20f251e7c29285e8820da2ff498d9400) to see how it's done in our sample app 186 | 187 | 1. In your application _styles.xml_ file (usually located in _\/android/app/src/main/res/values_ folder) create a **Theme** with the following schema 188 | 189 | ```xml 190 | 199 | ``` 200 | 201 | > See the full set of customizable fields [here](https://developers.facebook.com/docs/accountkit/android/customizing) 202 | 203 | 2. In your app _AndroidManifest.xml_ file (usually under _\/android/app/src/main_ folder) set that **Theme** to the **AccountKitActivity** 204 | 205 | ```xml 206 | 209 | 210 | 211 | 215 | 216 | 217 | ``` 218 | 219 |
220 | 221 | ## Troubleshooting 222 | 223 |
224 | "A system issue occured, Please try again" when sending SMS 225 |
226 | 227 | A. Check your `FacebookAppID` and `AccountKitClientToken` on iOS `Info.plist` and Android `strings.xml` are correct 228 | 229 | B. If you have enabled the **client access token flow in fb account kit dashboard**, then `responseType` should be set to `code` when calling `configure` 230 | 231 | ```dart 232 | // Configures the SDK with some options 233 | import 'package:flutter_account_kit/flutter_account_kit.dart'; 234 | 235 | FlutterAccountKit akt = new FlutterAccountKit(); 236 | Config cfg = Config() 237 | ..responseType = ResponseType.code; 238 | akt.configure(cfg); 239 | 240 | 241 | ``` 242 |
243 | 244 | ## Inspiration 245 | This project was inspired by 246 | [flutter_facebook_login](https://github.com/roughike/flutter_facebook_login) and 247 | [react-native-facebook-account-kit](https://github.com/underscopeio/react-native-facebook-account-kit) -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.iml 4 | .gradle 5 | /local.properties 6 | /build 7 | /captures 8 | .atom/ 9 | .idea 10 | .vscode/ 11 | .packages 12 | .pub/ 13 | build/ 14 | ios/.generated/ 15 | packages 16 | pubspec.lock 17 | .flutter-plugins -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.peerwaya.flutteraccountkit' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.0.1' 12 | } 13 | } 14 | 15 | rootProject.allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | apply plugin: 'com.android.library' 23 | 24 | android { 25 | compileSdkVersion 28 26 | 27 | defaultConfig { 28 | minSdkVersion 16 29 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 30 | } 31 | lintOptions { 32 | disable 'InvalidPackage' 33 | } 34 | } 35 | 36 | dependencies { 37 | api ('com.facebook.android:account-kit-sdk:5.+') 38 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 20 20:35:01 WAT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_account_kit' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/peerwaya/flutteraccountkit/FlutterAccountKitPlugin.java: -------------------------------------------------------------------------------- 1 | package com.peerwaya.flutteraccountkit; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.content.pm.PackageManager; 6 | import android.os.Bundle; 7 | import androidx.core.content.ContextCompat; 8 | import android.util.Log; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import io.flutter.plugin.common.MethodCall; 15 | import io.flutter.plugin.common.MethodChannel; 16 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 17 | import io.flutter.plugin.common.MethodChannel.Result; 18 | import io.flutter.plugin.common.PluginRegistry.Registrar; 19 | 20 | import com.facebook.accountkit.AccessToken; 21 | import com.facebook.accountkit.Account; 22 | import com.facebook.accountkit.AccountKit; 23 | import com.facebook.accountkit.AccountKitCallback; 24 | import com.facebook.accountkit.AccountKitError; 25 | import com.facebook.accountkit.PhoneNumber; 26 | import com.facebook.accountkit.ui.AccountKitActivity; 27 | import com.facebook.accountkit.ui.AccountKitConfiguration; 28 | import com.facebook.accountkit.ui.LoginType; 29 | 30 | /** 31 | * FlutterAccountKitPlugin 32 | */ 33 | public class FlutterAccountKitPlugin implements MethodCallHandler { 34 | public static final String CHANNEL_NAME = "com.peerwaya/flutter_account_kit"; 35 | private static final String METHOD_LOG_IN = "login"; 36 | private static final String METHOD_LOG_OUT = "logOut"; 37 | private static final String METHOD_GET_CURRENT_ACCESS_TOKEN = "getCurrentAccessToken"; 38 | private static final String METHOD_GET_CURRENT_ACCOUNT = "getCurrentAccount"; 39 | private static final String METHOD_CONFIGURE = "configure"; 40 | private static final String ARG_LOGIN_TYPE = "loginType"; 41 | private static final String ARG_CONFIG_OPTIONS = "configOptions"; 42 | public static int APP_REQUEST_CODE = 99; 43 | public static String LOG_TAG = "FlutterAccountKit"; 44 | private final AccountKitDelegate delegate; 45 | 46 | private FlutterAccountKitPlugin(Registrar registrar) { 47 | delegate = new AccountKitDelegate(registrar); 48 | } 49 | 50 | public static void registerWith(Registrar registrar) { 51 | final FlutterAccountKitPlugin plugin = new FlutterAccountKitPlugin(registrar); 52 | final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME); 53 | channel.setMethodCallHandler(plugin); 54 | AccountKit.initialize(registrar.context(), null); 55 | } 56 | 57 | // Replace Turkish İ and ı with their normalized versions (I and i, respectively) 58 | private static String safeString(String str) { 59 | return str.replace("İ", "I").replace("ı", "i"); 60 | } 61 | 62 | @Override 63 | public void onMethodCall(MethodCall call, Result result) { 64 | switch (call.method) { 65 | case METHOD_CONFIGURE: 66 | Map options = call.argument(ARG_CONFIG_OPTIONS); 67 | delegate.configure(options, result); 68 | break; 69 | case METHOD_LOG_IN: 70 | String loginTypeStr = call.argument(ARG_LOGIN_TYPE); 71 | LoginType loginType = LoginType.valueOf(safeString(loginTypeStr.toUpperCase())); 72 | delegate.logIn(loginType, result); 73 | break; 74 | case METHOD_LOG_OUT: 75 | delegate.logOut(result); 76 | break; 77 | case METHOD_GET_CURRENT_ACCESS_TOKEN: 78 | delegate.getCurrentAccessToken(result); 79 | break; 80 | case METHOD_GET_CURRENT_ACCOUNT: 81 | delegate.getCurrentAccount(result); 82 | break; 83 | default: 84 | result.notImplemented(); 85 | break; 86 | } 87 | } 88 | 89 | public static final class AccountKitDelegate { 90 | private final Registrar registrar; 91 | private final LoginResultDelegate resultDelegate; 92 | private Map options; 93 | 94 | public AccountKitDelegate(Registrar registrar) { 95 | this.registrar = registrar; 96 | this.resultDelegate = new LoginResultDelegate(); 97 | registrar.addActivityResultListener(resultDelegate); 98 | } 99 | 100 | public void configure( 101 | Map options, Result result) { 102 | this.options = options; 103 | result.success(null); 104 | } 105 | 106 | 107 | public void logIn( 108 | LoginType loginType, Result result) { 109 | if (!AccountKit.isInitialized()) { 110 | Log.w(LOG_TAG, "AccountKit not initialized yet. `login` call discarded"); 111 | result.success(null); 112 | return; 113 | } 114 | 115 | if (this.options == null) { 116 | Log.e(LOG_TAG, "You must call `configure` method providing configure options first"); 117 | result.success(null); 118 | return; 119 | } 120 | final String method = METHOD_LOG_IN; 121 | this.resultDelegate.setPendingResult(method, result); 122 | 123 | final Intent intent = new Intent(this.registrar.context(), AccountKitActivity.class); 124 | final AccountKitConfiguration.AccountKitConfigurationBuilder configurationBuilder = 125 | createAccountKitConfiguration(loginType); 126 | intent.putExtra(AccountKitActivity.ACCOUNT_KIT_ACTIVITY_CONFIGURATION, configurationBuilder.build()); 127 | this.registrar.activity().startActivityForResult(intent, APP_REQUEST_CODE, new Bundle()); 128 | } 129 | 130 | public void logOut(Result result) { 131 | if (!AccountKit.isInitialized()) { 132 | Log.w(LOG_TAG, "AccountKit not initialized yet. `logout` call discarded"); 133 | result.success(null); 134 | return; 135 | } 136 | 137 | AccountKit.logOut(); 138 | result.success(null); 139 | } 140 | 141 | public void getCurrentAccessToken(Result result) { 142 | if (!AccountKit.isInitialized()) { 143 | Log.w(LOG_TAG, "AccountKit not initialized yet. `getCurrentAccessToken` call discarded"); 144 | result.success(null); 145 | return; 146 | } 147 | 148 | AccessToken token = AccountKit.getCurrentAccessToken(); 149 | 150 | Map tokenMap = LoginResults.accessToken(token); 151 | 152 | result.success(tokenMap); 153 | } 154 | 155 | public void getCurrentAccount(final Result result) { 156 | if (!AccountKit.isInitialized()) { 157 | Log.w(LOG_TAG, "AccountKit not initialized yet. `getCurrentAccount` call discarded"); 158 | result.success(null); 159 | return; 160 | } 161 | 162 | AccountKit.getCurrentAccount(new AccountKitCallback() { 163 | @Override 164 | public void onSuccess(Account account) { 165 | result.success(LoginResults.account(account)); 166 | } 167 | 168 | @Override 169 | public void onError(AccountKitError error) { 170 | result.success(null); 171 | } 172 | }); 173 | } 174 | 175 | /** 176 | * Private methods 177 | */ 178 | 179 | private AccountKitConfiguration.AccountKitConfigurationBuilder createAccountKitConfiguration( 180 | final LoginType loginType) { 181 | AccountKitConfiguration.AccountKitConfigurationBuilder configurationBuilder = 182 | new AccountKitConfiguration.AccountKitConfigurationBuilder(loginType, 183 | AccountKitActivity.ResponseType.valueOf( 184 | safeString(((String) this.options.get("responseType")).toUpperCase()))); 185 | 186 | String initialAuthState = (String) this.options.get(("initialAuthState")); 187 | if (initialAuthState != null && !initialAuthState.isEmpty()) { 188 | configurationBuilder.setInitialAuthState(initialAuthState); 189 | } 190 | 191 | String initialEmail = (String) this.options.get("initialEmail"); 192 | if (initialEmail != null && !initialEmail.isEmpty()) { 193 | configurationBuilder.setInitialEmail(initialEmail); 194 | } 195 | 196 | String initialPhoneCountryPrefix = (String) this.options.get("initialPhoneCountryPrefix"); 197 | String initialPhoneNumber = (String) this.options.get("initialPhoneNumber"); 198 | 199 | if (initialPhoneCountryPrefix != null && initialPhoneNumber != null) { 200 | PhoneNumber phoneNumber = new PhoneNumber(initialPhoneCountryPrefix, initialPhoneNumber, null); 201 | configurationBuilder.setInitialPhoneNumber(phoneNumber); 202 | } 203 | 204 | configurationBuilder.setFacebookNotificationsEnabled( 205 | (Boolean) this.options.get("facebookNotificationsEnabled")); 206 | 207 | boolean readPhoneStateEnabled = (Boolean) this.options.get("readPhoneStateEnabled"); 208 | if (readPhoneStateEnabled && PackageManager.PERMISSION_DENIED == ContextCompat.checkSelfPermission( 209 | this.registrar.context(), Manifest.permission.READ_PHONE_STATE)) { 210 | Log.w(LOG_TAG, "To allow reading phone number add READ_PHONE_STATE permission in your app's manifest"); 211 | configurationBuilder.setReadPhoneStateEnabled(false); 212 | } else { 213 | configurationBuilder.setReadPhoneStateEnabled(readPhoneStateEnabled); 214 | } 215 | 216 | if (this.options.containsKey("countryBlacklist")) { 217 | String[] blacklist = formatCountryList((List) this.options.get("countryBlacklist")); 218 | configurationBuilder.setSMSBlacklist(blacklist); 219 | } 220 | 221 | if (this.options.containsKey("countryWhitelist")) { 222 | String[] whitelist = formatCountryList((List) this.options.get("countryWhitelist")); 223 | configurationBuilder.setSMSWhitelist(whitelist); 224 | } 225 | 226 | if (this.options.containsKey("defaultCountry")) { 227 | configurationBuilder.setDefaultCountryCode((String) this.options.get("defaultCountry")); 228 | } 229 | 230 | return configurationBuilder; 231 | } 232 | 233 | private String[] formatCountryList(List list) { 234 | List pre = new ArrayList<>(); 235 | for (int i = 0, n = list.size(); i < n; i++) { 236 | pre.add(list.get(i)); 237 | } 238 | 239 | String[] out = new String[pre.size()]; 240 | return pre.toArray(out); 241 | } 242 | 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /android/src/main/java/com/peerwaya/flutteraccountkit/LoginResultDelegate.java: -------------------------------------------------------------------------------- 1 | package com.peerwaya.flutteraccountkit; 2 | 3 | import android.content.Intent; 4 | 5 | import com.facebook.accountkit.AccountKitLoginResult; 6 | 7 | import io.flutter.plugin.common.MethodChannel; 8 | import io.flutter.plugin.common.PluginRegistry; 9 | 10 | class LoginResultDelegate implements PluginRegistry.ActivityResultListener { 11 | private static final String ERROR_LOGIN_IN_PROGRESS = "login_in_progress"; 12 | 13 | private MethodChannel.Result pendingResult; 14 | 15 | 16 | void setPendingResult(String methodName, MethodChannel.Result result) { 17 | if (pendingResult != null) { 18 | result.error( 19 | ERROR_LOGIN_IN_PROGRESS, 20 | methodName + " called while another Facebook " + 21 | "login operation was in progress.", 22 | null 23 | ); 24 | } 25 | 26 | pendingResult = result; 27 | } 28 | 29 | 30 | @Override 31 | public boolean onActivityResult(int requestCode, int resultCode, Intent data) { 32 | if (requestCode == FlutterAccountKitPlugin.APP_REQUEST_CODE) { 33 | AccountKitLoginResult loginResult = data.getParcelableExtra(AccountKitLoginResult.RESULT_KEY); 34 | if (loginResult.getError() != null) { 35 | finishWithResult(LoginResults.error(loginResult.getError())); 36 | } else if (loginResult.wasCancelled()) { 37 | finishWithResult(LoginResults.cancelledByUser); 38 | } else { 39 | finishWithResult(LoginResults.success(loginResult)); 40 | } 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | private void finishWithResult(Object result) { 47 | if (pendingResult != null) { 48 | pendingResult.success(result); 49 | pendingResult = null; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /android/src/main/java/com/peerwaya/flutteraccountkit/LoginResults.java: -------------------------------------------------------------------------------- 1 | package com.peerwaya.flutteraccountkit; 2 | 3 | import com.facebook.accountkit.AccessToken; 4 | import com.facebook.accountkit.Account; 5 | import com.facebook.accountkit.AccountKitError; 6 | import com.facebook.accountkit.AccountKitLoginResult; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class LoginResults { 12 | static final Map cancelledByUser = new HashMap() {{ 13 | put("status", "cancelledByUser"); 14 | }}; 15 | 16 | static Map success(AccountKitLoginResult loginResult) { 17 | final AccessToken accessToken = loginResult.getAccessToken(); 18 | if (accessToken != null) { 19 | final Map accessTokenMap = LoginResults.accessToken(accessToken); 20 | final String state = loginResult.getFinalAuthorizationState(); 21 | return new HashMap() {{ 22 | put("state", state); 23 | put("status", "loggedIn"); 24 | put("accessToken", accessTokenMap); 25 | }}; 26 | } else { 27 | final String code = loginResult.getAuthorizationCode(); 28 | final String state = loginResult.getFinalAuthorizationState(); 29 | return new HashMap() {{ 30 | put("status", "loggedIn"); 31 | put("code", code); 32 | put("state", state); 33 | }}; 34 | } 35 | } 36 | 37 | static Map error(final AccountKitError error) { 38 | return new HashMap() {{ 39 | put("status", "error"); 40 | put("errorMessage", error.getErrorType().getMessage()); 41 | }}; 42 | } 43 | 44 | static Map accessToken(final AccessToken accessToken) { 45 | if (accessToken == null) { 46 | return null; 47 | } 48 | 49 | return new HashMap() {{ 50 | put("accountId", accessToken.getAccountId()); 51 | put("appId", accessToken.getApplicationId()); 52 | put("token", accessToken.getToken()); 53 | put("lastRefresh", accessToken.getLastRefresh().getTime()); 54 | put("refreshIntervalSeconds", accessToken.getTokenRefreshIntervalSeconds()); 55 | }}; 56 | } 57 | 58 | static Map account(final Account account) { 59 | if (account == null) { 60 | return null; 61 | } 62 | 63 | final HashMap map = new HashMap() {{ 64 | put("accountId", account.getId()); 65 | put("email", account.getEmail()); 66 | }}; 67 | 68 | if (account.getPhoneNumber() != null) { 69 | final HashMap phoneNumber = new HashMap() {{ 70 | put("countryCode", account.getPhoneNumber().getCountryCode()); 71 | put("number", account.getPhoneNumber().getPhoneNumber()); 72 | }}; 73 | map.put("phoneNumber", phoneNumber); 74 | } 75 | 76 | return map; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /coverage/lcov.info: -------------------------------------------------------------------------------- 1 | SF:lib/src/access_token.dart 2 | DA:26,1 3 | DA:27,1 4 | DA:28,1 5 | DA:29,1 6 | DA:30,1 7 | DA:31,1 8 | DA:37,1 9 | DA:38,1 10 | DA:39,1 11 | DA:40,1 12 | DA:41,1 13 | DA:42,1 14 | DA:43,1 15 | DA:47,1 16 | DA:50,1 17 | DA:51,3 18 | DA:52,3 19 | DA:53,3 20 | DA:54,3 21 | DA:55,3 22 | DA:56,3 23 | DA:58,0 24 | DA:60,0 25 | DA:61,0 26 | DA:62,0 27 | DA:63,0 28 | DA:64,0 29 | LF:27 30 | LH:21 31 | end_of_record 32 | SF:lib/src/account.dart 33 | DA:20,1 34 | DA:21,1 35 | DA:22,1 36 | DA:23,1 37 | DA:24,2 38 | DA:27,1 39 | DA:33,0 40 | DA:34,0 41 | DA:35,0 42 | DA:36,0 43 | DA:37,0 44 | DA:41,1 45 | DA:44,1 46 | DA:45,3 47 | DA:46,3 48 | DA:47,3 49 | DA:49,0 50 | DA:51,0 51 | LF:18 52 | LH:11 53 | end_of_record 54 | SF:lib/src/account_kit_theme.dart 55 | DA:6,1 56 | DA:10,1 57 | DA:11,2 58 | DA:12,2 59 | DA:13,2 60 | DA:14,2 61 | DA:19,1 62 | DA:20,1 63 | DA:23,1 64 | DA:24,1 65 | DA:26,1 66 | DA:30,0 67 | DA:33,1 68 | DA:111,1 69 | DA:112,1 70 | DA:113,2 71 | DA:114,1 72 | DA:115,2 73 | DA:116,2 74 | DA:117,2 75 | DA:118,2 76 | DA:119,2 77 | DA:120,2 78 | DA:122,2 79 | DA:123,2 80 | DA:124,2 81 | DA:125,2 82 | DA:126,2 83 | DA:127,2 84 | DA:128,2 85 | DA:129,2 86 | DA:130,2 87 | DA:131,1 88 | DA:135,1 89 | DA:136,2 90 | DA:137,1 91 | DA:138,1 92 | DA:142,3 93 | DA:148,2 94 | DA:150,1 95 | DA:153,1 96 | LF:41 97 | LH:40 98 | end_of_record 99 | SF:lib/src/config.dart 100 | DA:9,1 101 | DA:13,2 102 | DA:14,1 103 | DA:17,3 104 | DA:19,2 105 | DA:21,2 106 | DA:22,1 107 | DA:30,1 108 | DA:31,1 109 | DA:32,1 110 | DA:33,1 111 | DA:35,1 112 | DA:39,0 113 | DA:43,1 114 | DA:44,1 115 | DA:45,1 116 | DA:46,1 117 | DA:48,1 118 | DA:52,0 119 | DA:55,1 120 | DA:136,1 121 | DA:137,1 122 | DA:138,1 123 | DA:141,0 124 | DA:142,0 125 | DA:145,1 126 | DA:146,1 127 | DA:147,1 128 | DA:150,0 129 | DA:151,0 130 | DA:154,1 131 | DA:155,1 132 | DA:156,1 133 | DA:159,1 134 | DA:160,1 135 | DA:163,1 136 | DA:164,1 137 | DA:165,1 138 | DA:168,1 139 | DA:169,1 140 | DA:172,1 141 | DA:173,2 142 | DA:174,1 143 | DA:177,1 144 | DA:178,1 145 | DA:185,1 146 | DA:186,1 147 | DA:187,1 148 | DA:188,1 149 | DA:189,1 150 | DA:190,1 151 | DA:191,1 152 | DA:192,1 153 | DA:193,1 154 | DA:194,1 155 | DA:195,3 156 | DA:196,3 157 | DA:198,1 158 | DA:199,3 159 | DA:202,3 160 | DA:203,0 161 | DA:206,3 162 | DA:207,0 163 | DA:211,1 164 | DA:212,2 165 | DA:213,1 166 | DA:214,1 167 | DA:218,3 168 | LF:68 169 | LH:60 170 | end_of_record 171 | SF:lib/src/login_result.dart 172 | DA:40,1 173 | DA:41,2 174 | DA:42,1 175 | DA:43,1 176 | DA:44,2 177 | DA:47,1 178 | DA:48,1 179 | DA:50,1 180 | DA:52,1 181 | DA:54,1 182 | DA:56,1 183 | DA:60,0 184 | LF:12 185 | LH:11 186 | end_of_record 187 | SF:lib/src/login_status.dart 188 | DA:2,2 189 | DA:4,1 190 | DA:8,1 191 | DA:12,1 192 | LF:4 193 | LH:4 194 | end_of_record 195 | SF:lib/src/phone_number.dart 196 | DA:9,1 197 | DA:15,1 198 | DA:16,1 199 | DA:17,1 200 | DA:23,1 201 | DA:24,1 202 | DA:25,1 203 | DA:26,1 204 | DA:30,1 205 | DA:33,1 206 | DA:34,3 207 | DA:35,3 208 | DA:36,3 209 | DA:38,0 210 | DA:39,0 211 | DA:41,0 212 | DA:42,0 213 | LF:17 214 | LH:13 215 | end_of_record 216 | SF:lib/src/response_type.dart 217 | DA:2,2 218 | DA:4,1 219 | DA:7,1 220 | LF:3 221 | LH:3 222 | end_of_record 223 | SF:lib/src/title_type.dart 224 | DA:3,2 225 | DA:5,1 226 | DA:8,1 227 | LF:3 228 | LH:3 229 | end_of_record 230 | SF:lib/src/account_kit.dart 231 | DA:42,1 232 | DA:48,0 233 | DA:50,1 234 | DA:52,1 235 | DA:54,1 236 | DA:57,0 237 | DA:69,1 238 | DA:72,0 239 | DA:74,4 240 | DA:80,3 241 | DA:100,1 242 | DA:102,2 243 | DA:108,2 244 | DA:133,1 245 | DA:135,2 246 | DA:141,2 247 | DA:151,1 248 | DA:152,2 249 | DA:153,2 250 | DA:155,1 251 | DA:156,2 252 | DA:166,1 253 | DA:167,2 254 | DA:168,2 255 | DA:170,1 256 | DA:171,2 257 | DA:175,2 258 | DA:186,1 259 | DA:187,2 260 | LF:29 261 | LH:26 262 | end_of_record 263 | SF:lib/src/login_type.dart 264 | DA:3,2 265 | DA:5,1 266 | DA:8,1 267 | LF:3 268 | LH:3 269 | end_of_record 270 | SF:lib/src/countries.dart 271 | DA:1,1 272 | LF:1 273 | LH:1 274 | end_of_record 275 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .vscode/ 5 | .packages 6 | .pub/ 7 | build/ 8 | ios/.generated/ 9 | packages 10 | pubspec.lock 11 | .flutter-plugins -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b 8 | channel: beta 9 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_account_kit_example 2 | 3 | Demonstrates how to use the flutter_account_kit plugin. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 28 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.peerwaya.flutteraccountkitexample" 27 | minSdkVersion 16 28 | targetSdkVersion 28 29 | versionCode 1 30 | versionName "1.0" 31 | testInstrumentationRunner "androidx.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 'androidx.test:runner:1.1.2-alpha01' 50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.2-alpha01' 51 | } 52 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 11 | 12 | 17 | 21 | 28 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 48 | 50 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/peerwaya/flutteraccountkitexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.peerwaya.flutteraccountkitexample; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #00A5FF 3 | #0084CC 4 | #0091e0 5 | #00A5FF 6 | #00a5ff 7 | #ff5d76 8 | #50e3c2 9 | #f5a623 10 | #ffffff 11 | #50d2c2 12 | #caf5f2 13 | #00a092 14 | #000000 15 | #333333 16 | #B3B3B3 17 | #FFFFFF 18 | #434343 19 | #FFEBAF 20 | #b5b5b5 21 | #979797 22 | #333333 23 | #757575 24 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Example 3 | [FACEBOOK_APP_ID] 4 | [ACCOUNT_KIT_CLIENT_TOKEN] 5 | 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 27 | 28 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:3.3.0" 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 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/flutter_account_kit_example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/flutter_account_kit_example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | def parse_KV_file(file, separator='=') 8 | file_abs_path = File.expand_path(file) 9 | if !File.exists? file_abs_path 10 | return []; 11 | end 12 | pods_ary = [] 13 | skip_line_start_symbols = ["#", "/"] 14 | File.foreach(file_abs_path) { |line| 15 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 16 | plugin = line.split(pattern=separator) 17 | if plugin.length == 2 18 | podname = plugin[0].strip() 19 | path = plugin[1].strip() 20 | podpath = File.expand_path("#{path}", file_abs_path) 21 | pods_ary.push({:name => podname, :path => podpath}); 22 | else 23 | puts "Invalid plugin specification: #{line}" 24 | end 25 | } 26 | return pods_ary 27 | end 28 | 29 | target 'Runner' do 30 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 31 | # referring to absolute paths on developers' machines. 32 | system('rm -rf .symlinks') 33 | system('mkdir -p .symlinks/plugins') 34 | 35 | # Flutter Pods 36 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 37 | if generated_xcode_build_settings.empty? 38 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 39 | end 40 | generated_xcode_build_settings.map { |p| 41 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 42 | symlink = File.join('.symlinks', 'flutter') 43 | File.symlink(File.dirname(p[:path]), symlink) 44 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 45 | end 46 | } 47 | 48 | # Plugin Pods 49 | plugin_pods = parse_KV_file('../.flutter-plugins') 50 | plugin_pods.map { |p| 51 | symlink = File.join('.symlinks', 'plugins', p[:name]) 52 | File.symlink(p[:path], symlink) 53 | pod p[:name], :path => File.join(symlink, 'ios') 54 | } 55 | end 56 | 57 | post_install do |installer| 58 | installer.pods_project.targets.each do |target| 59 | target.build_configurations.each do |config| 60 | config.build_settings['ENABLE_BITCODE'] = 'NO' 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AccountKit (5.0.1) 3 | - Flutter (1.0.0) 4 | - flutter_account_kit (0.0.1): 5 | - AccountKit 6 | - Flutter 7 | 8 | DEPENDENCIES: 9 | - Flutter (from `.symlinks/flutter/ios`) 10 | - flutter_account_kit (from `.symlinks/plugins/flutter_account_kit/ios`) 11 | 12 | SPEC REPOS: 13 | https://github.com/cocoapods/specs.git: 14 | - AccountKit 15 | 16 | EXTERNAL SOURCES: 17 | Flutter: 18 | :path: ".symlinks/flutter/ios" 19 | flutter_account_kit: 20 | :path: ".symlinks/plugins/flutter_account_kit/ios" 21 | 22 | SPEC CHECKSUMS: 23 | AccountKit: d05258857e74b04386c263a1365b27dbb5ff62b9 24 | Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296 25 | flutter_account_kit: e3eb9b78195f278c3d47426b6d93159b09b3e58a 26 | 27 | PODFILE CHECKSUM: 1e5af4103afd21ca5ead147d7b81d06f494f51a2 28 | 29 | COCOAPODS: 1.5.3 30 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 15 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 17 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | A7251D9F3FD9314612320D5D /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D9AA9DCFFCF0A30AB210CD16 /* libPods-Runner.a */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXCopyFilesBuildPhase section */ 27 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 28 | isa = PBXCopyFilesBuildPhase; 29 | buildActionMask = 2147483647; 30 | dstPath = ""; 31 | dstSubfolderSpec = 10; 32 | files = ( 33 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 34 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 35 | ); 36 | name = "Embed Frameworks"; 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXCopyFilesBuildPhase section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 43 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 44 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 45 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 52 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | D9AA9DCFFCF0A30AB210CD16 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 67 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 68 | A7251D9F3FD9314612320D5D /* libPods-Runner.a in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 0A32470B53D6D602572A693D /* Frameworks */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | D9AA9DCFFCF0A30AB210CD16 /* libPods-Runner.a */, 79 | ); 80 | name = Frameworks; 81 | sourceTree = ""; 82 | }; 83 | 9740EEB11CF90186004384FC /* Flutter */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 3B80C3931E831B6300D905FE /* App.framework */, 87 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 88 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 89 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 90 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 91 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 92 | ); 93 | name = Flutter; 94 | sourceTree = ""; 95 | }; 96 | 97C146E51CF9000F007C117D = { 97 | isa = PBXGroup; 98 | children = ( 99 | 9740EEB11CF90186004384FC /* Flutter */, 100 | 97C146F01CF9000F007C117D /* Runner */, 101 | 97C146EF1CF9000F007C117D /* Products */, 102 | A8D0DC21F7B27083D2E899D8 /* Pods */, 103 | 0A32470B53D6D602572A693D /* Frameworks */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 97C146EF1CF9000F007C117D /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 97C146EE1CF9000F007C117D /* Runner.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 97C146F01CF9000F007C117D /* Runner */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 119 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 120 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 121 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 122 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 123 | 97C147021CF9000F007C117D /* Info.plist */, 124 | 97C146F11CF9000F007C117D /* Supporting Files */, 125 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 126 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 127 | ); 128 | path = Runner; 129 | sourceTree = ""; 130 | }; 131 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 97C146F21CF9000F007C117D /* main.m */, 135 | ); 136 | name = "Supporting Files"; 137 | sourceTree = ""; 138 | }; 139 | A8D0DC21F7B27083D2E899D8 /* Pods */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | ); 143 | name = Pods; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 97C146ED1CF9000F007C117D /* Runner */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 152 | buildPhases = ( 153 | 4C39D368667FBEB134DD2023 /* [CP] Check Pods Manifest.lock */, 154 | 9740EEB61CF901F6004384FC /* Run Script */, 155 | 97C146EA1CF9000F007C117D /* Sources */, 156 | 97C146EB1CF9000F007C117D /* Frameworks */, 157 | 97C146EC1CF9000F007C117D /* Resources */, 158 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 159 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 160 | AC4FDE51954A1F25AF6BC99B /* [CP] Embed Pods Frameworks */, 161 | 15B85B74DD95371BD28E2400 /* [CP] Copy Pods Resources */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | ); 167 | name = Runner; 168 | productName = Runner; 169 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 170 | productType = "com.apple.product-type.application"; 171 | }; 172 | /* End PBXNativeTarget section */ 173 | 174 | /* Begin PBXProject section */ 175 | 97C146E61CF9000F007C117D /* Project object */ = { 176 | isa = PBXProject; 177 | attributes = { 178 | LastUpgradeCheck = 0910; 179 | ORGANIZATIONNAME = "The Chromium Authors"; 180 | TargetAttributes = { 181 | 97C146ED1CF9000F007C117D = { 182 | CreatedOnToolsVersion = 7.3.1; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | Base, 193 | ); 194 | mainGroup = 97C146E51CF9000F007C117D; 195 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | 97C146ED1CF9000F007C117D /* Runner */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 97C146EC1CF9000F007C117D /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 210 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 211 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 212 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 213 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 214 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXResourcesBuildPhase section */ 219 | 220 | /* Begin PBXShellScriptBuildPhase section */ 221 | 15B85B74DD95371BD28E2400 /* [CP] Copy Pods Resources */ = { 222 | isa = PBXShellScriptBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | ); 226 | inputPaths = ( 227 | "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", 228 | "${PODS_ROOT}/AccountKit/AccountKitStrings.bundle", 229 | ); 230 | name = "[CP] Copy Pods Resources"; 231 | outputPaths = ( 232 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccountKitStrings.bundle", 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | shellPath = /bin/sh; 236 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; 237 | showEnvVarsInLog = 0; 238 | }; 239 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 240 | isa = PBXShellScriptBuildPhase; 241 | buildActionMask = 2147483647; 242 | files = ( 243 | ); 244 | inputPaths = ( 245 | ); 246 | name = "Thin Binary"; 247 | outputPaths = ( 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 252 | }; 253 | 4C39D368667FBEB134DD2023 /* [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 | 9740EEB61CF901F6004384FC /* Run Script */ = { 272 | isa = PBXShellScriptBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | ); 276 | inputPaths = ( 277 | ); 278 | name = "Run Script"; 279 | outputPaths = ( 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | shellPath = /bin/sh; 283 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 284 | }; 285 | AC4FDE51954A1F25AF6BC99B /* [CP] Embed Pods Frameworks */ = { 286 | isa = PBXShellScriptBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | ); 290 | inputPaths = ( 291 | "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 292 | "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", 293 | ); 294 | name = "[CP] Embed Pods Frameworks"; 295 | outputPaths = ( 296 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | shellPath = /bin/sh; 300 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 301 | showEnvVarsInLog = 0; 302 | }; 303 | /* End PBXShellScriptBuildPhase section */ 304 | 305 | /* Begin PBXSourcesBuildPhase section */ 306 | 97C146EA1CF9000F007C117D /* Sources */ = { 307 | isa = PBXSourcesBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 311 | 97C146F31CF9000F007C117D /* main.m in Sources */, 312 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXSourcesBuildPhase section */ 317 | 318 | /* Begin PBXVariantGroup section */ 319 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 320 | isa = PBXVariantGroup; 321 | children = ( 322 | 97C146FB1CF9000F007C117D /* Base */, 323 | ); 324 | name = Main.storyboard; 325 | sourceTree = ""; 326 | }; 327 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 328 | isa = PBXVariantGroup; 329 | children = ( 330 | 97C147001CF9000F007C117D /* Base */, 331 | ); 332 | name = LaunchScreen.storyboard; 333 | sourceTree = ""; 334 | }; 335 | /* End PBXVariantGroup section */ 336 | 337 | /* Begin XCBuildConfiguration section */ 338 | 97C147031CF9000F007C117D /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 341 | buildSettings = { 342 | ALWAYS_SEARCH_USER_PATHS = NO; 343 | CLANG_ANALYZER_NONNULL = YES; 344 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 345 | CLANG_CXX_LIBRARY = "libc++"; 346 | CLANG_ENABLE_MODULES = YES; 347 | CLANG_ENABLE_OBJC_ARC = YES; 348 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 349 | CLANG_WARN_BOOL_CONVERSION = YES; 350 | CLANG_WARN_COMMA = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 353 | CLANG_WARN_EMPTY_BODY = YES; 354 | CLANG_WARN_ENUM_CONVERSION = YES; 355 | CLANG_WARN_INFINITE_RECURSION = YES; 356 | CLANG_WARN_INT_CONVERSION = YES; 357 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 358 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 359 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 360 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 361 | CLANG_WARN_STRICT_PROTOTYPES = YES; 362 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 363 | CLANG_WARN_UNREACHABLE_CODE = YES; 364 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 365 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 366 | COPY_PHASE_STRIP = NO; 367 | DEBUG_INFORMATION_FORMAT = dwarf; 368 | ENABLE_STRICT_OBJC_MSGSEND = YES; 369 | ENABLE_TESTABILITY = YES; 370 | GCC_C_LANGUAGE_STANDARD = gnu99; 371 | GCC_DYNAMIC_NO_PIC = NO; 372 | GCC_NO_COMMON_BLOCKS = YES; 373 | GCC_OPTIMIZATION_LEVEL = 0; 374 | GCC_PREPROCESSOR_DEFINITIONS = ( 375 | "DEBUG=1", 376 | "$(inherited)", 377 | ); 378 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 379 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 380 | GCC_WARN_UNDECLARED_SELECTOR = YES; 381 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 382 | GCC_WARN_UNUSED_FUNCTION = YES; 383 | GCC_WARN_UNUSED_VARIABLE = YES; 384 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 385 | MTL_ENABLE_DEBUG_INFO = YES; 386 | ONLY_ACTIVE_ARCH = YES; 387 | SDKROOT = iphoneos; 388 | TARGETED_DEVICE_FAMILY = "1,2"; 389 | }; 390 | name = Debug; 391 | }; 392 | 97C147041CF9000F007C117D /* Release */ = { 393 | isa = XCBuildConfiguration; 394 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 395 | buildSettings = { 396 | ALWAYS_SEARCH_USER_PATHS = NO; 397 | CLANG_ANALYZER_NONNULL = YES; 398 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 399 | CLANG_CXX_LIBRARY = "libc++"; 400 | CLANG_ENABLE_MODULES = YES; 401 | CLANG_ENABLE_OBJC_ARC = YES; 402 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 403 | CLANG_WARN_BOOL_CONVERSION = YES; 404 | CLANG_WARN_COMMA = YES; 405 | CLANG_WARN_CONSTANT_CONVERSION = YES; 406 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 407 | CLANG_WARN_EMPTY_BODY = YES; 408 | CLANG_WARN_ENUM_CONVERSION = YES; 409 | CLANG_WARN_INFINITE_RECURSION = YES; 410 | CLANG_WARN_INT_CONVERSION = YES; 411 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 412 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 414 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 415 | CLANG_WARN_STRICT_PROTOTYPES = YES; 416 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 417 | CLANG_WARN_UNREACHABLE_CODE = YES; 418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 419 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 420 | COPY_PHASE_STRIP = NO; 421 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 422 | ENABLE_NS_ASSERTIONS = NO; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | GCC_C_LANGUAGE_STANDARD = gnu99; 425 | GCC_NO_COMMON_BLOCKS = YES; 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 433 | MTL_ENABLE_DEBUG_INFO = NO; 434 | SDKROOT = iphoneos; 435 | TARGETED_DEVICE_FAMILY = "1,2"; 436 | VALIDATE_PRODUCT = YES; 437 | }; 438 | name = Release; 439 | }; 440 | 97C147061CF9000F007C117D /* Debug */ = { 441 | isa = XCBuildConfiguration; 442 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 443 | buildSettings = { 444 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 445 | CURRENT_PROJECT_VERSION = 1; 446 | ENABLE_BITCODE = NO; 447 | FRAMEWORK_SEARCH_PATHS = ( 448 | "$(inherited)", 449 | "$(PROJECT_DIR)/Flutter", 450 | ); 451 | INFOPLIST_FILE = Runner/Info.plist; 452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 453 | LIBRARY_SEARCH_PATHS = ( 454 | "$(inherited)", 455 | "$(PROJECT_DIR)/Flutter", 456 | ); 457 | PRODUCT_BUNDLE_IDENTIFIER = com.peerwaya.flutterAccountKitExample; 458 | PRODUCT_NAME = "$(TARGET_NAME)"; 459 | VERSIONING_SYSTEM = "apple-generic"; 460 | }; 461 | name = Debug; 462 | }; 463 | 97C147071CF9000F007C117D /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 466 | buildSettings = { 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | CURRENT_PROJECT_VERSION = 1; 469 | ENABLE_BITCODE = NO; 470 | FRAMEWORK_SEARCH_PATHS = ( 471 | "$(inherited)", 472 | "$(PROJECT_DIR)/Flutter", 473 | ); 474 | INFOPLIST_FILE = Runner/Info.plist; 475 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 476 | LIBRARY_SEARCH_PATHS = ( 477 | "$(inherited)", 478 | "$(PROJECT_DIR)/Flutter", 479 | ); 480 | PRODUCT_BUNDLE_IDENTIFIER = com.peerwaya.flutterAccountKitExample; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | VERSIONING_SYSTEM = "apple-generic"; 483 | }; 484 | name = Release; 485 | }; 486 | /* End XCBuildConfiguration section */ 487 | 488 | /* Begin XCConfigurationList section */ 489 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 490 | isa = XCConfigurationList; 491 | buildConfigurations = ( 492 | 97C147031CF9000F007C117D /* Debug */, 493 | 97C147041CF9000F007C117D /* Release */, 494 | ); 495 | defaultConfigurationIsVisible = 0; 496 | defaultConfigurationName = Release; 497 | }; 498 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 499 | isa = XCConfigurationList; 500 | buildConfigurations = ( 501 | 97C147061CF9000F007C117D /* Debug */, 502 | 97C147071CF9000F007C117D /* Release */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | /* End XCConfigurationList section */ 508 | }; 509 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 510 | } 511 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/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. -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_account_kit_example 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 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | FacebookAppID 45 | {your-facebook-appid} 46 | AccountKitClientToken 47 | {your-account-kit-client-token} 48 | CFBundleURLTypes 49 | 50 | 51 | CFBundleURLSchemes 52 | 53 | ak{your-facebook-app-id} 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_account_kit/flutter_account_kit.dart'; 5 | 6 | void main() => runApp(new MyApp()); 7 | 8 | class MyApp extends StatefulWidget { 9 | @override 10 | _MyAppState createState() => new _MyAppState(); 11 | } 12 | 13 | class _MyAppState extends State { 14 | FlutterAccountKit akt = FlutterAccountKit(); 15 | int _state = 0; 16 | bool _isInitialized = false; 17 | 18 | @override 19 | void initState() { 20 | super.initState(); 21 | initAccountkit(); 22 | } 23 | 24 | // Platform messages are asynchronous, so we initialize in an async method. 25 | Future initAccountkit() async { 26 | print('Init account kit called'); 27 | bool initialized = false; 28 | // Platform messages may fail, so we use a try/catch PlatformException. 29 | try { 30 | final theme = AccountKitTheme( 31 | headerBackgroundColor: Colors.green, 32 | buttonBackgroundColor: Colors.yellow, 33 | buttonBorderColor: Colors.yellow, 34 | buttonTextColor: Colors.black87); 35 | await akt.configure(Config() 36 | ..facebookNotificationsEnabled = true 37 | ..receiveSMS = true 38 | ..readPhoneStateEnabled = true 39 | ..theme = theme 40 | ); 41 | initialized = true; 42 | } on PlatformException { 43 | print('Failed to initialize account kit'); 44 | } 45 | 46 | // If the widget was removed from the tree while the asynchronous platform 47 | // message was in flight, we want to discard the reply rather than calling 48 | // setState to update our non-existent appearance. 49 | if (!mounted) return; 50 | setState(() { 51 | _isInitialized = initialized; 52 | print("isInitialied $_isInitialized"); 53 | }); 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return new MaterialApp( 59 | home: new Scaffold( 60 | appBar: new AppBar( 61 | title: const Text('Plugin example app'), 62 | ), 63 | body: new Center( 64 | child: RaisedButton( 65 | padding: EdgeInsets.all(0.0), 66 | color: _state == 2 ? Colors.green : Colors.blue, 67 | child: buildButtonChild(), 68 | onPressed: _isInitialized ? this.login : null, 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | 75 | Widget buildButtonChild() { 76 | if (_state == 0) { 77 | return Text( 78 | 'Login', 79 | style: TextStyle(color: Colors.white, fontSize: 16.0), 80 | ); 81 | } else if (_state == 1) { 82 | return SizedBox( 83 | height: 24.0, 84 | width: 24.0, 85 | child: CircularProgressIndicator( 86 | value: null, 87 | valueColor: AlwaysStoppedAnimation(Colors.white), 88 | )); 89 | } else { 90 | return Icon(Icons.check, color: Colors.white); 91 | } 92 | } 93 | 94 | Future login() async { 95 | if (_state == 1) { 96 | return; 97 | } 98 | setState(() { 99 | _state = 1; 100 | }); 101 | final result = await akt.logInWithPhone(); 102 | if (result.status == LoginStatus.cancelledByUser) { 103 | print('Login cancelled by user'); 104 | setState(() { 105 | _state = 0; 106 | }); 107 | } else if (result.status == LoginStatus.error) { 108 | print('Login error'); 109 | setState(() { 110 | _state = 0; 111 | }); 112 | } else { 113 | print('Login success'); 114 | setState(() { 115 | _state = 2; 116 | }); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_account_kit_example 2 | description: Demonstrates how to use the flutter_account_kit plugin. 3 | 4 | dependencies: 5 | flutter: 6 | sdk: flutter 7 | 8 | # The following adds the Cupertino Icons font to your application. 9 | # Use with the CupertinoIcons class for iOS style icons. 10 | cupertino_icons: ^0.1.2 11 | 12 | dev_dependencies: 13 | flutter_test: 14 | sdk: flutter 15 | 16 | flutter_account_kit: 17 | path: ../ 18 | 19 | # For information on the generic Dart part of this file, see the 20 | # following page: https://www.dartlang.org/tools/pub/pubspec 21 | 22 | # The following section is specific to Flutter. 23 | flutter: 24 | 25 | # The following line ensures that the Material Icons font is 26 | # included with your application, so that you can use the icons in 27 | # the material Icons class. 28 | uses-material-design: true 29 | 30 | # To add assets to your application, add an assets section, like this: 31 | # assets: 32 | # - images/a_dot_burr.jpeg 33 | # - images/a_dot_ham.jpeg 34 | 35 | # An image asset can refer to one or more resolution-specific "variants", see 36 | # https://flutter.io/assets-and-images/#resolution-aware. 37 | 38 | # For details regarding adding assets from package dependencies, see 39 | # https://flutter.io/assets-and-images/#from-packages 40 | 41 | # To add custom fonts to your application, add a fonts section here, 42 | # in this "flutter" section. Each entry in this list should have a 43 | # "family" key with the font family name, and a "fonts" key with a 44 | # list giving the asset and other descriptors for the font. For 45 | # example: 46 | # fonts: 47 | # - family: Schyler 48 | # fonts: 49 | # - asset: fonts/Schyler-Regular.ttf 50 | # - asset: fonts/Schyler-Italic.ttf 51 | # style: italic 52 | # - family: Trajan Pro 53 | # fonts: 54 | # - asset: fonts/TrajanPro.ttf 55 | # - asset: fonts/TrajanPro_Bold.ttf 56 | # weight: 700 57 | # 58 | # For details regarding fonts from package dependencies, 59 | # see https://flutter.io/custom-fonts/#from-packages 60 | -------------------------------------------------------------------------------- /flutter_account_kit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /flutter_account_kit_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_account_kit/0988d933d5af75ddfe7415c581e8e326849aab36/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/FlutterAccountKitPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "FlutterAccountKitViewController.h" 3 | 4 | @interface FlutterAccountKitPlugin : NSObject 5 | 6 | @property NSDictionary *options; 7 | + (void)registerWithRegistrar:(NSObject *)registrar ; 8 | + (NSMutableDictionary*) formatAccessToken: (id) accessToken; 9 | + (NSMutableDictionary*) formatAccountData: (id) account; 10 | @end 11 | -------------------------------------------------------------------------------- /ios/Classes/FlutterAccountKitPlugin.m: -------------------------------------------------------------------------------- 1 | // 2 | // AccountKit.m 3 | // Runner 4 | // 5 | // Created by Onyemaechi Okafor on 19/07/2018. 6 | // Copyright © 2018 The Chromium Authors. All rights reserved. 7 | // 8 | 9 | #import "FlutterAccountKitPlugin.h" 10 | 11 | @implementation FlutterAccountKitPlugin { 12 | AKFAccountKit *_accountKit; 13 | } 14 | 15 | + (void)registerWithRegistrar:(NSObject *)registrar { 16 | FlutterMethodChannel *channel = [FlutterMethodChannel 17 | methodChannelWithName:@"com.peerwaya/flutter_account_kit" 18 | binaryMessenger:[registrar messenger]]; 19 | FlutterAccountKitPlugin *instance = [[FlutterAccountKitPlugin alloc] init]; 20 | [registrar addApplicationDelegate:instance]; 21 | [registrar addMethodCallDelegate:instance channel:channel]; 22 | } 23 | 24 | - (void)handleMethodCall:(FlutterMethodCall *)call 25 | result:(FlutterResult)result { 26 | if ([@"configure" isEqualToString:call.method]) { 27 | NSDictionary *options = call.arguments[@"configOptions"]; 28 | 29 | [self configureWithOptions:options 30 | result:result]; 31 | } else if ([@"login" isEqualToString:call.method]) { 32 | AKFLoginType loginType = 33 | [self loginTypeFromString:call.arguments[@"loginType"]]; 34 | [self loginWithType:loginType 35 | result:result]; 36 | } else if ([@"logOut" isEqualToString:call.method]) { 37 | [self logOut:result]; 38 | } else if ([@"getCurrentAccessToken" isEqualToString:call.method]) { 39 | [self getCurrentAccessToken:result]; 40 | } else if ([@"getCurrentAccount" isEqualToString:call.method]) { 41 | [self getCurrentAccount:result]; 42 | } else { 43 | result(FlutterMethodNotImplemented); 44 | } 45 | } 46 | 47 | - (void)configureWithOptions:(NSDictionary *)options 48 | result:(FlutterResult)result { 49 | self.options = options; 50 | result(nil); 51 | } 52 | 53 | - (void)loginWithType:(AKFLoginType)type 54 | result:(FlutterResult)result { 55 | @try { 56 | FlutterAccountKitViewController* a = [[FlutterAccountKitViewController alloc] initWithAccountKit: [self getAccountKit]]; 57 | a.theme = [self getTheme]; 58 | a.countryWhitelist = [self.options valueForKey:@"countryWhitelist"]; 59 | a.countryBlacklist = [self.options valueForKey:@"countryBlacklist"]; 60 | a.defaultCountry = [self.options valueForKey:@"defaultCountry"]; 61 | a.initialEmail = [self.options valueForKey:@"initialEmail"]; 62 | a.initialPhoneCountryPrefix = [self.options valueForKey:@"initialPhoneCountryPrefix"]; 63 | a.initialPhoneNumber = [self.options valueForKey:@"initialPhoneNumber"]; 64 | if (type == AKFLoginTypePhone) { 65 | [a loginWithPhone: result]; 66 | } else { 67 | [a loginWithEmail: result]; 68 | } 69 | } 70 | @catch (NSException * e) { 71 | result([FlutterError errorWithCode:@"login_failed" 72 | message:@"Could not login" 73 | details:[FlutterAccountKitPlugin errorFromException:e]]); 74 | } 75 | } 76 | 77 | - (AKFLoginType)loginTypeFromString:(NSString *)loginTypeStr { 78 | if ([@"email" isEqualToString:loginTypeStr]) { 79 | return AKFLoginTypeEmail; 80 | } else if ([@"phone" isEqualToString:loginTypeStr]) { 81 | return AKFLoginTypePhone; 82 | } else { 83 | NSString *message = [NSString 84 | stringWithFormat:@"Unknown login type: %@", loginTypeStr]; 85 | 86 | @throw [NSException exceptionWithName:@"InvalidLoginTypeException" 87 | reason:message 88 | userInfo:nil]; 89 | } 90 | } 91 | 92 | - (void)getCurrentAccessToken:(FlutterResult)result { 93 | @try { 94 | id accessToken = [[self getAccountKit] currentAccessToken]; 95 | 96 | if (![accessToken accountID]) { 97 | return result(nil); 98 | } 99 | 100 | result([FlutterAccountKitPlugin formatAccessToken:accessToken]); 101 | } 102 | @catch (NSException * e) { 103 | result([FlutterError errorWithCode:@"access_token_error" 104 | message:@"Could not get access token" 105 | details:[FlutterAccountKitPlugin errorFromException:e]]); 106 | } 107 | } 108 | 109 | - (void)getCurrentAccount:(FlutterResult)result { 110 | __block bool callbackCalled = false; 111 | [[self getAccountKit] requestAccount:^(id account, NSError *error) { 112 | if (callbackCalled) { 113 | return; 114 | } 115 | callbackCalled = true; 116 | 117 | if (error) { 118 | result([FlutterError errorWithCode:@"request_account" 119 | message:@"Could not get account data" 120 | details:error]); 121 | } else { 122 | result([FlutterAccountKitPlugin formatAccountData:account]); 123 | } 124 | }]; 125 | } 126 | 127 | - (AKFTheme *)getTheme { 128 | AKFTheme *theme = [AKFTheme defaultTheme]; 129 | NSDictionary *themeOptions = [self.options objectForKey:@"theme"]; 130 | if(themeOptions == nil) { 131 | return theme; 132 | } 133 | NSArray *colorOptions = @[@"backgroundColor", 134 | @"headerBackgroundColor",@"headerTextColor",@"headerButtonTextColor", 135 | @"buttonBackgroundColor",@"buttonBorderColor",@"buttonTextColor", 136 | @"buttonDisabledBackgroundColor",@"buttonDisabledBorderColor", 137 | @"buttonDisabledTextColor",@"iconColor",@"inputBackgroundColor", 138 | @"inputBorderColor",@"inputTextColor",@"textColor",@"titleColor"]; 139 | for(NSString *key in themeOptions) { 140 | UIColor *color; 141 | if([colorOptions containsObject:key]) { 142 | NSDictionary *value = [themeOptions valueForKey:key]; 143 | color = [UIColor colorWithRed:[[value valueForKey:@"r"] floatValue] 144 | green:[[value valueForKey:@"g"] floatValue] 145 | blue:[[value valueForKey:@"b"] floatValue] 146 | alpha:[[value valueForKey:@"a"] floatValue]]; 147 | [theme setValue:color forKey:key]; 148 | } else if([key isEqualToString:@"backgroundImage"]) { 149 | theme.backgroundImage = [UIImage imageNamed:[themeOptions objectForKey:key]]; 150 | } else if([key isEqualToString:@"statusBarStyle"]) { 151 | int statusBarStyle = ((NSNumber*)[themeOptions valueForKey:key]).intValue; 152 | if (UIStatusBarStyleDefault == statusBarStyle) { 153 | theme.statusBarStyle = UIStatusBarStyleDefault; 154 | } 155 | if (UIStatusBarStyleLightContent == statusBarStyle) { 156 | theme.statusBarStyle = UIStatusBarStyleLightContent; 157 | } 158 | } 159 | } 160 | return theme; 161 | } 162 | 163 | - (void)logOut:(FlutterResult)result { 164 | @try { 165 | [[self getAccountKit] logOut]; 166 | result(nil); 167 | } 168 | @catch (NSException * e) { 169 | result([FlutterError errorWithCode:@"logout_error" 170 | message:@"Could not logout" 171 | details:[FlutterAccountKitPlugin errorFromException:e]]); 172 | } 173 | } 174 | 175 | - (AKFAccountKit*) getAccountKit 176 | { 177 | if (_accountKit == nil) { 178 | // may also specify AKFResponseTypeAccessToken 179 | BOOL useAccessToken = [[self.options valueForKey:@"responseType"] isEqualToString:@"token"]; 180 | AKFResponseType responseType = useAccessToken ? AKFResponseTypeAccessToken : AKFResponseTypeAuthorizationCode; 181 | _accountKit = [[AKFAccountKit alloc] initWithResponseType:responseType]; 182 | } 183 | 184 | return _accountKit; 185 | } 186 | 187 | + (NSMutableDictionary*) formatAccountData: (id) account 188 | { 189 | NSMutableDictionary *result =[[NSMutableDictionary alloc] init]; 190 | result[@"id"] = account.accountID; 191 | result[@"email"] = account.emailAddress; 192 | 193 | if (account.phoneNumber && account.phoneNumber.phoneNumber) { 194 | result[@"phoneNumber"] = @{ 195 | @"number": account.phoneNumber.phoneNumber, 196 | @"countryCode": account.phoneNumber.countryCode 197 | }; 198 | } 199 | 200 | return result; 201 | } 202 | 203 | + (NSMutableDictionary*) formatAccessToken: (id) accessToken 204 | { 205 | NSMutableDictionary *accessTokenData =[[NSMutableDictionary alloc] init]; 206 | accessTokenData[@"accountId"] = [accessToken accountID]; 207 | accessTokenData[@"appId"] = [accessToken applicationID]; 208 | accessTokenData[@"token"] = [accessToken tokenString]; 209 | accessTokenData[@"lastRefresh"] = [NSNumber numberWithLong: ([[accessToken lastRefresh] timeIntervalSince1970] * 1000)]; 210 | accessTokenData[@"refreshIntervalSeconds"] = [NSNumber numberWithLong: [accessToken tokenRefreshInterval]]; 211 | 212 | 213 | return accessTokenData; 214 | } 215 | 216 | + (NSError *) errorFromException: (NSException *) exception 217 | { 218 | NSDictionary *exceptionInfo = @{ 219 | @"name": exception.name, 220 | @"reason": exception.reason, 221 | @"callStackReturnAddresses": exception.callStackReturnAddresses, 222 | @"callStackSymbols": exception.callStackSymbols, 223 | @"userInfo": exception.userInfo 224 | }; 225 | 226 | return [[NSError alloc] initWithDomain: @"FlutterAccountKit" 227 | code: 0 228 | userInfo: exceptionInfo]; 229 | } 230 | @end 231 | -------------------------------------------------------------------------------- /ios/Classes/FlutterAccountKitViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | 5 | @interface FlutterAccountKitViewController : UIViewController 6 | 7 | @property(nonatomic, strong) FlutterAccountKitViewController *instance; 8 | @property(nonatomic, strong) AKFTheme *theme; 9 | @property(nonatomic, strong) NSArray *countryWhitelist; 10 | @property(nonatomic, strong) NSArray *countryBlacklist; 11 | @property(nonatomic, strong) NSString *defaultCountry; 12 | @property(nonatomic, strong) NSString *initialEmail; 13 | @property(nonatomic, strong) NSString *initialPhoneNumber; 14 | @property(nonatomic, strong) NSString *initialPhoneCountryPrefix; 15 | 16 | - (instancetype) initWithAccountKit: (AKFAccountKit *)accountKit; 17 | 18 | - (void)loginWithPhone: (FlutterResult)result; 19 | 20 | - (void)loginWithEmail: (FlutterResult)result; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ios/Classes/FlutterAccountKitViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AccountKitViewController.m 3 | // Runner 4 | // 5 | // Created by Onyemaechi Okafor on 19/07/2018. 6 | // Copyright © 2018 The Chromium Authors. All rights reserved. 7 | // 8 | 9 | #import "FlutterAccountKitPlugin.h" 10 | 11 | @implementation FlutterAccountKitViewController 12 | { 13 | FlutterAccountKitViewController *_instance; 14 | 15 | AKFAccountKit *_accountKit; 16 | UIViewController *_pendingLoginViewController; 17 | NSString *_authorizationCode; 18 | BOOL *isUserLoggedIn; 19 | 20 | FlutterResult _result; 21 | } 22 | 23 | - (instancetype)initWithAccountKit:(AKFAccountKit *)accountKit 24 | { 25 | self = [super init]; 26 | _accountKit = accountKit; 27 | 28 | _pendingLoginViewController = [_accountKit viewControllerForLoginResume]; 29 | _instance = self; 30 | 31 | return self; 32 | } 33 | 34 | - (void)_prepareLoginViewController:(UIViewController *)viewController 35 | { 36 | viewController.delegate = self; 37 | if(self.theme != nil) { 38 | viewController.theme = self.theme; 39 | } 40 | if (self.countryWhitelist != nil) { 41 | viewController.whitelistedCountryCodes = self.countryWhitelist; 42 | } 43 | if (self.countryBlacklist != nil) { 44 | viewController.blacklistedCountryCodes = self.countryBlacklist; 45 | } 46 | viewController.defaultCountryCode = self.defaultCountry; 47 | 48 | } 49 | 50 | - (void)loginWithPhone: (FlutterResult)result 51 | { 52 | _result = result; 53 | NSString *prefillPhone = self.initialPhoneNumber; 54 | NSString *prefillCountryCode = self.initialPhoneCountryPrefix; 55 | NSString *inputState = [[NSUUID UUID] UUIDString]; 56 | AKFPhoneNumber * prefillPhoneNumber = [[AKFPhoneNumber alloc] initWithCountryCode:prefillCountryCode phoneNumber:prefillPhone]; 57 | 58 | dispatch_async(dispatch_get_main_queue(), ^{ 59 | UIViewController *viewController = [_accountKit viewControllerForPhoneLoginWithPhoneNumber:prefillPhoneNumber state:inputState]; 60 | [self _prepareLoginViewController:viewController]; 61 | UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController; 62 | [rootViewController presentViewController:viewController animated:YES completion:NULL]; 63 | }); 64 | } 65 | 66 | - (void)loginWithEmail: (FlutterResult)result; 67 | { 68 | _result = result; 69 | NSString *prefillEmail = self.initialEmail; 70 | NSString *inputState = [[NSUUID UUID] UUIDString]; 71 | 72 | dispatch_async(dispatch_get_main_queue(), ^{ 73 | UIViewController *viewController = [_accountKit viewControllerForEmailLoginWithEmail:prefillEmail state:inputState]; 74 | [self _prepareLoginViewController:viewController]; 75 | UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController; 76 | [rootViewController presentViewController:viewController animated:YES completion:NULL]; 77 | }); 78 | } 79 | 80 | - (void)viewController:(UIViewController *)viewController 81 | didCompleteLoginWithAccessToken:(id)accessToken 82 | state:(NSString *)state 83 | { 84 | if (_result) { 85 | _result(@{ 86 | @"status" : @"loggedIn", 87 | @"accessToken" : [FlutterAccountKitPlugin formatAccessToken: accessToken] 88 | }); 89 | } 90 | } 91 | 92 | - (void)viewController:(UIViewController *)viewController 93 | didCompleteLoginWithAuthorizationCode:(NSString *)code 94 | state:(NSString *)state 95 | { 96 | if (_result) { 97 | _result(@{ 98 | @"status" : @"loggedIn", 99 | @"code" : code, 100 | }); 101 | } 102 | } 103 | 104 | - (void)viewWillAppear:(BOOL)animated 105 | { 106 | // TODO: analyse if this needs to be implemented here or could be handled in React side 107 | } 108 | 109 | - (void)viewController:(UIViewController *)viewController didFailWithError:(NSError *)error 110 | { 111 | if (_result) { 112 | _result(@{ 113 | @"status" : @"error", 114 | @"errorMessage" : [error description], 115 | }); 116 | } 117 | } 118 | 119 | - (void)viewControllerDidCancel:(UIViewController *)viewController 120 | { 121 | if (_result) { 122 | _result(@{ 123 | @"status" : @"cancelledByUser", 124 | }); 125 | } 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /ios/flutter_account_kit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'flutter_account_kit' 6 | s.version = '0.0.1' 7 | s.summary = 'A Flutter plugin for allowing users to authenticate with native Android & iOS AccountKit SDKs' 8 | s.description = <<-DESC 9 | A new flutter plugin project. 10 | DESC 11 | s.homepage = 'http://example.com' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'Your Company' => 'email@example.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | s.dependency 'AccountKit' 19 | s.static_framework = true 20 | s.ios.deployment_target = '8.0' 21 | end 22 | 23 | -------------------------------------------------------------------------------- /lib/flutter_account_kit.dart: -------------------------------------------------------------------------------- 1 | export 'src/access_token.dart'; 2 | export 'src/account.dart'; 3 | export 'src/account_kit_theme.dart'; 4 | export 'src/config.dart'; 5 | export 'src/login_result.dart'; 6 | export 'src/login_status.dart'; 7 | export 'src/phone_number.dart'; 8 | export 'src/response_type.dart'; 9 | export 'src/title_type.dart'; 10 | export 'src/account_kit.dart'; 11 | -------------------------------------------------------------------------------- /lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | //This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static const GeneratedLocalizationsDelegate delegate = 15 | GeneratedLocalizationsDelegate(); 16 | 17 | static S of(BuildContext context) => Localizations.of(context, S); 18 | 19 | @override 20 | TextDirection get textDirection => TextDirection.ltr; 21 | 22 | } 23 | 24 | class $en extends S { 25 | const $en(); 26 | } 27 | 28 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 29 | const GeneratedLocalizationsDelegate(); 30 | 31 | List get supportedLocales { 32 | return const [ 33 | Locale("en", ""), 34 | ]; 35 | } 36 | 37 | LocaleListResolutionCallback listResolution({Locale fallback}) { 38 | return (List locales, Iterable supported) { 39 | if (locales == null || locales.isEmpty) { 40 | return fallback ?? supported.first; 41 | } else { 42 | return _resolve(locales.first, fallback, supported); 43 | } 44 | }; 45 | } 46 | 47 | LocaleResolutionCallback resolution({Locale fallback}) { 48 | return (Locale locale, Iterable supported) { 49 | return _resolve(locale, fallback, supported); 50 | }; 51 | } 52 | 53 | Locale _resolve(Locale locale, Locale fallback, Iterable supported) { 54 | if (locale == null || !isSupported(locale)) { 55 | return fallback ?? supported.first; 56 | } 57 | 58 | final Locale languageLocale = Locale(locale.languageCode, ""); 59 | if (supported.contains(locale)) { 60 | return locale; 61 | } else if (supported.contains(languageLocale)) { 62 | return languageLocale; 63 | } else { 64 | final Locale fallbackLocale = fallback ?? supported.first; 65 | return fallbackLocale; 66 | } 67 | } 68 | 69 | @override 70 | Future load(Locale locale) { 71 | final String lang = getLang(locale); 72 | if (lang != null) { 73 | switch (lang) { 74 | case "en": 75 | return SynchronousFuture(const $en()); 76 | default: 77 | // NO-OP. 78 | } 79 | } 80 | return SynchronousFuture(const S()); 81 | } 82 | 83 | @override 84 | bool isSupported(Locale locale) => 85 | locale != null && supportedLocales.contains(locale); 86 | 87 | @override 88 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 89 | } 90 | 91 | String getLang(Locale l) => l == null 92 | ? null 93 | : l.countryCode != null && l.countryCode.isEmpty 94 | ? l.languageCode 95 | : l.toString(); 96 | -------------------------------------------------------------------------------- /lib/src/access_token.dart: -------------------------------------------------------------------------------- 1 | /// The access token for using Facebook APIs. 2 | /// 3 | /// Includes the token itself, along with useful metadata about it, such as the 4 | /// associated accountId that the token contains. 5 | class AccessToken { 6 | /// The app id 7 | final String appId; 8 | 9 | /// The access token returned by the Facebook login, which can be used to 10 | /// access Facebook APIs. 11 | final String token; 12 | 13 | /// The id for the accountId that is associated with this access token. 14 | final String accountId; 15 | 16 | /// The time [AccessToken.token] was last refreshed in milliseconds 17 | final int lastRefresh; 18 | 19 | /// The interval between between token refresh in seconds 20 | final int refreshIntervalSeconds; 21 | 22 | /// Constructs a new access token instance from a [Map]. 23 | /// 24 | /// This is used mostly internally by this library, but could be useful if 25 | /// storing the token locally by using the [toMap] method. 26 | AccessToken.fromMap(Map map) 27 | : appId = map['appId'], 28 | token = map['token'], 29 | accountId = map['accountId'], 30 | lastRefresh = map['lastRefresh'], 31 | refreshIntervalSeconds = map['refreshIntervalSeconds']; 32 | 33 | /// Transforms this access token to a [Map]. 34 | /// 35 | /// This could be useful for encoding this access token as JSON and then 36 | /// storing it locally 37 | Map toMap() { 38 | return { 39 | 'appId': appId, 40 | 'token': token, 41 | 'accountId': accountId, 42 | 'lastRefresh': lastRefresh, 43 | 'refreshIntervalSeconds': refreshIntervalSeconds, 44 | }; 45 | } 46 | 47 | @override 48 | bool operator ==(Object other) => 49 | identical(this, other) || 50 | other is AccessToken && 51 | runtimeType == other.runtimeType && 52 | appId == other.appId && 53 | token == other.token && 54 | accountId == other.accountId && 55 | lastRefresh == other.lastRefresh && 56 | refreshIntervalSeconds == other.refreshIntervalSeconds; 57 | 58 | @override 59 | int get hashCode => 60 | appId.hashCode ^ 61 | token.hashCode ^ 62 | accountId.hashCode ^ 63 | lastRefresh.hashCode ^ 64 | refreshIntervalSeconds.hashCode; 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/account.dart: -------------------------------------------------------------------------------- 1 | import 'phone_number.dart'; 2 | 3 | /// The account information of the device. 4 | /// 5 | /// Includes relevant information such as the accountId, phoneNumber or email 6 | class Account { 7 | /// The accountId that is associated with this account. 8 | final String accountId; 9 | 10 | /// The phoneNumber that is associated with this account. 11 | final PhoneNumber phoneNumber; 12 | 13 | /// The email that is associated with this account. 14 | final String email; 15 | 16 | /// Constructs a new account instance from a [Map]. 17 | /// 18 | /// This is used mostly internally by this library, but could be useful if 19 | /// storing the account locally by using the [toMap] method. 20 | Account.fromMap(Map map) 21 | : accountId = map['accountId'], 22 | phoneNumber = map['phoneNumber'] != null 23 | ? new PhoneNumber.fromMap( 24 | map['phoneNumber'].cast(), 25 | ) 26 | : null, 27 | email = map['email']; 28 | 29 | /// Transforms this access token to a [Map]. 30 | /// 31 | /// This could be useful for encoding this account as JSON and then 32 | /// storing it locally 33 | Map toMap() { 34 | return { 35 | 'accountId': accountId, 36 | 'phoneNumber': phoneNumber.toMap(), 37 | 'email': email, 38 | }; 39 | } 40 | 41 | @override 42 | bool operator ==(Object other) => 43 | identical(this, other) || 44 | other is Account && 45 | runtimeType == other.runtimeType && 46 | phoneNumber == other.phoneNumber && 47 | accountId == other.accountId; 48 | 49 | @override 50 | int get hashCode => 51 | accountId.hashCode ^ phoneNumber.hashCode ^ email.hashCode; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/account_kit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_account_kit/src/access_token.dart'; 5 | import 'package:flutter_account_kit/src/account.dart'; 6 | import 'package:flutter_account_kit/src/login_result.dart'; 7 | import 'package:flutter_account_kit/src/config.dart'; 8 | import 'package:flutter_account_kit/src/login_type.dart'; 9 | 10 | /// AccountKit is a plugin for authenticating your users using the native 11 | /// Android & iOS Facebook Accountkit Login SDKs 12 | /// The login methods return a [LoginResult] that contains relevant 13 | /// information about whether the user logged in, cancelled the login dialog, 14 | /// or if the login flow resulted in an error. 15 | /// 16 | /// For example, this sample code illustrates how to handle the different 17 | /// cases: 18 | /// 19 | /// ```dart 20 | /// import 'package:flutter_account_kit/flutter_account_kit.dart'; 21 | /// 22 | /// AccountKit akt = new AccountKit(); 23 | /// LoginResult result = 24 | /// await akt.logInWithPhone(); 25 | /// 26 | /// switch (result.status) { 27 | /// case LoginStatus.loggedIn: 28 | /// _sendTokenToServer(result.accessToken.token); 29 | /// _showLoggedInUI(); 30 | /// break; 31 | /// case LoginStatus.cancelledByUser: 32 | /// _showConvincingMessageOnUI(); 33 | /// break; 34 | /// case LoginStatus.error: 35 | /// _showErrorOnUI(); 36 | /// break; 37 | /// } 38 | ///``` 39 | /// Before using this plugin, some initial setup is required for the Android 40 | /// and iOS clients. See the README for detailed instructions. 41 | class FlutterAccountKit { 42 | static const MethodChannel channel = 43 | const MethodChannel('com.peerwaya/flutter_account_kit'); 44 | 45 | /// The default configuration map 46 | /// 47 | /// see https://developers.facebook.com/docs/accountkit/android/accountkitconfigurationbuilder 48 | static final Config defaultConfig = Config(); 49 | 50 | static String toNativeLoginType(LoginType type) { 51 | switch (type) { 52 | case LoginType.email: 53 | return 'email'; 54 | case LoginType.phone: 55 | return 'phone'; 56 | } 57 | throw new StateError('Invalid accountkit type.'); 58 | } 59 | 60 | /// Enables the client access token flow 61 | /// 62 | /// Set to [ResponseType.token] if the Enable Client Access Token Flow switch in your app's dashboard is ON 63 | /// and [ResponseType.code] if it is OFF. It is set to [ResponseType.token] by default 64 | 65 | /// Sets the accountkit configuration options 66 | /// 67 | /// Options can be set to null. In this case, the default config [FlutterAccountKit.defaultConfig] is used 68 | 69 | Future configure(Config options) async { 70 | var config = options; 71 | if (config == null) { 72 | config = defaultConfig; 73 | } 74 | await channel.invokeMethod('configure', {'configOptions': config.toMap()}); 75 | } 76 | 77 | /// Returns whether the user is currently logged in or not. 78 | /// 79 | /// Convenience method for checking if the [currentAccessToken] is null. 80 | Future get isLoggedIn async => await currentAccessToken != null; 81 | 82 | /// Retrieves the current access token for the application. 83 | /// 84 | /// This could be useful for logging in the user automatically in the case 85 | /// where you don't persist the access token in your Flutter app yourself. 86 | /// 87 | /// For example: 88 | /// 89 | /// ```dart 90 | /// final AccessToken accessToken = await AccountKit.currentAccessToken; 91 | /// 92 | /// if (accessToken != null) { 93 | /// Handle Returning User 94 | /// } else { 95 | /// Handle new or logged out user 96 | /// } 97 | /// ``` 98 | /// 99 | /// If the user is not logged in, this returns null. 100 | Future get currentAccessToken async { 101 | final Map accessToken = 102 | await channel.invokeMethod('getCurrentAccessToken'); 103 | 104 | if (accessToken == null) { 105 | return null; 106 | } 107 | 108 | return AccessToken.fromMap(accessToken.cast()); 109 | } 110 | 111 | /// Retrieves the current account for the application. 112 | /// 113 | /// If you began the login session with [ResponseType.token], 114 | /// it's possible to access the Account Kit ID, phone number and email of 115 | /// the current account via a call to currentAccount. 116 | /// 117 | /// ```dart 118 | /// final Account account = await AccountKit.currentAccount; 119 | /// 120 | /// if (account != null) { 121 | /// Get Account Kit ID 122 | /// String accountKitId = account.accountId; 123 | /// Get phone number 124 | /// PhoneNumber phoneNumber = account.phoneNumber; 125 | /// Get email 126 | /// String email = account.email; 127 | /// } else { 128 | /// Handle null account 129 | /// } 130 | /// ``` 131 | /// 132 | /// If the user is not logged in, this returns null. 133 | Future get currentAccount async { 134 | final Map account = 135 | await channel.invokeMethod('getCurrentAccount'); 136 | 137 | if (account == null) { 138 | return null; 139 | } 140 | 141 | return Account.fromMap(account.cast()); 142 | } 143 | 144 | /// Logs the user in with email. 145 | /// 146 | /// This defaults to using the [ResponseType.token] access flow 147 | /// 148 | /// Returns a [LoginResult] that contains relevant information about 149 | /// the current login status. For sample code, see the [FlutterAccountKit] class- 150 | /// level documentation. 151 | Future logInWithEmail() async { 152 | final Map result = await channel.invokeMethod( 153 | 'login', {'loginType': toNativeLoginType(LoginType.email)}); 154 | 155 | return _deliverResult( 156 | new LoginResult.fromMap(result.cast())); 157 | } 158 | 159 | /// Logs the user in with phone number. 160 | /// 161 | /// This defaults to using the [ResponseType.token] access flow 162 | /// 163 | /// Returns a [LoginResult] that contains relevant information about 164 | /// the current login status. For sample code, see the [FlutterAccountKit] class- 165 | /// level documentation. 166 | Future logInWithPhone() async { 167 | final Map result = await channel.invokeMethod( 168 | 'login', {'loginType': toNativeLoginType(LoginType.phone)}); 169 | 170 | return _deliverResult( 171 | new LoginResult.fromMap(result.cast())); 172 | } 173 | 174 | /// Logs the currently logged in user out. 175 | Future logOut() async => channel.invokeMethod('logOut'); 176 | 177 | /// There's a weird bug where calling Navigator.push (or any similar method) 178 | /// straight after getting a result from the method channel causes the app 179 | /// to hang. 180 | /// 181 | /// As a hack/workaround, we add a new task to the task queue with a slight 182 | /// delay, using the [Future.delayed] constructor. 183 | /// 184 | /// For more context, see this issue: 185 | /// https://github.com/roughike/flutter_facebook_login/issues/14 186 | Future _deliverResult(T result) { 187 | return Future.delayed(const Duration(milliseconds: 500), () => result); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /lib/src/account_kit_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// This class is used to customize the account kit theme. 4 | /// 5 | class AccountKitTheme { 6 | static Map colorToMap(Color color) { 7 | if (color == null) { 8 | return null; 9 | } 10 | return { 11 | 'r': color.red / 0xFF, 12 | 'g': color.green / 0xFF, 13 | 'b': color.blue / 0xFF, 14 | 'a': color.alpha / 0xFF 15 | }; 16 | } 17 | 18 | /// returns the native statusBarStyle 19 | dynamic _toNativeStatusBarStyle() { 20 | if (statusBarStyle == null) { 21 | return null; 22 | } 23 | switch (statusBarStyle) { 24 | case StatusBarStyle.defaultStyle: 25 | return 0; 26 | case StatusBarStyle.lightStyle: 27 | return 1; 28 | } 29 | 30 | throw new StateError('Invalid response type.'); 31 | } 32 | 33 | AccountKitTheme( 34 | {this.backgroundColor, 35 | this.backgroundImage, 36 | this.headerBackgroundColor, 37 | this.headerTextColor, 38 | this.headerButtonTextColor, 39 | this.buttonBackgroundColor, 40 | this.buttonBorderColor, 41 | this.buttonTextColor, 42 | this.buttonDisabledBackgroundColor, 43 | this.buttonDisabledBorderColor, 44 | this.buttonDisabledTextColor, 45 | this.iconColor, 46 | this.inputBackgroundColor, 47 | this.inputBorderColor, 48 | this.inputTextColor, 49 | this.textColor, 50 | this.statusBarStyle, 51 | this.titleColor}); 52 | 53 | /// The name of the background image resource file 54 | String backgroundImage; 55 | 56 | /// The style to use for the status bar 57 | StatusBarStyle statusBarStyle; 58 | 59 | /// Color for the background of the UI if an image is not used 60 | Color backgroundColor; 61 | 62 | /// Color for the header background 63 | Color headerBackgroundColor; 64 | 65 | /// Color for the header text 66 | Color headerTextColor; 67 | 68 | /// Color for the header button text 69 | Color headerButtonTextColor; 70 | 71 | /// Color for the background of the buttons 72 | Color buttonBackgroundColor; 73 | 74 | /// Color for the borders of buttons 75 | Color buttonBorderColor; 76 | 77 | /// Color for the buttonText 78 | Color buttonTextColor; 79 | 80 | /// Color for the disabled background color 81 | Color buttonDisabledBackgroundColor; 82 | 83 | /// Color for the borders of the disabled buttons 84 | Color buttonDisabledBorderColor; 85 | 86 | /// Color for the disabled text on buttons 87 | Color buttonDisabledTextColor; 88 | 89 | /// Color for icons 90 | Color iconColor; 91 | 92 | /// Color for the background of the input boxes. 93 | Color inputBackgroundColor; 94 | 95 | /// Color of the input boxes' border 96 | Color inputBorderColor; 97 | 98 | /// Text color of the input text for Phone Number and Confirmation Code 99 | Color inputTextColor; 100 | 101 | /// Color for text 102 | Color textColor; 103 | 104 | /// Color for title 105 | Color titleColor; 106 | 107 | /// Transforms this access token to a [Map]. 108 | /// 109 | /// This could be useful for encoding this account as JSON and then 110 | /// storing it locally 111 | Map toMap() { 112 | final map = { 113 | 'backgroundColor': colorToMap(backgroundColor), 114 | 'backgroundImage': backgroundImage, 115 | 'headerBackgroundColor': colorToMap(headerBackgroundColor), 116 | 'headerTextColor': colorToMap(headerTextColor), 117 | 'headerButtonTextColor': colorToMap(headerButtonTextColor), 118 | 'buttonBackgroundColor': colorToMap(buttonBackgroundColor), 119 | 'buttonTextColor': colorToMap(buttonTextColor), 120 | 'buttonBorderColor': colorToMap(buttonBorderColor), 121 | 'buttonDisabledBackgroundColor': 122 | colorToMap(buttonDisabledBackgroundColor), 123 | 'buttonDisabledBorderColor': colorToMap(buttonDisabledBorderColor), 124 | 'buttonDisabledTextColor': colorToMap(buttonDisabledTextColor), 125 | 'iconColor': colorToMap(iconColor), 126 | 'inputBackgroundColor': colorToMap(inputBackgroundColor), 127 | 'inputBorderColor': colorToMap(inputBorderColor), 128 | 'inputTextColor': colorToMap(inputTextColor), 129 | 'textColor': colorToMap(textColor), 130 | 'titleColor': colorToMap(titleColor), 131 | 'statusBarStyle': _toNativeStatusBarStyle(), 132 | }; 133 | 134 | /// remove null or empty keys 135 | final keysToRemove = []; 136 | for (String key in map.keys) { 137 | if (map[key] == null) { 138 | keysToRemove.add(key); 139 | } 140 | } 141 | 142 | map.removeWhere((String key, dynamic val) => keysToRemove.contains(key)); 143 | return map; 144 | } 145 | } 146 | 147 | /// The style to use for the status bar 148 | enum StatusBarStyle { 149 | /// Dark status bar 150 | defaultStyle, 151 | 152 | /// Light status bar 153 | lightStyle 154 | } 155 | -------------------------------------------------------------------------------- /lib/src/config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'response_type.dart'; 3 | import 'title_type.dart'; 4 | import 'countries.dart'; 5 | import 'account_kit_theme.dart'; 6 | import 'phone_number.dart'; 7 | 8 | /// Assert all specified country codes are supported. 9 | void assertValidityOfCountryCodes(List countryCodes, String fieldName) { 10 | if (countryCodes == null) { 11 | return; 12 | } 13 | countryCodes.forEach((String countryCode) { 14 | final label = '"$fieldName": Invalid value found.'; 15 | 16 | assert( 17 | countryCode == countryCode.toUpperCase(), 18 | '$label Value should be in uppercase (${countryCode 19 | .toUpperCase()}), "$countryCode" found.'); 20 | 21 | assert(supported_countries.contains(countryCode), 22 | '$label Country code "$countryCode" in "$fieldName" is not supported'); 23 | }); 24 | } 25 | 26 | /// This Configures the flutter accountkit plugin 27 | /// 28 | class Config { 29 | /// returns the [String] representation of the current responseType 30 | String _responseTypeAsString() { 31 | assert(_responseType != null, 'The response type was unexpectedly null.'); 32 | switch (_responseType) { 33 | case ResponseType.token: 34 | return 'token'; 35 | case ResponseType.code: 36 | return 'code'; 37 | } 38 | 39 | throw new StateError('Invalid response type.'); 40 | } 41 | 42 | /// returns the [String] representation of the current [Config.titleType] 43 | String _titleTypeAsString() { 44 | assert(_titleType != null, 'The response type was unexpectedly null.'); 45 | switch (_titleType) { 46 | case TitleType.login: 47 | return 'login'; 48 | case TitleType.appName: 49 | return 'app_name'; 50 | } 51 | 52 | throw new StateError('Invalid response type.'); 53 | } 54 | 55 | Config({ 56 | this.initialAuthState, 57 | this.initialEmail, 58 | this.initialPhoneNumber, 59 | this.facebookNotificationsEnabled, 60 | this.theme, 61 | this.readPhoneStateEnabled, 62 | this.receiveSMS, 63 | ResponseType responseType = ResponseType.token, 64 | TitleType titleType = TitleType.login, 65 | }) : this._responseType = responseType, 66 | this._titleType = titleType; 67 | 68 | /// The response type that determines whether to use access token or authorization code login flow 69 | /// based on the setting in the Facebook developer portal 70 | ResponseType _responseType = ResponseType.token; 71 | 72 | /// The title of the Login Screen 73 | /// 74 | /// Set [TitleType.appName] to use your application's name as the title for the login screen, 75 | /// or [TitleType.login] to use a localized translation of "Login" as the title. 76 | /// 77 | TitleType _titleType = TitleType.login; 78 | 79 | /// A developer-generated nonce used to verify that the received response matches the request 80 | /// 81 | /// Fill this with a random value at runtime; when the login call returns, 82 | /// check that the corresponding param in the response matches the one 83 | /// you set in this method. 84 | String initialAuthState; 85 | 86 | /// Pre-fill the user's email address in the email login flow. 87 | /// 88 | String initialEmail; 89 | 90 | /// Pre-fill the user's phone number in the SMS login flow. 91 | /// 92 | PhoneNumber initialPhoneNumber; 93 | 94 | /// Allows receiving confirmation message via facebook notification 95 | /// 96 | /// If this flag is set, Account Kit offers the user the option to receive their confirmation 97 | /// message via a Facebook notification in the event of an SMS failure, 98 | bool facebookNotificationsEnabled; 99 | 100 | /// Set the [Theme] to use. IOS only 101 | AccountKitTheme theme; 102 | 103 | /// If the READ_PHONE_STATE permission is granted and this flag is true, 104 | /// the app will pre-fill the user's phone number in the SMS login flow 105 | /// 106 | /// Set to false if you wish to use the READ_PHONE_STATE permission yourself, 107 | /// but you do not want the user's phone number pre-filled by Account Kit. 108 | /// 109 | /// Android only 110 | bool readPhoneStateEnabled; 111 | 112 | /// If the RECEIVE_SMS permission is granted and this flag is true, 113 | /// the app will automatically read the Account Kit confirmation SMS 114 | /// and pre-fill the confirmation code in the SMS login flow 115 | bool receiveSMS; 116 | 117 | /// Use this to specify a list of country codes to exclude during the SMS login flow 118 | /// 119 | /// Only the country codes in the blacklist are unavailable. 120 | /// People can still use the rest of Account Kit's supported country codes. 121 | /// If a country code appears in both the whitelist and the blacklist, 122 | /// the blacklist takes precedence and the country code is not available. 123 | /// Just like the whitelist, the value is an array of short country codes as defined by ISO 3166-1 Alpha 2. 124 | List _countryBlacklist; 125 | 126 | /// Use this to specify a list of permitted country codes for use in the SMS login flow 127 | /// 128 | /// The value is an array of short country codes as defined by ISO 3166-1 Alpha 2. 129 | /// To restrict availability to just the US (+1) and 130 | /// The Netherlands (+31), pass in ["US", "NL"]. 131 | List _countryWhitelist; 132 | 133 | /// Set the default country code shown in the SMS login flow. 134 | String _defaultCountry; 135 | 136 | set titleType(TitleType titleType) { 137 | assert(titleType != null, 'The titleType cannot be null.'); 138 | _titleType = titleType; 139 | } 140 | 141 | TitleType get titleType { 142 | return _titleType; 143 | } 144 | 145 | set responseType(ResponseType responseType) { 146 | assert(responseType != null, 'The responseType type cannot be null.'); 147 | _responseType = responseType; 148 | } 149 | 150 | ResponseType get responseType { 151 | return _responseType; 152 | } 153 | 154 | set countryBlacklist(List countryBlacklist) { 155 | assertValidityOfCountryCodes(countryBlacklist, 'countryBlacklist'); 156 | _countryBlacklist = countryBlacklist; 157 | } 158 | 159 | List get countryBlacklist { 160 | return _countryBlacklist; 161 | } 162 | 163 | set countryWhitelist(List countryWhitelist) { 164 | assertValidityOfCountryCodes(countryWhitelist, 'countryWhitelist'); 165 | _countryWhitelist = countryWhitelist; 166 | } 167 | 168 | List get countryWhitelist { 169 | return _countryWhitelist; 170 | } 171 | 172 | set defaultCountry(String defaultCountry) { 173 | assertValidityOfCountryCodes([defaultCountry], 'defaultCountry'); 174 | _defaultCountry = defaultCountry; 175 | } 176 | 177 | String get defaultCountry { 178 | return _defaultCountry; 179 | } 180 | 181 | /// Transforms this access token to a [Map]. 182 | /// 183 | /// This could be useful for encoding this account as JSON and then 184 | /// storing it locally 185 | Map toMap() { 186 | Map map = { 187 | 'initialAuthState': initialAuthState, 188 | 'facebookNotificationsEnabled': facebookNotificationsEnabled, 189 | 'readPhoneStateEnabled': readPhoneStateEnabled, 190 | 'receiveSMS': receiveSMS, 191 | 'defaultCountry': defaultCountry, 192 | 'responseType': _responseTypeAsString(), 193 | 'titleType': _titleTypeAsString(), 194 | 'initialEmail': initialEmail, 195 | 'initialPhoneCountryPrefix': initialPhoneNumber != null ? initialPhoneNumber.countryCode : null, 196 | 'initialPhoneNumber': initialPhoneNumber != null ? initialPhoneNumber.number : null, 197 | }; 198 | if (theme != null) { 199 | map['theme'] = theme.toMap(); 200 | } 201 | 202 | if (countryWhitelist != null && countryWhitelist.isNotEmpty) { 203 | map['countryWhitelist'] = countryWhitelist; 204 | } 205 | 206 | if (countryBlacklist != null && countryBlacklist.isNotEmpty) { 207 | map['countryWhitelist'] = countryBlacklist; 208 | } 209 | 210 | /// remove null or empty keys 211 | final keysToRemove = []; 212 | for (String key in map.keys) { 213 | if (map[key] == null) { 214 | keysToRemove.add(key); 215 | } 216 | } 217 | 218 | map.removeWhere((String key, dynamic val) => keysToRemove.contains(key)); 219 | return map; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /lib/src/countries.dart: -------------------------------------------------------------------------------- 1 | const supported_countries = [ 2 | 'AF', 3 | 'AL', 4 | 'DZ', 5 | 'AS', 6 | 'AD', 7 | 'AO', 8 | 'AI', 9 | 'AG', 10 | 'AR', 11 | 'AM', 12 | 'AW', 13 | 'AU', 14 | 'AT', 15 | 'AZ', 16 | 'BH', 17 | 'BD', 18 | 'BB', 19 | 'BY', 20 | 'BE', 21 | 'BZ', 22 | 'BJ', 23 | 'BM', 24 | 'BT', 25 | 'BO', 26 | 'BQ', 27 | 'BA', 28 | 'BW', 29 | 'BR', 30 | 'IO', 31 | 'VG', 32 | 'BN', 33 | 'BG', 34 | 'BF', 35 | 'BI', 36 | 'KH', 37 | 'CM', 38 | 'CA', 39 | 'CV', 40 | 'KY', 41 | 'CF', 42 | 'TD', 43 | 'CL', 44 | 'CN', 45 | 'CO', 46 | 'KM', 47 | 'CK', 48 | 'CR', 49 | 'CI', 50 | 'HR', 51 | 'CU', 52 | 'CW', 53 | 'CY', 54 | 'CZ', 55 | 'CD', 56 | 'DK', 57 | 'DJ', 58 | 'DM', 59 | 'DO', 60 | 'EC', 61 | 'EG', 62 | 'SV', 63 | 'GQ', 64 | 'ER', 65 | 'EE', 66 | 'ET', 67 | 'FK', 68 | 'FO', 69 | 'FM', 70 | 'FJ', 71 | 'FI', 72 | 'FR', 73 | 'GF', 74 | 'PF', 75 | 'GA', 76 | 'GE', 77 | 'DE', 78 | 'GH', 79 | 'GI', 80 | 'GR', 81 | 'GL', 82 | 'GD', 83 | 'GP', 84 | 'GU', 85 | 'GT', 86 | 'GG', 87 | 'GN', 88 | 'GW', 89 | 'GY', 90 | 'HT', 91 | 'HN', 92 | 'HK', 93 | 'HU', 94 | 'IS', 95 | 'IN', 96 | 'ID', 97 | 'IR', 98 | 'IQ', 99 | 'IE', 100 | 'IM', 101 | 'IL', 102 | 'IT', 103 | 'JM', 104 | 'JP', 105 | 'JE', 106 | 'JO', 107 | 'KZ', 108 | 'KE', 109 | 'KI', 110 | 'KW', 111 | 'KG', 112 | 'LA', 113 | 'LV', 114 | 'LB', 115 | 'LS', 116 | 'LR', 117 | 'LY', 118 | 'LI', 119 | 'LT', 120 | 'LU', 121 | 'MO', 122 | 'MK', 123 | 'MG', 124 | 'MW', 125 | 'MY', 126 | 'MV', 127 | 'ML', 128 | 'MT', 129 | 'MH', 130 | 'MQ', 131 | 'MR', 132 | 'MU', 133 | 'YT', 134 | 'MX', 135 | 'MD', 136 | 'MC', 137 | 'MN', 138 | 'ME', 139 | 'MS', 140 | 'MA', 141 | 'MZ', 142 | 'MM', 143 | 'NA', 144 | 'NR', 145 | 'NP', 146 | 'NL', 147 | 'NC', 148 | 'NZ', 149 | 'NI', 150 | 'NE', 151 | 'NG', 152 | 'NU', 153 | 'NF', 154 | 'KP', 155 | 'MP', 156 | 'NO', 157 | 'OM', 158 | 'PK', 159 | 'PW', 160 | 'PS', 161 | 'PA', 162 | 'PG', 163 | 'PY', 164 | 'PE', 165 | 'PH', 166 | 'PL', 167 | 'PT', 168 | 'PR', 169 | 'QA', 170 | 'CG', 171 | 'RE', 172 | 'RO', 173 | 'RU', 174 | 'RW', 175 | 'SH', 176 | 'KN', 177 | 'PM', 178 | 'VC', 179 | 'WS', 180 | 'SM', 181 | 'ST', 182 | 'SA', 183 | 'SN', 184 | 'RS', 185 | 'SC', 186 | 'SL', 187 | 'SG', 188 | 'SX', 189 | 'SK', 190 | 'SI', 191 | 'SB', 192 | 'SO', 193 | 'ZA', 194 | 'KR', 195 | 'SS', 196 | 'ES', 197 | 'LK', 198 | 'LC', 199 | 'SD', 200 | 'SR', 201 | 'SZ', 202 | 'SE', 203 | 'CH', 204 | 'SY', 205 | 'TW', 206 | 'TJ', 207 | 'TZ', 208 | 'TH', 209 | 'BS', 210 | 'GM', 211 | 'TL', 212 | 'TG', 213 | 'TK', 214 | 'TO', 215 | 'TT', 216 | 'TN', 217 | 'TR', 218 | 'TM', 219 | 'TC', 220 | 'TV', 221 | 'UG', 222 | 'UA', 223 | 'AE', 224 | 'GB', 225 | 'US', 226 | 'UY', 227 | 'VI', 228 | 'UZ', 229 | 'VU', 230 | 'VE', 231 | 'VN', 232 | 'WF', 233 | 'EH', 234 | 'YE', 235 | 'ZM', 236 | ]; 237 | -------------------------------------------------------------------------------- /lib/src/login_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_account_kit/src/access_token.dart'; 2 | import 'login_status.dart'; 3 | 4 | /// The result when the Facebook login flow has completed. 5 | /// 6 | /// The login methods always return an instance of this class, whether the 7 | /// user logged in, cancelled or the login resulted in an error. To handle 8 | /// the different possible scenarios, first see what the [status] is. 9 | /// 10 | /// To see a comprehensive example on how to handle the different login 11 | /// results, see the [FacebookLogin] class-level documentation. 12 | class LoginResult { 13 | /// The status after a Facebook login flow has completed. 14 | /// 15 | /// This affects the [accessToken], [code] and [errorMessage] variables and whether 16 | /// they're available or not. If the user cancelled the login flow, 17 | /// [accessToken], [code] and [errorMessage] are null. 18 | final LoginStatus status; 19 | 20 | /// The access token obtained after the user has 21 | /// successfully logged in. 22 | /// 23 | /// Only available when the [status] equals [LoginStatus.loggedIn] and responseType is [ResponseType.token] 24 | /// otherwise null. 25 | final AccessToken accessToken; 26 | 27 | /// The code obtained after the user has 28 | /// successfully logged in. 29 | /// 30 | /// Only available when the [status] equals [LoginStatus.loggedIn], and responseType is [ResponseType.code], 31 | /// /// otherwise null. 32 | final String code; 33 | 34 | /// The error message when the log in flow completed with an error. 35 | /// 36 | /// Only available when the [status] equals [FacebookLoginStatus.error], 37 | /// otherwise null. 38 | final String errorMessage; 39 | 40 | LoginResult.fromMap(Map map) 41 | : status = _parseStatus(map['status']), 42 | accessToken = map['accessToken'] != null 43 | ? new AccessToken.fromMap( 44 | map['accessToken'].cast(), 45 | ) 46 | : null, 47 | code = map['code'], 48 | errorMessage = map['errorMessage']; 49 | 50 | static LoginStatus _parseStatus(String status) { 51 | switch (status) { 52 | case 'loggedIn': 53 | return LoginStatus.loggedIn; 54 | case 'cancelledByUser': 55 | return LoginStatus.cancelledByUser; 56 | case 'error': 57 | return LoginStatus.error; 58 | } 59 | 60 | throw new StateError('Invalid status: $status'); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/login_status.dart: -------------------------------------------------------------------------------- 1 | /// The status after a Facebook login flow has completed. 2 | enum LoginStatus { 3 | /// The login was successful and the user is now logged in. 4 | loggedIn, 5 | 6 | /// The user cancelled the login flow, usually by closing the Facebook 7 | /// login dialog. 8 | cancelledByUser, 9 | 10 | /// The Facebook login completed with an error and the user couldn't log 11 | /// in for some reason. 12 | error, 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/login_type.dart: -------------------------------------------------------------------------------- 1 | /// Determines which Login flow should be initiated 2 | 3 | enum LoginType { 4 | /// Initiates a SMS login flow 5 | email, 6 | 7 | /// Initiated a phone number login flow 8 | phone 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/phone_number.dart: -------------------------------------------------------------------------------- 1 | /// Describes the phone number associated with the [Account] 2 | class PhoneNumber { 3 | /// The countryCode associated with this phone number 4 | final String countryCode; 5 | 6 | /// The national number associated with this phone number. 7 | final String number; 8 | 9 | const PhoneNumber({this.countryCode, this.number}); 10 | 11 | /// Constructs a new phone number instance from a [Map]. 12 | /// 13 | /// This is used mostly internally by this library, but could be useful if 14 | /// storing the phone number locally by using the [toMap] method. 15 | PhoneNumber.fromMap(Map map) 16 | : countryCode = map['countryCode'], 17 | number = map['number']; 18 | 19 | /// Transforms this phone number to a [Map]. 20 | /// 21 | /// This could be useful for encoding this phone number as JSON and then 22 | /// storing it locally 23 | Map toMap() { 24 | return { 25 | 'countryCode': countryCode, 26 | 'number': number, 27 | }; 28 | } 29 | 30 | @override 31 | bool operator ==(Object other) => 32 | identical(this, other) || 33 | other is PhoneNumber && 34 | runtimeType == other.runtimeType && 35 | countryCode == other.countryCode && 36 | number == other.number; 37 | 38 | @override 39 | int get hashCode => countryCode.hashCode ^ number.hashCode; 40 | 41 | @override 42 | String toString() => '+$countryCode$number'; 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/response_type.dart: -------------------------------------------------------------------------------- 1 | /// Represents your application's authorization setting in the Facebook developer portal dashboard 2 | enum ResponseType { 3 | /// use [token] if the Enable Client Access Token Flow switch in your app's dashboard is ON 4 | token, 5 | 6 | /// use [code] if the Enable Client Access Token Flow switch in your app's dashboard is OFF 7 | code, 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/title_type.dart: -------------------------------------------------------------------------------- 1 | /// Determines the title of the Login screen presented 2 | 3 | enum TitleType { 4 | /// Use your application's name as the title for the login screen 5 | appName, 6 | 7 | /// Usea localized translation of "Login" as the title 8 | login 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_account_kit/src/countries.dart'; 2 | 3 | /// Assert valid types for selected option [propName]. 4 | void assertArray(dynamic prop, String propName) { 5 | assert(prop == null || prop is List, 6 | "'$propName' should be an array, '$prop' given."); 7 | } 8 | 9 | /// Assert valid types for selected option props. 10 | void assertString(dynamic prop, String propName) { 11 | assert(prop == null || prop is String, 12 | "'$propName' should be a String, '$prop' given."); 13 | } 14 | 15 | /// Assert all specified country codes are supported. 16 | void assertValidityOfCountryCodes(Map options) { 17 | final countryBlacklist = options['countryBlacklist']; 18 | final countryWhitelist = options['countryWhitelist']; 19 | final defaultCountry = options['defaultCountry']; 20 | { 21 | "countryBlacklist": countryBlacklist == null ? [] : countryBlacklist, 22 | "countryWhitelist": countryWhitelist == null ? [] : countryWhitelist, 23 | "defaultCountry": defaultCountry == null ? [] : [defaultCountry] 24 | }.forEach((String collectionName, dynamic collection) { 25 | assertArray(collection, collectionName); 26 | collection.forEach((dynamic countryCode) { 27 | final label = '"$collectionName": Invalid value found.'; 28 | 29 | assert(countryCode is String, 30 | '$label Value should be String, "$countryCode" found.'); 31 | 32 | assert( 33 | countryCode == countryCode.toUpperCase(), 34 | '$label Value should be in uppercase (${countryCode 35 | .toUpperCase()}), "$countryCode" found.'); 36 | 37 | assert(supported_countries.contains(countryCode), 38 | '$label Country code "$countryCode" in "$collectionName" is not supported'); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_account_kit 2 | description: A Flutter plugin for allowing users to authenticate with the native Android & iOS AccountKit SDKs 3 | version: 0.7.0 4 | author: Onyemaechi Okafor 5 | homepage: https://github.com/peerwaya/flutter-account-kit 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | 11 | # For information on the generic Dart part of this file, see the 12 | # following page: https://www.dartlang.org/tools/pub/pubspec 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # The following section is specific to Flutter. 19 | flutter: 20 | plugin: 21 | androidPackage: com.peerwaya.flutteraccountkit 22 | pluginClass: FlutterAccountKitPlugin 23 | 24 | environment: 25 | sdk: ">=2.0.0-dev.58.0 <3.0.0" 26 | flutter: ">=0.11.3 <2.0.0" -------------------------------------------------------------------------------- /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/custom_matchers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:flutter_account_kit/src/response_type.dart'; 3 | import 'package:flutter_account_kit/src/title_type.dart'; 4 | 5 | Matcher isConfiguredWithDefaultOptions() { 6 | return isMethodCall( 7 | 'configure', 8 | arguments: { 9 | 'configOptions': { 10 | 'responseType': 'token', 11 | 'titleType': 'login' 12 | }, 13 | }, 14 | ); 15 | } 16 | 17 | Matcher isConfiguredWithTitleType(TitleType type) { 18 | return isMethodCall( 19 | 'configure', 20 | arguments: { 21 | 'configOptions': { 22 | 'responseType': 'token', 23 | 'titleType': type == TitleType.appName ? "app_name" : "login" 24 | }, 25 | }, 26 | ); 27 | } 28 | 29 | Matcher isConfiguredWithResponseType(ResponseType type) { 30 | return isMethodCall( 31 | 'configure', 32 | arguments: { 33 | 'configOptions': { 34 | 'responseType': type == ResponseType.token ? "token" : "code", 35 | 'titleType': 'login' 36 | }, 37 | }, 38 | ); 39 | } 40 | 41 | Matcher isEmailLogin() { 42 | return isMethodCall( 43 | 'login', 44 | arguments: {'loginType': 'email'}, 45 | ); 46 | } 47 | 48 | Matcher isPhoneNumberLogin() { 49 | return isMethodCall( 50 | 'login', 51 | arguments: {'loginType': 'phone'}, 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /test/flutter_account_kit_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_account_kit/flutter_account_kit.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | import 'custom_matchers.dart'; 6 | 7 | void main() { 8 | group("FlutterAccountKit", () { 9 | const MethodChannel channel = const MethodChannel( 10 | 'com.peerwaya/flutter_account_kit', 11 | ); 12 | const countryCode = '234'; 13 | const number = '8090000000'; 14 | 15 | const kPhoneNumber = 16 | const PhoneNumber(countryCode: countryCode, number: number); 17 | 18 | const kPhoneNumberMap = {'countryCode': '234', 'number': '8090000000'}; 19 | 20 | const kAccessToken = { 21 | 'appId': 'test_app_id', 22 | 'accountId': 'test_account_id', 23 | 'token': 'test_token', 24 | 'lastRefresh': 1532080630941, 25 | 'refreshIntervalSeconds': 3600 26 | }; 27 | 28 | const kAccount = { 29 | 'accountId': 'test_account_id', 30 | 'phoneNumber': const {'countryCode': countryCode, 'number': number}, 31 | 'email': 'you@example.com' 32 | }; 33 | 34 | const kLoggedInResponseTypeToken = const { 35 | 'status': 'loggedIn', 36 | 'accessToken': kAccessToken, 37 | }; 38 | 39 | const kLoggedInResponseTypeCode = const { 40 | 'status': 'loggedIn', 41 | 'code': 'test_code', 42 | }; 43 | 44 | const kCancelledByUserResponse = const {'status': 'cancelledByUser'}; 45 | 46 | const kErrorResponse = const { 47 | 'status': 'error', 48 | 'errorMessage': 'test error message', 49 | }; 50 | 51 | final kTheme = AccountKitTheme( 52 | backgroundColor: Color.fromARGB(255, 255, 255, 255), 53 | buttonBackgroundColor: Color.fromARGB(255, 0, 0, 0), 54 | buttonTextColor: Color.fromARGB(255, 255, 255, 255), 55 | inputTextColor: Color.fromARGB(255, 255, 255, 255), 56 | statusBarStyle: StatusBarStyle.lightStyle, 57 | ); 58 | 59 | final kValidConfig = Config() 60 | ..facebookNotificationsEnabled = true 61 | ..receiveSMS = true 62 | ..readPhoneStateEnabled = true 63 | ..initialPhoneNumber = kPhoneNumber 64 | ..defaultCountry = "NG" 65 | ..theme = kTheme; 66 | 67 | final kValidConfigMap = { 68 | 'facebookNotificationsEnabled': true, 69 | 'readPhoneStateEnabled': true, 70 | 'receiveSMS': true, 71 | 'defaultCountry': 'NG', 72 | 'responseType': 'token', 73 | 'titleType': 'login', 74 | 'initialPhoneCountryPrefix': '234', 75 | 'initialPhoneNumber': '8090000000', 76 | 'theme': { 77 | 'backgroundColor': {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0}, 78 | 'buttonBackgroundColor': {'r': 0.0, 'g': 0.0, 'b': 0.0, 'a': 1.0}, 79 | 'buttonTextColor': {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0}, 80 | 'inputTextColor': {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0}, 81 | 'statusBarStyle': 1 82 | }, 83 | }; 84 | 85 | final Config kDefaultConfig = Config(); 86 | 87 | final List log = []; 88 | FlutterAccountKit akt; 89 | 90 | void setMethodCallResponse(Map response) { 91 | channel.setMockMethodCallHandler((MethodCall methodCall) { 92 | log.add(methodCall); 93 | return new Future.value(response); 94 | }); 95 | } 96 | 97 | void expectPhoneNumberParsedCorrectly(PhoneNumber phoneNumber) { 98 | expect(phoneNumber.countryCode, countryCode); 99 | expect(phoneNumber.number, number); 100 | } 101 | 102 | void expectAccessTokenParsedCorrectly(AccessToken accessToken) { 103 | expect(accessToken.token, 'test_token'); 104 | expect(accessToken.accountId, 'test_account_id'); 105 | expect(accessToken.appId, 'test_app_id'); 106 | expect(accessToken.lastRefresh, 1532080630941); 107 | expect(accessToken.refreshIntervalSeconds, 3600); 108 | } 109 | 110 | void expectAccountParsedCorrectly(Account account) { 111 | expect(account.accountId, 'test_account_id'); 112 | expect(account.email, 'you@example.com'); 113 | expectPhoneNumberParsedCorrectly(account.phoneNumber); 114 | } 115 | 116 | setUp(() async { 117 | akt = new FlutterAccountKit(); 118 | log.clear(); 119 | }); 120 | 121 | test('$AccountKitTheme#toMap()', () async { 122 | setMethodCallResponse(null); 123 | final Map map = kTheme.toMap(); 124 | expect( 125 | map, 126 | { 127 | 'backgroundColor': {'r': 1, 'g': 1, 'b': 1, 'a': 1}, 128 | 'buttonBackgroundColor': {'r': 0, 'g': 0, 'b': 0, 'a': 1}, 129 | 'buttonTextColor': {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0}, 130 | 'inputTextColor': {'r': 1.0, 'g': 1.0, 'b': 1.0, 'a': 1.0}, 131 | 'statusBarStyle': 1 132 | }, 133 | ); 134 | }); 135 | 136 | test('$Config#toMap()', () async { 137 | setMethodCallResponse(null); 138 | final Map map = kValidConfig.toMap(); 139 | expect( 140 | map, 141 | kValidConfigMap, 142 | ); 143 | }); 144 | 145 | test('$AccessToken#fromMap()', () async { 146 | setMethodCallResponse(null); 147 | final AccessToken accessToken = AccessToken.fromMap(kAccessToken); 148 | expectAccessTokenParsedCorrectly(accessToken); 149 | }); 150 | 151 | test('$AccessToken#toMap()', () async { 152 | setMethodCallResponse(kLoggedInResponseTypeToken); 153 | 154 | final LoginResult result = await akt.logInWithEmail(); 155 | final Map map = result.accessToken.toMap(); 156 | 157 | expect( 158 | map, 159 | // Just copy-pasting the kAccessToken here. This is just in case; 160 | // we could accidentally make this test non-deterministic. 161 | { 162 | 'appId': 'test_app_id', 163 | 'accountId': 'test_account_id', 164 | 'token': 'test_token', 165 | 'lastRefresh': 1532080630941, 166 | 'refreshIntervalSeconds': 3600 167 | }, 168 | ); 169 | }); 170 | 171 | test('configure - with valid options', () async { 172 | setMethodCallResponse(null); 173 | await akt.configure(kValidConfig); 174 | expect( 175 | log, 176 | contains( 177 | isMethodCall( 178 | 'configure', 179 | arguments: { 180 | 'configOptions': kValidConfigMap, 181 | }, 182 | ), 183 | )); 184 | }); 185 | 186 | test('$Config - invalid blacklist (country code) array', () async { 187 | setMethodCallResponse(null); 188 | expect(() => Config()..countryBlacklist = ['NGN'], throwsAssertionError); 189 | }); 190 | 191 | test('$Config - invalid whitelist (country code) array', () async { 192 | setMethodCallResponse(null); 193 | expect(() => Config()..countryWhitelist = ['NGN'], throwsAssertionError); 194 | }); 195 | 196 | test('$Config - invalid blacklist(lowercase country code) array', () async { 197 | setMethodCallResponse(null); 198 | expect(() => Config()..countryBlacklist = ['ng'], throwsAssertionError); 199 | }); 200 | 201 | test('$Config - invalid whitelist (country code) array', () async { 202 | setMethodCallResponse(null); 203 | expect(() => Config()..countryWhitelist = ['NGN'], throwsAssertionError); 204 | }); 205 | 206 | test('$Config - invalid whitelist (lowercase country code) array', 207 | () async { 208 | setMethodCallResponse(null); 209 | expect(() => Config()..countryWhitelist = ['ng'], throwsAssertionError); 210 | }); 211 | 212 | test('configure - empty black list is removed', () async { 213 | setMethodCallResponse(null); 214 | final config = Config()..countryBlacklist = []; 215 | await akt.configure(config); 216 | expect(log, [ 217 | isMethodCall( 218 | 'configure', 219 | arguments: { 220 | 'configOptions': kDefaultConfig.toMap(), 221 | }, 222 | ), 223 | ]); 224 | }); 225 | 226 | test('configure - empty white list is removed', () async { 227 | setMethodCallResponse(null); 228 | final config = Config()..countryWhitelist = []; 229 | await akt.configure(config); 230 | expect(log, [ 231 | isMethodCall( 232 | 'configure', 233 | arguments: { 234 | 'configOptions': kDefaultConfig.toMap(), 235 | }, 236 | ), 237 | ]); 238 | }); 239 | 240 | test('$Config - null responseType is not allowed', () async { 241 | setMethodCallResponse(null); 242 | // Setting a null responseType is not allowed. 243 | expect(() => Config()..responseType = null, throwsAssertionError); 244 | }); 245 | 246 | test('$Config - null titleType is not allowed', () async { 247 | setMethodCallResponse(null); 248 | // Setting a null titleType is not allowed. 249 | expect(() => Config()..titleType = null, throwsAssertionError); 250 | }); 251 | 252 | test('$AccessToken equality test', () { 253 | final AccessToken first = new AccessToken.fromMap(kAccessToken); 254 | final AccessToken second = new AccessToken.fromMap(kAccessToken); 255 | expect(first, equals(second)); 256 | }); 257 | 258 | test('$Account equality test', () { 259 | final Account first = new Account.fromMap(kAccount); 260 | final Account second = new Account.fromMap(kAccount); 261 | expect(first, equals(second)); 262 | }); 263 | 264 | test('$PhoneNumber equality test', () { 265 | final PhoneNumber first = new PhoneNumber.fromMap(kPhoneNumberMap); 266 | final PhoneNumber second = new PhoneNumber.fromMap(kPhoneNumberMap); 267 | expect(first, equals(second)); 268 | }); 269 | 270 | test('$PhoneNumber#toMap()', () async { 271 | final Map map = kPhoneNumber.toMap(); 272 | expect( 273 | map, 274 | kPhoneNumberMap, 275 | ); 276 | }); 277 | 278 | test('responseType - token is the default', () async { 279 | setMethodCallResponse(kCancelledByUserResponse); 280 | 281 | await akt.logInWithEmail(); 282 | await akt.logInWithPhone(); 283 | 284 | expect( 285 | log, 286 | [ 287 | isMethodCall( 288 | 'login', 289 | arguments: { 290 | 'loginType': 'email', 291 | }, 292 | ), 293 | isMethodCall( 294 | 'login', 295 | arguments: { 296 | 'loginType': 'phone', 297 | }, 298 | ), 299 | ], 300 | ); 301 | }); 302 | 303 | test('responseType - test configure with all options', () async { 304 | final config = Config()..responseType = ResponseType.token; 305 | await akt.configure(config); 306 | 307 | config..responseType = ResponseType.code; 308 | await akt.configure(config); 309 | 310 | expect( 311 | log, 312 | [ 313 | isConfiguredWithResponseType(ResponseType.token), 314 | isConfiguredWithResponseType(ResponseType.code), 315 | ], 316 | ); 317 | }); 318 | 319 | test('titleType - test configure with all options', () async { 320 | final config = Config()..titleType = TitleType.login; 321 | await akt.configure(config); 322 | 323 | config..titleType = TitleType.appName; 324 | await akt.configure(config); 325 | 326 | expect( 327 | log, 328 | [ 329 | isConfiguredWithTitleType(TitleType.login), 330 | isConfiguredWithTitleType(TitleType.appName), 331 | ], 332 | ); 333 | }); 334 | 335 | test('loginWithEmail - user logged in with response type token', () async { 336 | setMethodCallResponse(kLoggedInResponseTypeToken); 337 | 338 | final LoginResult result = await akt.logInWithEmail(); 339 | expect(result.status, LoginStatus.loggedIn); 340 | expectAccessTokenParsedCorrectly(result.accessToken); 341 | 342 | expect( 343 | log, 344 | [ 345 | isMethodCall( 346 | 'login', 347 | arguments: { 348 | 'loginType': 'email', 349 | }, 350 | ), 351 | ], 352 | ); 353 | }); 354 | 355 | test('loginWithEmail - cancelled by user with response type token', 356 | () async { 357 | setMethodCallResponse(kCancelledByUserResponse); 358 | 359 | final LoginResult result = await akt.logInWithEmail(); 360 | 361 | expect(result.status, LoginStatus.cancelledByUser); 362 | expect(result.accessToken, isNull); 363 | }); 364 | 365 | test('loginWithEmail - error with response type token', () async { 366 | setMethodCallResponse(kErrorResponse); 367 | 368 | final LoginResult result = await akt.logInWithEmail(); 369 | 370 | expect(result.status, LoginStatus.error); 371 | expect(result.errorMessage, 'test error message'); 372 | expect(result.accessToken, isNull); 373 | }); 374 | 375 | test('loginWithPhone - user logged in with response type token', () async { 376 | setMethodCallResponse(kLoggedInResponseTypeToken); 377 | 378 | final LoginResult result = await akt.logInWithPhone(); 379 | 380 | expect(result.status, LoginStatus.loggedIn); 381 | expectAccessTokenParsedCorrectly(result.accessToken); 382 | 383 | expect( 384 | log, 385 | [ 386 | isMethodCall( 387 | 'login', 388 | arguments: { 389 | 'loginType': 'phone', 390 | }, 391 | ), 392 | ], 393 | ); 394 | }); 395 | 396 | test('loginWithPhone - cancelled by user with response type token', 397 | () async { 398 | setMethodCallResponse(kCancelledByUserResponse); 399 | 400 | final LoginResult result = await akt.logInWithPhone(); 401 | 402 | expect(result.status, LoginStatus.cancelledByUser); 403 | expect(result.accessToken, isNull); 404 | }); 405 | 406 | test('loginWithPhone - error with response type token', () async { 407 | setMethodCallResponse(kErrorResponse); 408 | 409 | final LoginResult result = await akt.logInWithPhone(); 410 | 411 | expect(result.status, LoginStatus.error); 412 | expect(result.errorMessage, 'test error message'); 413 | expect(result.accessToken, isNull); 414 | }); 415 | 416 | test('loginWithEmail - user logged in with response type code', () async { 417 | setMethodCallResponse(kLoggedInResponseTypeCode); 418 | final config = Config(responseType: ResponseType.code); 419 | await akt.configure(config); 420 | final LoginResult result = await akt.logInWithEmail(); 421 | expect(result.status, LoginStatus.loggedIn); 422 | expect(result.code, 'test_code'); 423 | 424 | expect( 425 | log, 426 | [ 427 | isConfiguredWithResponseType(ResponseType.code), 428 | isMethodCall( 429 | 'login', 430 | arguments: { 431 | 'loginType': 'email', 432 | }, 433 | ), 434 | ], 435 | ); 436 | }); 437 | 438 | test('loginWithEmail - cancelled by user with response type code', 439 | () async { 440 | setMethodCallResponse(kCancelledByUserResponse); 441 | 442 | final config = Config(responseType: ResponseType.code); 443 | await akt.configure(config); 444 | final LoginResult result = await akt.logInWithEmail(); 445 | 446 | expect(result.status, LoginStatus.cancelledByUser); 447 | expect(result.code, isNull); 448 | }); 449 | 450 | test('loginWithEmail - error with response type code', () async { 451 | setMethodCallResponse(kErrorResponse); 452 | final config = Config(responseType: ResponseType.code); 453 | await akt.configure(config); 454 | 455 | final LoginResult result = await akt.logInWithEmail(); 456 | 457 | expect(result.status, LoginStatus.error); 458 | expect(result.errorMessage, 'test error message'); 459 | expect(result.code, isNull); 460 | }); 461 | 462 | test('loginWithPhone - user logged in with response type code', () async { 463 | setMethodCallResponse(kLoggedInResponseTypeCode); 464 | final config = Config(responseType: ResponseType.code); 465 | await akt.configure(config); 466 | 467 | final LoginResult result = await akt.logInWithPhone(); 468 | 469 | expect(result.status, LoginStatus.loggedIn); 470 | expect(result.code, 'test_code'); 471 | 472 | expect( 473 | log, 474 | [ 475 | isConfiguredWithResponseType(ResponseType.code), 476 | isMethodCall( 477 | 'login', 478 | arguments: { 479 | 'loginType': 'phone', 480 | }, 481 | ), 482 | ], 483 | ); 484 | }); 485 | 486 | test('loginWithPhone - cancelled by user with response type code', 487 | () async { 488 | setMethodCallResponse(kCancelledByUserResponse); 489 | final config = Config(responseType: ResponseType.code); 490 | await akt.configure(config); 491 | final LoginResult result = await akt.logInWithPhone(); 492 | 493 | expect(result.status, LoginStatus.cancelledByUser); 494 | expect(result.code, isNull); 495 | }); 496 | 497 | test('loginWithPhone - error with response type code', () async { 498 | setMethodCallResponse(kErrorResponse); 499 | final config = Config(responseType: ResponseType.code); 500 | await akt.configure(config); 501 | final LoginResult result = await akt.logInWithPhone(); 502 | 503 | expect(result.status, LoginStatus.error); 504 | expect(result.errorMessage, 'test error message'); 505 | expect(result.code, isNull); 506 | }); 507 | 508 | test('logOut test', () async { 509 | setMethodCallResponse(null); 510 | 511 | await akt.logOut(); 512 | 513 | expect( 514 | log, 515 | [ 516 | isMethodCall( 517 | 'logOut', 518 | arguments: null, 519 | ), 520 | ], 521 | ); 522 | }); 523 | 524 | test('get isLoggedIn - false when currentAccessToken null', () async { 525 | setMethodCallResponse(null); 526 | 527 | final bool isLoggedIn = await akt.isLoggedIn; 528 | expect(isLoggedIn, isFalse); 529 | }); 530 | 531 | test('get isLoggedIn - true when currentAccessToken is not null', () async { 532 | setMethodCallResponse(kAccessToken); 533 | 534 | final bool isLoggedIn = await akt.isLoggedIn; 535 | expect(isLoggedIn, isTrue); 536 | }); 537 | 538 | test('get currentAccessToken - handles null response gracefully', () async { 539 | setMethodCallResponse(null); 540 | 541 | final AccessToken accessToken = await akt.currentAccessToken; 542 | expect(accessToken, isNull); 543 | }); 544 | 545 | test('get currentAccessToken - when token returned, parses it properly', 546 | () async { 547 | setMethodCallResponse(kAccessToken); 548 | 549 | final AccessToken accessToken = await akt.currentAccessToken; 550 | expectAccessTokenParsedCorrectly(accessToken); 551 | }); 552 | 553 | test('get account - handles null response gracefully', () async { 554 | setMethodCallResponse(null); 555 | 556 | final Account account = await akt.currentAccount; 557 | expect(account, isNull); 558 | }); 559 | 560 | test('get currentAccount - when account returned, parses it properly', 561 | () async { 562 | setMethodCallResponse(kAccount); 563 | 564 | final Account account = await akt.currentAccount; 565 | expectAccountParsedCorrectly(account); 566 | }); 567 | }); 568 | } 569 | --------------------------------------------------------------------------------