├── .gitignore ├── .metadata ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ └── com │ │ │ └── flutterkhi │ │ │ └── app │ │ │ └── flutterpk │ │ │ └── MainActivity.kt │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── feature.png ├── flutterKarachi.png ├── ic_person.png ├── ic_phone_setup.png ├── icon │ └── icon.png ├── loader.png └── map.png ├── data ├── convert.dart └── firebase-export.json ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── caches │ ├── EventDate.dart │ ├── location.dart │ └── user.dart ├── contribution │ └── contribution_dialog.dart ├── dialogs │ └── custom_error_dialog.dart ├── feedback │ └── feedback.dart ├── global.dart ├── helpers │ ├── formatters.dart │ ├── regex-helpers.dart │ └── shared_preferences.dart ├── home │ ├── home_master.dart │ ├── login.dart │ └── onboarding.dart ├── main.dart ├── profile │ └── profile_dialog.dart ├── registration │ └── registration.dart ├── schedule │ ├── model.dart │ ├── schedule_page.dart │ └── session_detail.dart ├── theme.dart ├── util.dart ├── venue_detail.dart ├── venue_model.dart └── widgets │ ├── capsule_text.dart │ ├── custom_app_bar.dart │ ├── dots_indicator.dart │ ├── full_screen_loader.dart │ └── sprung_box.dart ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | **/android/app/google-services.json 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | **/ios/Runner/GoogleService-Info.plist 67 | 68 | 69 | # Exceptions to above rules. 70 | !**/ios/**/default.mode1v3 71 | !**/ios/**/default.mode2v3 72 | !**/ios/**/default.pbxuser 73 | !**/ios/**/default.perspectivev3 74 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 75 | -------------------------------------------------------------------------------- /.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: 4cd87705b7e13783da99356a820728c9d3f95130 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Pakistan App 2 | Flutter Live Extended is organized by Flutter Pakistan community in Karachi and it is based on Flutter Live event. 3 | 4 | This project is the Flutter app for the event. Feel free to fork this repo to customize the app for your own event. 5 | 6 | ## Inspiration 7 | The app takes a lot from [Google I/O Android app](https://github.com/google/iosched) while trimming down the stuff that was not needed. It also adds a few features as described in next section. 8 | 9 | # Features 10 | The app displays a list of sessions with an option to see the details about sessions and speakers. Users can also register for the event using the app and at event day, it lets users mark their attendance using QR code. The app uses Google Sign-in to fetch user details and to enable event registration through the app. 11 | 12 | The app also displays a map of the venue with an option to navigate using the installed maps app. 13 | 14 | # Development Environment 15 | The app is written entirely in Flutter and should compile and run on stable Flutter versions (tested till 1.2). 16 | 17 | ## Firebase 18 | The app makes considerable use of following Firebase components: 19 | * [Cloud Firestore](https://firebase.google.com/docs/firestore/) is the source of all data being shown on the app including session and speaker details, and user registration. 20 | * [Firebase Authentication](https://firebase.google.com/docs/auth/) is used to let the user sign-in using their Google ID. 21 | 22 | ### Setting up 23 | This repo does not contain some required Firebase setup. Follow [this guide](https://firebase.google.com/docs/flutter/setup) to setup Cloud Firestore for this app. There's also a good [video walkthrough](https://www.youtube.com/watch?v=DqJ_KjFzL9I) for setting up. 24 | 25 | ### Importing Firebase data 26 | The app depends on an already setup Firestore data in order to run properly which is present in `/data/firebase-export.json`. To import this data, follow these steps: 27 | 1. Install [Firestore Import/Export utility](https://www.npmjs.com/package/node-firestore-import-export) 28 | 2. [Export your Firebase private key](https://www.npmjs.com/package/node-firestore-import-export#retrieving-google-cloud-account-credentials), it will be automatically downloaded to your computer. Copy this file to the `/data` folder. Rename this file to `firebase-credentials.json`. 29 | 3. Open terminal and go to `/data` folder. 30 | 4. Run the following command 31 | ``` 32 | $ firestore-import -a firebase-credentials.json -b firebase-export.json 33 | ``` 34 | 5. After this command is executed successfully, go to your Database section of your Firebase console to make sure that the data is created there. 35 | 36 | ### Exporting Firebase data 37 | You can also export Firestore data using the following command: 38 | ``` 39 | $ firestore-export -a firebase-credentials.json -b firebase-export.json -p 40 | ``` 41 | This will export the data into `firebase-export.json` file. 42 | 43 | One common use case is to get all users and export them to CSV. There's a Dart script included in the `data` folder and it can be used as shown below: 44 | ``` 45 | $ dart convert.dart firebase-export.json 46 | ``` 47 | This will create an `export.csv` in the same folder which you can then use with Microsoft Excel, Google Sheets etc. 48 | 49 | # Getting started with Flutter 50 | A few resources to get you started if this is your first Flutter project: 51 | 52 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 53 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 54 | 55 | For help getting started with Flutter, view our 56 | [online documentation](https://flutter.io/docs), which offers tutorials, 57 | samples, guidance on mobile development, and a full API reference. 58 | 59 | # Copyright 60 | 61 | ``` 62 | Copyright 2019 Flutter Pakistan. All rights reserved. 63 | 64 | Licensed under the Apache License, Version 2.0 (the "License"); 65 | you may not use this file except in compliance with the License. 66 | You may obtain a copy of the License at 67 | 68 | http://www.apache.org/licenses/LICENSE-2.0 69 | 70 | Unless required by applicable law or agreed to in writing, software 71 | distributed under the License is distributed on an "AS IS" BASIS, 72 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 73 | See the License for the specific language governing permissions and 74 | limitations under the License. 75 | ``` -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.flutterkhi.app.flutterpk" 42 | minSdkVersion 16 43 | targetSdkVersion 27 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | multiDexEnabled true 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | implementation "android.arch.core:runtime:1.1.1" 66 | implementation "android.arch.core:common:1.1.1" 67 | implementation 'com.android.support:multidex:1.0.3' 68 | testImplementation 'junit:junit:4.12' 69 | androidTestImplementation 'androidx.test:runner:1.1.0' 70 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 71 | } 72 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 21 | 28 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/flutterkhi/app/flutterpk/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.flutterkhi.app.flutterpk 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.app.FlutterActivity 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | GeneratedPluginRegistrant.registerWith(this) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.21' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.3.0' 10 | classpath 'com.google.gms:google-services:4.2.0' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true -------------------------------------------------------------------------------- /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-5.0-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /assets/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/feature.png -------------------------------------------------------------------------------- /assets/flutterKarachi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/flutterKarachi.png -------------------------------------------------------------------------------- /assets/ic_person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/ic_person.png -------------------------------------------------------------------------------- /assets/ic_phone_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/ic_phone_setup.png -------------------------------------------------------------------------------- /assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/icon/icon.png -------------------------------------------------------------------------------- /assets/loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/loader.png -------------------------------------------------------------------------------- /assets/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/assets/map.png -------------------------------------------------------------------------------- /data/convert.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | void main(List args) { 5 | var file = File.fromUri(Uri.parse(args[0])); 6 | print('Reading file: ${file.path}'); 7 | var content = file.readAsStringSync(); 8 | var data = json.decode(content); 9 | var users = data['__collections__']['users'] as Map; 10 | print('Total users: ${users.keys.length}'); 11 | 12 | var lines = []; 13 | lines.add( 14 | 'name,email,phone,is_present,is_registered,designation,occupation,institute_work,contribution'); 15 | for (String key in users.keys) { 16 | var user = users[key]; 17 | 18 | if (user['email'] == null) continue; 19 | 20 | var values = [ 21 | user['name'], 22 | user['email'], 23 | user['mobileNumber'], 24 | (user['isPresent'] as bool).toString(), 25 | (user['isRegistered'] as bool).toString(), 26 | user['registration'] == null ? '' : user['registration']['designation'], 27 | user['registration'] == null ? '' : user['registration']['occupation'], 28 | user['registration'] == null 29 | ? '' 30 | : user['registration']['workOrInstitute'], 31 | user['contribution'] == null ? '' : user['contribution'].toString(), 32 | ]; 33 | var line = values.join(','); 34 | print(line); 35 | lines.add(line); 36 | } 37 | 38 | print('Writing ${lines.length} records to file'); 39 | var outFile = File.fromUri(Uri.parse('export.csv')); 40 | outFile.createSync(); 41 | outFile.writeAsStringSync(lines.join('\r\n')); 42 | 43 | print('Done'); 44 | } 45 | -------------------------------------------------------------------------------- /data/firebase-export.json: -------------------------------------------------------------------------------- 1 | { 2 | "__collections__": { 3 | "attendance": { 4 | "16032019": { 5 | "attendanceCount": 0, 6 | "__collections__": { 7 | "attendees": {} 8 | } 9 | } 10 | }, 11 | "dates": { 12 | "WJqsWCMmpTga6K4sbNz0": { 13 | "date": { 14 | "__datatype__": "timestamp", 15 | "value": { 16 | "_seconds": 1552676400, 17 | "_nanoseconds": 0 18 | } 19 | }, 20 | "eventTitle": "Flutter live extended '19", 21 | "venue": { 22 | "location": { 23 | "latitude": 24.86369, 24 | "longitude": 67.075474 25 | }, 26 | "title": "10Pearls", 27 | "city": "Karachi", 28 | "imageUrl": "https://pakvocation.com/wp-content/uploads/2017/12/10PEARLS.png", 29 | "address": "8th floor, Parsa Tower, Main Shahrah-e-Faisal, Karachi" 30 | }, 31 | "__collections__": { 32 | "sessions": { 33 | "Q2a930CWsxrTU3aXjBbK": { 34 | "id": "pairCode", 35 | "speakers": [ 36 | "sana", 37 | "taha" 38 | ], 39 | "startDateTime": { 40 | "__datatype__": "timestamp", 41 | "value": { 42 | "_seconds": 1552717500, 43 | "_nanoseconds": 0 44 | } 45 | }, 46 | "speakerId": "taha", 47 | "title": "Flutter development - Live code", 48 | "endDateTime": { 49 | "__datatype__": "timestamp", 50 | "value": { 51 | "_seconds": 1552719600, 52 | "_nanoseconds": 0 53 | } 54 | }, 55 | "color": "teal", 56 | "description": "A live code action by two Flutter developers, showcasing the delightful development experience while creating a complete application.", 57 | "textColor": "white", 58 | "__collections__": {} 59 | }, 60 | "SDhaXrZZyMrVxLMVtWLM": { 61 | "textColor": "white", 62 | "id": "dart", 63 | "speakers": [ 64 | "waleed" 65 | ], 66 | "startDateTime": { 67 | "__datatype__": "timestamp", 68 | "value": { 69 | "_seconds": 1552714500, 70 | "_nanoseconds": 0 71 | } 72 | }, 73 | "speakerId": "waleed", 74 | "title": "Design with Flutter", 75 | "endDateTime": { 76 | "__datatype__": "timestamp", 77 | "value": { 78 | "_seconds": 1552716900, 79 | "_nanoseconds": 0 80 | } 81 | }, 82 | "color": "blueGrey", 83 | "description": "A talk on how Flutter goes a step ahead in helping designers get along with developers in order to achieve rapid development.", 84 | "__collections__": {} 85 | }, 86 | "ZmOtQvgTqbbpcq0OsT3z": { 87 | "endDateTime": { 88 | "__datatype__": "timestamp", 89 | "value": { 90 | "_seconds": 1552735800, 91 | "_nanoseconds": 0 92 | } 93 | }, 94 | "color": "brown", 95 | "description": "Live code a UI on Flutter. UI challenge will be given and the attendees will be coding the UI then and there!", 96 | "textColor": "white", 97 | "id": "uiChallenge", 98 | "speakers": [ 99 | "auby", 100 | "waleed" 101 | ], 102 | "startDateTime": { 103 | "__datatype__": "timestamp", 104 | "value": { 105 | "_seconds": 1552726800, 106 | "_nanoseconds": 0 107 | } 108 | }, 109 | "speakerId": "waleed", 110 | "title": "Flutter UI challenge", 111 | "__collections__": {} 112 | }, 113 | "jjerkNAOg54TZErlH91p": { 114 | "id": "break", 115 | "startDateTime": { 116 | "__datatype__": "timestamp", 117 | "value": { 118 | "_seconds": 1552723200, 119 | "_nanoseconds": 0 120 | } 121 | }, 122 | "title": "Prayer & refreshments", 123 | "endDateTime": { 124 | "__datatype__": "timestamp", 125 | "value": { 126 | "_seconds": 1552726800, 127 | "_nanoseconds": 0 128 | } 129 | }, 130 | "color": "amber", 131 | "textColor": "black", 132 | "__collections__": {} 133 | }, 134 | "vsTQp4eUDeEMdlYhH6hz": { 135 | "speakers": [ 136 | "waheeda", 137 | "hussain" 138 | ], 139 | "startDateTime": { 140 | "__datatype__": "timestamp", 141 | "value": { 142 | "_seconds": 1552719900, 143 | "_nanoseconds": 0 144 | } 145 | }, 146 | "speakerId": "waheeda", 147 | "title": "CI/CD with Codemagic", 148 | "endDateTime": { 149 | "__datatype__": "timestamp", 150 | "value": { 151 | "_seconds": 1552721700, 152 | "_nanoseconds": 0 153 | } 154 | }, 155 | "color": "lightBlue", 156 | "description": "An introduction and hands on, on how Codemagic helps flutter developers achieve continuous integration and continuous delivery.", 157 | "textColor": "white", 158 | "id": "cicd", 159 | "__collections__": {} 160 | }, 161 | "y9bKNPQoYIxt3zRB31PZ": { 162 | "id": "end", 163 | "speakers": [ 164 | "auby" 165 | ], 166 | "startDateTime": { 167 | "__datatype__": "timestamp", 168 | "value": { 169 | "_seconds": 1552722000, 170 | "_nanoseconds": 0 171 | } 172 | }, 173 | "speakerId": "auby", 174 | "title": "Ending note", 175 | "endDateTime": { 176 | "__datatype__": "timestamp", 177 | "value": { 178 | "_seconds": 1552723200, 179 | "_nanoseconds": 0 180 | } 181 | }, 182 | "color": "indigoAccent", 183 | "description": "Some valuable announcements and updates about the next events.", 184 | "textColor": "white", 185 | "__collections__": {} 186 | }, 187 | "yvAhvc07yrZwjIX9pIdK": { 188 | "textColor": "white", 189 | "id": "keynote", 190 | "speakers": [ 191 | "auby" 192 | ], 193 | "startDateTime": { 194 | "__datatype__": "timestamp", 195 | "value": { 196 | "_seconds": 1552712400, 197 | "_nanoseconds": 0 198 | } 199 | }, 200 | "speakerId": "auby", 201 | "title": "Keynote - introduction", 202 | "endDateTime": { 203 | "__datatype__": "timestamp", 204 | "value": { 205 | "_seconds": 1552714200, 206 | "_nanoseconds": 0 207 | } 208 | }, 209 | "color": "green", 210 | "description": "Learn about the latest product and platform innovations at Flutter Live Extended Keynote led by Asadullah Bin Yousuf.", 211 | "__collections__": {} 212 | } 213 | } 214 | } 215 | } 216 | }, 217 | "speakers": { 218 | "auby": { 219 | "photoUrl": "https://media.licdn.com/dms/image/C5603AQGLlMu1gNs1Vg/profile-displayphoto-shrink_200_200/0?e=1553126400&v=beta&t=0pGeAs9jYDYdyEnwqhwzdMxLECM-RsGVXqVezRbA0BM", 220 | "name": "Asadullah bin Yousuf", 221 | "description": "Asadullah is generally seen as a tech advocate and he has a passion to build high-quality software products. Mobile app is one of his focus areas and he has ample experience for developing on both major platforms i.e. iOS and Android. He is an active member of the global Flutter community and contributes through Medium articles, twitter, and community talks.", 222 | "__collections__": {} 223 | }, 224 | "hussain": { 225 | "photoUrl": "https://media.licdn.com/dms/image/C4E03AQGbcgyRLHMQZQ/profile-displayphoto-shrink_800_800/0?e=1557360000&v=beta&t=VDRNZXn_BqYKP0FWx9T10luqh7n2tT9P_z373QFdHSQ", 226 | "name": "Hussain Marvi", 227 | "description": "Hussain Marvi has been associated with 10Pearls as a Senior Software Developer for 3 years and has an industry experience of more than 5 years. He’s been developing mobile apps using cutting-edge cross-platform mobile technologies like Xamarin, React Native, Flutter and Native Android apps. ", 228 | "__collections__": {} 229 | }, 230 | "sana": { 231 | "description": "Sana Zehra is a software engineer involved in designing and development of software products. She is a lifelong learner and a technology enthusiast; with her experience in various back end technologies and cloud platform, she is involved in developing payment processing products at TPS Pakistan.", 232 | "photoUrl": "https://media.licdn.com/dms/image/C4D03AQHl0iIo5VYg5Q/profile-displayphoto-shrink_800_800/0?e=1556755200&v=beta&t=rkFpZZyQmw1IhM_g5Weu_wl9f3mnwTW2YAo_cTyZU3k", 233 | "name": "Sana Zehra", 234 | "__collections__": {} 235 | }, 236 | "taha": { 237 | "description": "Taha develops leading edge digital financial services based mobile applications at TPS Pakistan. He is a technology enthusiast, has good taste in design and loves problem solving. At present he is beginning to give back to community.", 238 | "photoUrl": "https://farm8.staticflickr.com/7882/46360905165_91bfebb04a_m.jpg", 239 | "name": "Syed Taha Ali", 240 | "__collections__": {} 241 | }, 242 | "waheeda": { 243 | "photoUrl": "https://drive.google.com/file/d/1M_tWrSGdAL9H7egFFHOHql3esEaFnZEB/view", 244 | "name": "Waheeda Ghani", 245 | "description": "Waheeda Ghani is part of the IT industry for more than 10 years. She has worked on Native iOS apps and some cross platform technologies including Xamarin, React Native, and Flutter. She also worked on gaming platforms like Unity 3D and Cocos2d. She is currently working at 10Pearls as a Technical Lead. ", 246 | "__collections__": {} 247 | }, 248 | "waleed": { 249 | "photoUrl": "https://media.licdn.com/dms/image/C4E03AQEjyERQaPjT7g/profile-displayphoto-shrink_800_800/0?e=1557360000&v=beta&t=BRPeTP8fS9Xl7ujmiUqt9-eajcio8C0ixKo5XGTrEVs", 250 | "name": "Waleed Arshad", 251 | "description": "Waleed Arshad is a mobile technologist, a core developer and one of the youngest contributor to Flutter global community in Pakistan. He mainly focuses on developing cross-platform mobile applications for both iOS and Android, and is inclined towards consistently coping up with leading-edge technology orientations. He has been a part of IBM Pakistan as a Cloud Application Developer, and is currently working as a core mobile application developer at TPS Worldwide.", 252 | "__collections__": {} 253 | } 254 | }, 255 | "users": {} 256 | } 257 | } -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | 38 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 39 | # referring to absolute paths on developers' machines. 40 | system('rm -rf .symlinks') 41 | system('mkdir -p .symlinks/plugins') 42 | 43 | # Flutter Pods 44 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 45 | if generated_xcode_build_settings.empty? 46 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first." 47 | end 48 | generated_xcode_build_settings.map { |p| 49 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 50 | symlink = File.join('.symlinks', 'flutter') 51 | File.symlink(File.dirname(p[:path]), symlink) 52 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 53 | end 54 | } 55 | 56 | # Plugin Pods 57 | plugin_pods = parse_KV_file('../.flutter-plugins') 58 | plugin_pods.map { |p| 59 | symlink = File.join('.symlinks', 'plugins', p[:name]) 60 | File.symlink(p[:path], symlink) 61 | pod p[:name], :path => File.join(symlink, 'ios') 62 | } 63 | end 64 | 65 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 66 | install! 'cocoapods', :disable_input_output_paths => true 67 | 68 | post_install do |installer| 69 | installer.pods_project.targets.each do |target| 70 | target.build_configurations.each do |config| 71 | config.build_settings['ENABLE_BITCODE'] = 'NO' 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /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 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 85FCAC6223A27F2300C14861 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 85FCAC6123A27F2300C14861 /* GoogleService-Info.plist */; }; 14 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | EC66248BD24BCCF793EE3784 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC457D08BFA24CCDC94DDA1D /* Pods_Runner.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = ""; 26 | dstSubfolderSpec = 10; 27 | files = ( 28 | ); 29 | name = "Embed Frameworks"; 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXCopyFilesBuildPhase section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 1DEC670CD989438AC1A31068 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 40 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 42 | 85FCAC6123A27F2300C14861 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 43 | 8677228A59CE8584A8902005 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | DEB10806EF1341A259FD8688 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 52 | FC457D08BFA24CCDC94DDA1D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | EC66248BD24BCCF793EE3784 /* Pods_Runner.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 24F8E09D7B1877FC42F22A76 /* Pods */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 1DEC670CD989438AC1A31068 /* Pods-Runner.debug.xcconfig */, 71 | DEB10806EF1341A259FD8688 /* Pods-Runner.release.xcconfig */, 72 | 8677228A59CE8584A8902005 /* Pods-Runner.profile.xcconfig */, 73 | ); 74 | path = Pods; 75 | sourceTree = ""; 76 | }; 77 | 9740EEB11CF90186004384FC /* Flutter */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 81 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 82 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 83 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 84 | ); 85 | name = Flutter; 86 | sourceTree = ""; 87 | }; 88 | 97C146E51CF9000F007C117D = { 89 | isa = PBXGroup; 90 | children = ( 91 | 9740EEB11CF90186004384FC /* Flutter */, 92 | 97C146F01CF9000F007C117D /* Runner */, 93 | 97C146EF1CF9000F007C117D /* Products */, 94 | 24F8E09D7B1877FC42F22A76 /* Pods */, 95 | D70B043E5722EFE37BA9A4D1 /* Frameworks */, 96 | ); 97 | sourceTree = ""; 98 | }; 99 | 97C146EF1CF9000F007C117D /* Products */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 97C146EE1CF9000F007C117D /* Runner.app */, 103 | ); 104 | name = Products; 105 | sourceTree = ""; 106 | }; 107 | 97C146F01CF9000F007C117D /* Runner */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 85FCAC6123A27F2300C14861 /* GoogleService-Info.plist */, 111 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 112 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 113 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 114 | 97C147021CF9000F007C117D /* Info.plist */, 115 | 97C146F11CF9000F007C117D /* Supporting Files */, 116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 118 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 119 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 120 | ); 121 | path = Runner; 122 | sourceTree = ""; 123 | }; 124 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | D70B043E5722EFE37BA9A4D1 /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | FC457D08BFA24CCDC94DDA1D /* Pods_Runner.framework */, 135 | ); 136 | name = Frameworks; 137 | sourceTree = ""; 138 | }; 139 | /* End PBXGroup section */ 140 | 141 | /* Begin PBXNativeTarget section */ 142 | 97C146ED1CF9000F007C117D /* Runner */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 145 | buildPhases = ( 146 | B45E9BA58BEBC4DDC3363DD8 /* [CP] Check Pods Manifest.lock */, 147 | 9740EEB61CF901F6004384FC /* Run Script */, 148 | 97C146EA1CF9000F007C117D /* Sources */, 149 | 97C146EB1CF9000F007C117D /* Frameworks */, 150 | 97C146EC1CF9000F007C117D /* Resources */, 151 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 152 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 153 | 0306BB140847055D6597D110 /* [CP] Embed Pods Frameworks */, 154 | 34645FEE4F10BAE08C0B828E /* [CP] Copy Pods Resources */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = Runner; 161 | productName = Runner; 162 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 163 | productType = "com.apple.product-type.application"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 97C146E61CF9000F007C117D /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastUpgradeCheck = 0910; 172 | ORGANIZATIONNAME = "The Chromium Authors"; 173 | TargetAttributes = { 174 | 97C146ED1CF9000F007C117D = { 175 | CreatedOnToolsVersion = 7.3.1; 176 | LastSwiftMigration = 0910; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = English; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | English, 186 | en, 187 | Base, 188 | ); 189 | mainGroup = 97C146E51CF9000F007C117D; 190 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 191 | projectDirPath = ""; 192 | projectRoot = ""; 193 | targets = ( 194 | 97C146ED1CF9000F007C117D /* Runner */, 195 | ); 196 | }; 197 | /* End PBXProject section */ 198 | 199 | /* Begin PBXResourcesBuildPhase section */ 200 | 97C146EC1CF9000F007C117D /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 205 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 206 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 207 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 208 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 209 | 85FCAC6223A27F2300C14861 /* GoogleService-Info.plist in Resources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXResourcesBuildPhase section */ 214 | 215 | /* Begin PBXShellScriptBuildPhase section */ 216 | 0306BB140847055D6597D110 /* [CP] Embed Pods Frameworks */ = { 217 | isa = PBXShellScriptBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | ); 221 | inputPaths = ( 222 | ); 223 | name = "[CP] Embed Pods Frameworks"; 224 | outputPaths = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | shellPath = /bin/sh; 228 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 229 | showEnvVarsInLog = 0; 230 | }; 231 | 34645FEE4F10BAE08C0B828E /* [CP] Copy Pods Resources */ = { 232 | isa = PBXShellScriptBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | ); 236 | inputPaths = ( 237 | ); 238 | name = "[CP] Copy Pods Resources"; 239 | outputPaths = ( 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | shellPath = /bin/sh; 243 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; 244 | showEnvVarsInLog = 0; 245 | }; 246 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 247 | isa = PBXShellScriptBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | ); 251 | inputPaths = ( 252 | ); 253 | name = "Thin Binary"; 254 | outputPaths = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | shellPath = /bin/sh; 258 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 259 | }; 260 | 9740EEB61CF901F6004384FC /* Run Script */ = { 261 | isa = PBXShellScriptBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | inputPaths = ( 266 | ); 267 | name = "Run Script"; 268 | outputPaths = ( 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | shellPath = /bin/sh; 272 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 273 | }; 274 | B45E9BA58BEBC4DDC3363DD8 /* [CP] Check Pods Manifest.lock */ = { 275 | isa = PBXShellScriptBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | inputFileListPaths = ( 280 | ); 281 | inputPaths = ( 282 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 283 | "${PODS_ROOT}/Manifest.lock", 284 | ); 285 | name = "[CP] Check Pods Manifest.lock"; 286 | outputFileListPaths = ( 287 | ); 288 | outputPaths = ( 289 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | shellPath = /bin/sh; 293 | 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"; 294 | showEnvVarsInLog = 0; 295 | }; 296 | /* End PBXShellScriptBuildPhase section */ 297 | 298 | /* Begin PBXSourcesBuildPhase section */ 299 | 97C146EA1CF9000F007C117D /* Sources */ = { 300 | isa = PBXSourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 304 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | /* End PBXSourcesBuildPhase section */ 309 | 310 | /* Begin PBXVariantGroup section */ 311 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 97C146FB1CF9000F007C117D /* Base */, 315 | ); 316 | name = Main.storyboard; 317 | sourceTree = ""; 318 | }; 319 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 320 | isa = PBXVariantGroup; 321 | children = ( 322 | 97C147001CF9000F007C117D /* Base */, 323 | ); 324 | name = LaunchScreen.storyboard; 325 | sourceTree = ""; 326 | }; 327 | /* End PBXVariantGroup section */ 328 | 329 | /* Begin XCBuildConfiguration section */ 330 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 331 | isa = XCBuildConfiguration; 332 | buildSettings = { 333 | ALWAYS_SEARCH_USER_PATHS = NO; 334 | CLANG_ANALYZER_NONNULL = YES; 335 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 336 | CLANG_CXX_LIBRARY = "libc++"; 337 | CLANG_ENABLE_MODULES = YES; 338 | CLANG_ENABLE_OBJC_ARC = YES; 339 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 340 | CLANG_WARN_BOOL_CONVERSION = YES; 341 | CLANG_WARN_COMMA = YES; 342 | CLANG_WARN_CONSTANT_CONVERSION = YES; 343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 344 | CLANG_WARN_EMPTY_BODY = YES; 345 | CLANG_WARN_ENUM_CONVERSION = YES; 346 | CLANG_WARN_INFINITE_RECURSION = YES; 347 | CLANG_WARN_INT_CONVERSION = YES; 348 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 350 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 351 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 352 | CLANG_WARN_STRICT_PROTOTYPES = YES; 353 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 354 | CLANG_WARN_UNREACHABLE_CODE = YES; 355 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 356 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 357 | COPY_PHASE_STRIP = NO; 358 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 359 | ENABLE_NS_ASSERTIONS = NO; 360 | ENABLE_STRICT_OBJC_MSGSEND = YES; 361 | GCC_C_LANGUAGE_STANDARD = gnu99; 362 | GCC_NO_COMMON_BLOCKS = YES; 363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 365 | GCC_WARN_UNDECLARED_SELECTOR = YES; 366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 367 | GCC_WARN_UNUSED_FUNCTION = YES; 368 | GCC_WARN_UNUSED_VARIABLE = YES; 369 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 370 | MTL_ENABLE_DEBUG_INFO = NO; 371 | SDKROOT = iphoneos; 372 | TARGETED_DEVICE_FAMILY = "1,2"; 373 | VALIDATE_PRODUCT = YES; 374 | }; 375 | name = Profile; 376 | }; 377 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 378 | isa = XCBuildConfiguration; 379 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 380 | buildSettings = { 381 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 382 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 383 | DEVELOPMENT_TEAM = S8QB4VV633; 384 | ENABLE_BITCODE = NO; 385 | FRAMEWORK_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "$(PROJECT_DIR)/Flutter", 388 | ); 389 | INFOPLIST_FILE = Runner/Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 391 | LIBRARY_SEARCH_PATHS = ( 392 | "$(inherited)", 393 | "$(PROJECT_DIR)/Flutter", 394 | ); 395 | PRODUCT_BUNDLE_IDENTIFIER = com.flutterkhi.app.flutterPk; 396 | PRODUCT_NAME = "$(TARGET_NAME)"; 397 | SWIFT_VERSION = 4.0; 398 | VERSIONING_SYSTEM = "apple-generic"; 399 | }; 400 | name = Profile; 401 | }; 402 | 97C147031CF9000F007C117D /* Debug */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | ALWAYS_SEARCH_USER_PATHS = NO; 406 | CLANG_ANALYZER_NONNULL = YES; 407 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 408 | CLANG_CXX_LIBRARY = "libc++"; 409 | CLANG_ENABLE_MODULES = YES; 410 | CLANG_ENABLE_OBJC_ARC = YES; 411 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_COMMA = YES; 414 | CLANG_WARN_CONSTANT_CONVERSION = YES; 415 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 416 | CLANG_WARN_EMPTY_BODY = YES; 417 | CLANG_WARN_ENUM_CONVERSION = YES; 418 | CLANG_WARN_INFINITE_RECURSION = YES; 419 | CLANG_WARN_INT_CONVERSION = YES; 420 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 421 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 422 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 423 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 424 | CLANG_WARN_STRICT_PROTOTYPES = YES; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = dwarf; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | ENABLE_TESTABILITY = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_DYNAMIC_NO_PIC = NO; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_OPTIMIZATION_LEVEL = 0; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 448 | MTL_ENABLE_DEBUG_INFO = YES; 449 | ONLY_ACTIVE_ARCH = YES; 450 | SDKROOT = iphoneos; 451 | TARGETED_DEVICE_FAMILY = "1,2"; 452 | }; 453 | name = Debug; 454 | }; 455 | 97C147041CF9000F007C117D /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ANALYZER_NONNULL = YES; 460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 461 | CLANG_CXX_LIBRARY = "libc++"; 462 | CLANG_ENABLE_MODULES = YES; 463 | CLANG_ENABLE_OBJC_ARC = YES; 464 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 465 | CLANG_WARN_BOOL_CONVERSION = YES; 466 | CLANG_WARN_COMMA = YES; 467 | CLANG_WARN_CONSTANT_CONVERSION = YES; 468 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 469 | CLANG_WARN_EMPTY_BODY = YES; 470 | CLANG_WARN_ENUM_CONVERSION = YES; 471 | CLANG_WARN_INFINITE_RECURSION = YES; 472 | CLANG_WARN_INT_CONVERSION = YES; 473 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 474 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 476 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 477 | CLANG_WARN_STRICT_PROTOTYPES = YES; 478 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 479 | CLANG_WARN_UNREACHABLE_CODE = YES; 480 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 481 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 482 | COPY_PHASE_STRIP = NO; 483 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 484 | ENABLE_NS_ASSERTIONS = NO; 485 | ENABLE_STRICT_OBJC_MSGSEND = YES; 486 | GCC_C_LANGUAGE_STANDARD = gnu99; 487 | GCC_NO_COMMON_BLOCKS = YES; 488 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 489 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 490 | GCC_WARN_UNDECLARED_SELECTOR = YES; 491 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 492 | GCC_WARN_UNUSED_FUNCTION = YES; 493 | GCC_WARN_UNUSED_VARIABLE = YES; 494 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 495 | MTL_ENABLE_DEBUG_INFO = NO; 496 | SDKROOT = iphoneos; 497 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 498 | TARGETED_DEVICE_FAMILY = "1,2"; 499 | VALIDATE_PRODUCT = YES; 500 | }; 501 | name = Release; 502 | }; 503 | 97C147061CF9000F007C117D /* Debug */ = { 504 | isa = XCBuildConfiguration; 505 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 506 | buildSettings = { 507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 508 | CLANG_ENABLE_MODULES = YES; 509 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 510 | ENABLE_BITCODE = NO; 511 | FRAMEWORK_SEARCH_PATHS = ( 512 | "$(inherited)", 513 | "$(PROJECT_DIR)/Flutter", 514 | ); 515 | INFOPLIST_FILE = Runner/Info.plist; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 517 | LIBRARY_SEARCH_PATHS = ( 518 | "$(inherited)", 519 | "$(PROJECT_DIR)/Flutter", 520 | ); 521 | PRODUCT_BUNDLE_IDENTIFIER = com.flutterkhi.app.flutterPk; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 524 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 525 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 526 | SWIFT_VERSION = 4.0; 527 | VERSIONING_SYSTEM = "apple-generic"; 528 | }; 529 | name = Debug; 530 | }; 531 | 97C147071CF9000F007C117D /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 534 | buildSettings = { 535 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 536 | CLANG_ENABLE_MODULES = YES; 537 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 538 | ENABLE_BITCODE = NO; 539 | FRAMEWORK_SEARCH_PATHS = ( 540 | "$(inherited)", 541 | "$(PROJECT_DIR)/Flutter", 542 | ); 543 | INFOPLIST_FILE = Runner/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 545 | LIBRARY_SEARCH_PATHS = ( 546 | "$(inherited)", 547 | "$(PROJECT_DIR)/Flutter", 548 | ); 549 | PRODUCT_BUNDLE_IDENTIFIER = com.flutterkhi.app.flutterPk; 550 | PRODUCT_NAME = "$(TARGET_NAME)"; 551 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 552 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 553 | SWIFT_VERSION = 4.0; 554 | VERSIONING_SYSTEM = "apple-generic"; 555 | }; 556 | name = Release; 557 | }; 558 | /* End XCBuildConfiguration section */ 559 | 560 | /* Begin XCConfigurationList section */ 561 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | 97C147031CF9000F007C117D /* Debug */, 565 | 97C147041CF9000F007C117D /* Release */, 566 | 249021D3217E4FDB00AE95B9 /* Profile */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 97C147061CF9000F007C117D /* Debug */, 575 | 97C147071CF9000F007C117D /* Release */, 576 | 249021D4217E4FDB00AE95B9 /* Profile */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | /* End XCConfigurationList section */ 582 | }; 583 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 584 | } 585 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import GoogleMaps 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | GMSServices.provideAPIKey("YOUR API KEY HERE") 12 | GeneratedPluginRegistrant.register(with: self) 13 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Karachi/flutter_pk/19a5897718ce0fd64601f776e41e590749a0801c/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleURLTypes 8 | 9 | 10 | CFBundleTypeRole 11 | Editor 12 | CFBundleURLSchemes 13 | 14 | 15 | 16 | com.googleusercontent.apps.1023750933650-k9qf3i4loubvia00oeq102c0bv10j4tn 17 | 18 | 19 | 20 | CFBundleExecutable 21 | $(EXECUTABLE_NAME) 22 | CFBundleIdentifier 23 | $(PRODUCT_BUNDLE_IDENTIFIER) 24 | CFBundleInfoDictionaryVersion 25 | 6.0 26 | CFBundleName 27 | flutter_pk 28 | CFBundlePackageType 29 | APPL 30 | CFBundleShortVersionString 31 | $(FLUTTER_BUILD_NAME) 32 | CFBundleSignature 33 | ???? 34 | CFBundleVersion 35 | $(FLUTTER_BUILD_NUMBER) 36 | LSRequiresIPhoneOS 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIMainStoryboardFile 41 | Main 42 | io.flutter.embedded_views_preview 43 | yes 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | UIViewControllerBasedStatusBarAppearance 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/caches/EventDate.dart: -------------------------------------------------------------------------------- 1 | class EventDateTimeCache { 2 | DateTime _eventDateTime; 3 | 4 | DateTime get eventDateTime => _eventDateTime; 5 | 6 | void setDateTime(DateTime eventDateTime) { 7 | _eventDateTime = eventDateTime; 8 | } 9 | 10 | void clear() { 11 | _eventDateTime = null; 12 | } 13 | } -------------------------------------------------------------------------------- /lib/caches/location.dart: -------------------------------------------------------------------------------- 1 | 2 | class LocationCache { 3 | String _longitude; 4 | String _latitude; 5 | 6 | String get longitude => _longitude; 7 | String get latitude => _latitude; 8 | 9 | void setLocation(String longitude, String latitude) { 10 | _longitude = longitude; 11 | _latitude = latitude; 12 | } 13 | 14 | void clear() { 15 | _longitude = null; 16 | _latitude = null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/caches/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | 3 | class UserCache { 4 | InAppUser _user; 5 | 6 | InAppUser get user => _user; 7 | 8 | Future getUser(String id, {bool useCached = true}) async { 9 | if (_user != null && useCached) { 10 | return _user; 11 | } 12 | _user = InAppUser.fromSnapshot( 13 | await FirebaseFirestore.instance.collection('users').document(id).get()); 14 | return _user; 15 | } 16 | 17 | void clear() => _user = null; 18 | } 19 | 20 | class InAppUser { 21 | final String id; 22 | final String name; 23 | final String email; 24 | final String photoUrl; 25 | final String mobileNumber; 26 | final bool isRegistered; 27 | final bool isContributor; 28 | final bool isPresent; 29 | final DocumentReference reference; 30 | 31 | Contribution contribution; 32 | 33 | InAppUser({ 34 | this.name, 35 | this.id, 36 | this.email, 37 | this.reference, 38 | this.photoUrl, 39 | this.isRegistered = false, 40 | this.isContributor = false, 41 | this.isPresent = false, 42 | this.mobileNumber, 43 | }); 44 | 45 | InAppUser.fromMap(Map map, {this.reference}) 46 | : id = map['id'], 47 | name = map['name'], 48 | email = map['email'], 49 | photoUrl = map['photoUrl'], 50 | isRegistered = map['isRegistered'], 51 | isContributor = map['isContributor'], 52 | isPresent = map['isPresent'], 53 | mobileNumber = map['mobileNumber'] { 54 | if (isContributor) contribution = Contribution.fromMap(map['contribution']); 55 | } 56 | 57 | Map toJson() => { 58 | "id": id, 59 | "name": name, 60 | "email": email, 61 | "photoUrl": photoUrl, 62 | "isRegistered": isRegistered, 63 | "mobileNumber": mobileNumber, 64 | "isPresent": isPresent, 65 | "isContributor": isContributor 66 | }; 67 | 68 | InAppUser.fromSnapshot(DocumentSnapshot snapshot) 69 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 70 | } 71 | 72 | class Contribution { 73 | final bool isVolunteer; 74 | final bool isLogisticsAdministrator; 75 | final bool isSpeaker; 76 | final bool isSocialMediaMarketingPerson; 77 | 78 | Contribution({ 79 | this.isSocialMediaMarketingPerson, 80 | this.isLogisticsAdministrator, 81 | this.isSpeaker, 82 | this.isVolunteer, 83 | }); 84 | 85 | Contribution.fromMap(Map map) 86 | : isSpeaker = map['speaker'], 87 | isSocialMediaMarketingPerson = map['socialMediaMarketing'], 88 | isLogisticsAdministrator = map['administrationAndLogistics'], 89 | isVolunteer = map['volunteer']; 90 | 91 | Map toJson() => { 92 | "socialMediaMarketing": isSocialMediaMarketingPerson, 93 | "speaker": isSpeaker, 94 | "administrationAndLogistics": isLogisticsAdministrator, 95 | "volunteer": isVolunteer 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /lib/contribution/contribution_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_pk/global.dart'; 4 | import 'package:rflutter_alert/rflutter_alert.dart'; 5 | 6 | class FullScreenContributionDialog extends StatefulWidget { 7 | @override 8 | FullScreenContributionDialogState createState() { 9 | return new FullScreenContributionDialogState(); 10 | } 11 | } 12 | 13 | class FullScreenContributionDialogState 14 | extends State { 15 | bool _isVolunteer = false; 16 | bool _isLogisticsAdministrator = false; 17 | bool _isSpeaker = false; 18 | bool _isSocialMediaMarketingPerson = false; 19 | bool _isError = false; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Scaffold( 29 | body: SafeArea( 30 | child: Column( 31 | crossAxisAlignment: CrossAxisAlignment.center, 32 | children: [ 33 | _buildCustomAppBarSpace(context), 34 | _buildBody(), 35 | ], 36 | ), 37 | ), 38 | ); 39 | } 40 | 41 | Padding _buildCustomAppBarSpace(BuildContext context) { 42 | return Padding( 43 | padding: const EdgeInsets.only(top: 16.0), 44 | child: Row( 45 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 46 | children: [ 47 | Expanded( 48 | child: FlatButton( 49 | child: Text('SKIP'), 50 | color: Colors.transparent, 51 | textColor: Colors.transparent, 52 | onPressed: () {}, 53 | ), 54 | ), 55 | Text('Community contribution'), 56 | Expanded( 57 | child: Align( 58 | alignment: Alignment.centerRight, 59 | child: FlatButton( 60 | child: Text( 61 | 'SKIP', 62 | style: Theme.of(context).textTheme.subhead.copyWith( 63 | color: Colors.grey, 64 | ), 65 | ), 66 | onPressed: () => Navigator.of(context).pop(), 67 | ), 68 | ), 69 | ) 70 | ], 71 | ), 72 | ); 73 | } 74 | 75 | Widget _buildBody() { 76 | return Expanded( 77 | child: Column( 78 | mainAxisAlignment: MainAxisAlignment.center, 79 | crossAxisAlignment: CrossAxisAlignment.center, 80 | children: [ 81 | _buildGraphic(), 82 | _buildQuestion(), 83 | _buildSelection(), 84 | ], 85 | ), 86 | ); 87 | } 88 | 89 | Padding _buildQuestion() { 90 | return Padding( 91 | padding: const EdgeInsets.all(8.0), 92 | child: Text( 93 | 'In what capacity do you want to contribute to the Flutter community?', 94 | style: Theme.of(context).textTheme.title, 95 | textAlign: TextAlign.center, 96 | ), 97 | ); 98 | } 99 | 100 | Widget _buildSelection() { 101 | return Expanded( 102 | child: ListView( 103 | children: [ 104 | CheckboxListTile( 105 | title: Text('Volunteer'), 106 | value: _isVolunteer, 107 | onChanged: (bool value) { 108 | setState(() => _isError = false); 109 | setState(() => _isVolunteer = value); 110 | }, 111 | ), 112 | CheckboxListTile( 113 | title: Text('Administration & Logistics'), 114 | value: _isLogisticsAdministrator, 115 | onChanged: (bool value) { 116 | setState(() => _isError = false); 117 | setState(() => _isLogisticsAdministrator = value); 118 | }, 119 | ), 120 | CheckboxListTile( 121 | title: Text('Speaker'), 122 | value: _isSpeaker, 123 | onChanged: (bool value) { 124 | setState(() => _isError = false); 125 | setState(() => _isSpeaker = value); 126 | }, 127 | ), 128 | CheckboxListTile( 129 | title: Text('Social media marketing'), 130 | value: _isSocialMediaMarketingPerson, 131 | onChanged: (bool value) { 132 | setState(() => _isError = false); 133 | setState(() => _isSocialMediaMarketingPerson = value); 134 | }, 135 | ), 136 | Padding( 137 | padding: const EdgeInsets.only(right: 8.0), 138 | child: Align( 139 | alignment: Alignment.centerRight, 140 | child: FlatButton( 141 | child: Row( 142 | mainAxisAlignment: MainAxisAlignment.end, 143 | children: [ 144 | Text('CONTINUE'), 145 | Icon( 146 | Icons.arrow_forward, 147 | size: 24.0, 148 | ) 149 | ], 150 | ), 151 | textColor: Theme.of(context).primaryColor, 152 | onPressed: _submitDetails, 153 | ), 154 | ), 155 | ), 156 | Padding( 157 | padding: const EdgeInsets.all(16.0), 158 | child: _isError 159 | ? Text( 160 | 'Please select at least one option. You can also SKIP this step from the top right corner of this screen', 161 | style: TextStyle(color: Colors.red), 162 | textAlign: TextAlign.center, 163 | ) 164 | : Container(), 165 | ), 166 | ], 167 | ), 168 | ); 169 | } 170 | 171 | void _submitDetails() async { 172 | if (!_validate()) return; 173 | try { 174 | FirebaseFirestore.instance.runTransaction((transaction) async { 175 | await transaction.update(userCache.user.reference, { 176 | 'isContributor': true, 177 | 'contribution': { 178 | 'volunteer': _isVolunteer, 179 | 'speaker': _isSpeaker, 180 | 'administrationAndLogistics': _isLogisticsAdministrator, 181 | 'socialMediMarketing': _isSocialMediaMarketingPerson, 182 | } 183 | }); 184 | }); 185 | await userCache.getUser(userCache.user.id, useCached: false); 186 | Navigator.of(context).pop(); 187 | } catch (ex) { 188 | print(ex); 189 | Alert( 190 | context: context, 191 | type: AlertType.error, 192 | title: "Oops!", 193 | desc: "An error has occurred", 194 | buttons: [ 195 | DialogButton( 196 | child: Text("Dismiss", 197 | style: Theme.of(context).textTheme.title.copyWith( 198 | color: Colors.white, 199 | )), 200 | color: Colors.red, 201 | onPressed: () { 202 | Navigator.of(context).pop(); 203 | }, 204 | ) 205 | ], 206 | ).show(); 207 | } 208 | } 209 | 210 | Row _buildGraphic() { 211 | return Row( 212 | mainAxisAlignment: MainAxisAlignment.center, 213 | crossAxisAlignment: CrossAxisAlignment.end, 214 | children: [ 215 | Stack( 216 | children: [ 217 | Center( 218 | child: Icon( 219 | Icons.tab, 220 | color: Theme.of(context).primaryColor, 221 | size: 80.0, 222 | ), 223 | ), 224 | Center( 225 | child: Padding( 226 | padding: const EdgeInsets.only(top: 24.0, left: 16.0), 227 | child: Image( 228 | image: AssetImage( 229 | 'assets/flutterKarachi.png', 230 | ), 231 | height: 40.0, 232 | width: 40.0, 233 | ), 234 | ), 235 | ) 236 | ], 237 | ), 238 | Padding( 239 | padding: const EdgeInsets.only(left: 8.0), 240 | child: Stack( 241 | children: [ 242 | Center( 243 | child: Icon( 244 | Icons.desktop_mac, 245 | color: Theme.of(context).primaryColor, 246 | size: 150.0, 247 | ), 248 | ), 249 | Center( 250 | child: Padding( 251 | padding: const EdgeInsets.only(top: 28.0, left: 42.0), 252 | child: Image( 253 | image: AssetImage( 254 | 'assets/flutterKarachi.png', 255 | ), 256 | height: 60.0, 257 | width: 60.0, 258 | ), 259 | ), 260 | ) 261 | ], 262 | ), 263 | ), 264 | Stack( 265 | children: [ 266 | Center( 267 | child: Icon( 268 | Icons.phone_iphone, 269 | color: Theme.of(context).primaryColor, 270 | size: 80.0, 271 | ), 272 | ), 273 | Center( 274 | child: Padding( 275 | padding: const EdgeInsets.only(top: 16.0, left: 19.0), 276 | child: Image( 277 | image: AssetImage( 278 | 'assets/flutterKarachi.png', 279 | ), 280 | height: 40.0, 281 | width: 40.0, 282 | ), 283 | ), 284 | ) 285 | ], 286 | ), 287 | ], 288 | ); 289 | } 290 | 291 | bool _validate() { 292 | if (!_isSocialMediaMarketingPerson && 293 | !_isSpeaker && 294 | !_isLogisticsAdministrator && 295 | !_isVolunteer) { 296 | setState(() => _isError = true); 297 | return false; 298 | } 299 | 300 | return true; 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /lib/dialogs/custom_error_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void showErrorDialog(String title, String message, BuildContext context) async { 4 | await showDialog( 5 | child: Center( 6 | child: SizedBox( 7 | height: 200.0, 8 | width: MediaQuery.of(context).size.width - 100, 9 | child: Stack( 10 | children: [ 11 | Container( 12 | decoration: BoxDecoration( 13 | color: Colors.transparent, 14 | borderRadius: BorderRadius.circular(10.0), 15 | ), 16 | ), 17 | Center( 18 | child: Stack( 19 | alignment: Alignment.center, 20 | children: [ 21 | Padding( 22 | padding: const EdgeInsets.only(top: 30.0), 23 | child: Container( 24 | decoration: BoxDecoration( 25 | color: Colors.white, 26 | borderRadius: BorderRadius.circular(15.0), 27 | border: Border.all( 28 | color: Theme.of(context).primaryColor)), 29 | child: Padding( 30 | padding: const EdgeInsets.only(top: 40.0), 31 | child: Column( 32 | mainAxisAlignment: MainAxisAlignment.start, 33 | children: [ 34 | Text( 35 | title, 36 | style: Theme.of(context).textTheme.title, 37 | ), 38 | Padding( 39 | padding: const EdgeInsets.only( 40 | top: 24.0, 41 | left: 16.0, 42 | right: 16.0, 43 | ), 44 | child: Center( 45 | child: Text( 46 | message, 47 | style: Theme.of(context).textTheme.subhead, 48 | textAlign: TextAlign.center, 49 | ), 50 | ), 51 | ), 52 | ], 53 | ), 54 | ), 55 | ), 56 | ), 57 | Padding( 58 | padding: const EdgeInsets.only(bottom: 64.0), 59 | child: Align( 60 | alignment: Alignment.topCenter, 61 | child: GestureDetector( 62 | child: CircleAvatar( 63 | backgroundColor: Theme.of(context).primaryColorLight, 64 | foregroundColor: Colors.white, 65 | child: Image(image: AssetImage('assets/flutterKarachi.png'),), 66 | radius: 30.0, 67 | ), 68 | onTap: () => Navigator.of(context).pop(), 69 | ), 70 | ), 71 | ), 72 | ], 73 | ), 74 | ), 75 | ], 76 | ), 77 | ), 78 | ), 79 | context: context, 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /lib/feedback/feedback.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_pk/global.dart'; 4 | import 'package:flutter_pk/schedule/model.dart'; 5 | import 'package:flutter_pk/theme.dart'; 6 | import 'package:flutter_pk/widgets/full_screen_loader.dart'; 7 | import 'package:rflutter_alert/rflutter_alert.dart'; 8 | import 'package:smooth_star_rating/smooth_star_rating.dart'; 9 | 10 | class FullScreenFeedbackDialog extends StatefulWidget { 11 | final Session session; 12 | FullScreenFeedbackDialog({this.session}); 13 | @override 14 | FullScreenFeedbackDialogState createState() { 15 | return FullScreenFeedbackDialogState(); 16 | } 17 | } 18 | 19 | class FullScreenFeedbackDialogState extends State { 20 | double rating = 0.0; 21 | bool _isLoading = false; 22 | @override 23 | Widget build(BuildContext context) { 24 | // TODO: implement build 25 | return Scaffold( 26 | backgroundColor: ColorDictionary.stringToColor[widget.session.color], 27 | body: Stack( 28 | children: [ 29 | SafeArea( 30 | child: Column( 31 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 32 | children: [ 33 | Row( 34 | children: [ 35 | IconButton( 36 | icon: Icon( 37 | Icons.clear, 38 | color: ColorDictionary 39 | .stringToColor[widget.session.textColor], 40 | ), 41 | onPressed: () => Navigator.of(context).pop(), 42 | ), 43 | ], 44 | ), 45 | Expanded( 46 | child: Column( 47 | mainAxisAlignment: MainAxisAlignment.center, 48 | children: [ 49 | Icon( 50 | Icons.rate_review, 51 | size: 100.0, 52 | color: ColorDictionary 53 | .stringToColor[widget.session.textColor], 54 | ), 55 | Padding( 56 | padding: const EdgeInsets.only(left: 16.0, right: 16.0), 57 | child: Text( 58 | 'Your feedback adds value to the quality of upcoming events!', 59 | textAlign: TextAlign.center, 60 | style: TextStyle( 61 | fontSize: 16.0, 62 | color: ColorDictionary 63 | .stringToColor[widget.session.textColor], 64 | ), 65 | ), 66 | ), 67 | Padding( 68 | padding: const EdgeInsets.only(top: 8.0), 69 | child: Text( 70 | 'Rate this session', 71 | textAlign: TextAlign.center, 72 | style: TextStyle( 73 | fontSize: 16.0, 74 | color: ColorDictionary 75 | .stringToColor[widget.session.textColor], 76 | ), 77 | ), 78 | ), 79 | Padding( 80 | padding: const EdgeInsets.all(16.0), 81 | child: Container( 82 | decoration: BoxDecoration( 83 | color: ColorDictionary 84 | .stringToColor[widget.session.textColor], 85 | borderRadius: BorderRadius.circular(10.0)), 86 | child: ListTile( 87 | title: Center( 88 | child: Text( 89 | widget.session.title, 90 | style: TextStyle( 91 | fontWeight: FontWeight.bold, 92 | color: ColorDictionary 93 | .stringToColor[widget.session.color], 94 | ), 95 | ), 96 | ), 97 | ), 98 | ), 99 | ), 100 | SmoothStarRating( 101 | allowHalfRating: true, 102 | onRatingChanged: (value) { 103 | setState(() { 104 | rating = value; 105 | }); 106 | }, 107 | starCount: 5, 108 | rating: rating, 109 | size: 40.0, 110 | color: ColorDictionary 111 | .stringToColor[widget.session.textColor], 112 | borderColor: ColorDictionary 113 | .stringToColor[widget.session.textColor], 114 | ), 115 | Padding( 116 | padding: const EdgeInsets.only(top: 16.0), 117 | child: Align( 118 | alignment: Alignment.centerRight, 119 | child: FlatButton( 120 | child: Row( 121 | mainAxisAlignment: MainAxisAlignment.end, 122 | children: [ 123 | Text( 124 | 'CONTINUE', 125 | style: TextStyle( 126 | color: ColorDictionary.stringToColor[ 127 | widget.session.textColor], 128 | ), 129 | ), 130 | Icon( 131 | Icons.arrow_forward, 132 | size: 24.0, 133 | color: ColorDictionary 134 | .stringToColor[widget.session.textColor], 135 | ) 136 | ], 137 | ), 138 | textColor: Theme.of(context).primaryColor, 139 | onPressed: _submitFeedback, 140 | ), 141 | ), 142 | ) 143 | ], 144 | ), 145 | ) 146 | ], 147 | ), 148 | ), 149 | _isLoading ? FullScreenLoader() : Container() 150 | ], 151 | ), 152 | ); 153 | } 154 | 155 | void _submitFeedback() async { 156 | setState(() => _isLoading = true); 157 | try { 158 | CollectionReference reference = 159 | FirebaseFirestore.instance.collection(FireStoreKeys.userCollection); 160 | await reference.doc(userCache.user.id).setData( 161 | { 162 | 'feedback': {widget.session.id: rating} 163 | }, 164 | SetOptions( 165 | merge: true 166 | ) 167 | ); 168 | Alert( 169 | context: context, 170 | type: AlertType.success, 171 | title: "Thank you!", 172 | desc: "Your feedback has been recorded", 173 | buttons: [ 174 | DialogButton( 175 | child: Text("Cool!", 176 | style: Theme.of(context).textTheme.title.copyWith( 177 | color: Colors.white, 178 | )), 179 | color: ColorDictionary.stringToColor[widget.session.color], 180 | onPressed: () { 181 | Navigator.of(context).pop(); 182 | Navigator.of(context).popUntil( 183 | ModalRoute.withName(Routes.home_master), 184 | ); 185 | }, 186 | ) 187 | ], 188 | ).show(); 189 | } catch (ex) { 190 | print(ex); 191 | Alert( 192 | context: context, 193 | type: AlertType.error, 194 | title: "Oops!", 195 | desc: "An error has occurred", 196 | buttons: [ 197 | DialogButton( 198 | child: Text("Dismiss", 199 | style: Theme.of(context).textTheme.title.copyWith( 200 | color: Colors.white, 201 | )), 202 | color: Colors.red, 203 | onPressed: () { 204 | Navigator.of(context).pop(); 205 | }, 206 | ) 207 | ], 208 | ).show(); 209 | } finally { 210 | setState(() => _isLoading = false); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /lib/global.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_pk/caches/EventDate.dart'; 4 | import 'package:flutter_pk/caches/location.dart'; 5 | import 'package:flutter_pk/caches/user.dart'; 6 | import 'package:google_sign_in/google_sign_in.dart'; 7 | import 'package:shared_preferences/shared_preferences.dart'; 8 | 9 | abstract class Routes { 10 | static const String home_master = '/home_master'; 11 | static const String main = '/main'; 12 | } 13 | 14 | abstract class GlobalConstants { 15 | static const int phoneNumberMaxLength = 13; 16 | static const String breakId = 'break'; 17 | static const int entryMaxLength = 50; 18 | static const String qrKey = "thisisahighlyencryptedaubykhanstringthatisbeingusedforfluttermeetupqrscan"; 19 | static const String addNumberDisplayText = 20 | 'Add your phone number in order to receive event updates.'; 21 | static const String editNumberDisplayText = 22 | 'Looks like you have a number registered against your account. You can use the same number to receive event confirmations or you can update it.'; 23 | } 24 | 25 | abstract class SharedPreferencesKeys { 26 | static const String firebaseUserId = 'uid'; 27 | } 28 | 29 | abstract class FireStoreKeys { 30 | static const String userCollection = 'users'; 31 | static const String dateCollection = 'dates'; 32 | static const String sessionCollection = 'sessions'; 33 | static const String speakerCollection = 'speakers'; 34 | static const String attendanceCollection = 'attendance'; 35 | static const String attendeesCollection = 'attendees'; 36 | static const String dateReferenceString = '16032019'; 37 | } 38 | 39 | final GoogleSignIn googleSignIn = GoogleSignIn(); 40 | final FirebaseAuth auth = FirebaseAuth.instance; 41 | final Future sharedPreferences = 42 | SharedPreferences.getInstance(); 43 | 44 | UserCache userCache = new UserCache(); 45 | 46 | LocationCache locationCache = new LocationCache(); 47 | 48 | EventDateTimeCache eventDateTimeCache = new EventDateTimeCache(); 49 | -------------------------------------------------------------------------------- /lib/helpers/formatters.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | abstract class DateFormats { 4 | static String shortUiDateTimeFormat = 'MMM dd, yyyy, h:mm a'; 5 | static String shortUiDateFormat = 'MMM dd, yyyy'; 6 | static String shortUiTimeFormat = 'h:mm a'; 7 | } 8 | 9 | String formatDate(DateTime date, String format) { 10 | var formatter = new DateFormat(format); 11 | return formatter.format(date); 12 | } -------------------------------------------------------------------------------- /lib/helpers/regex-helpers.dart: -------------------------------------------------------------------------------- 1 | abstract class RegexHelpers { 2 | static RegExp phoneNumberRegex = RegExp(r"^[+][0-9]*$"); 3 | } -------------------------------------------------------------------------------- /lib/helpers/shared_preferences.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_pk/global.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | class SharedPreferencesHandler { 5 | SharedPreferences _prefs; 6 | Future get prefs async { 7 | if (_prefs == null) { 8 | _prefs = await sharedPreferences; 9 | } 10 | 11 | return _prefs; 12 | } 13 | 14 | Future setPreference(String key, String value) async { 15 | await prefs.then((item) { 16 | item.setString(key, value); 17 | }); 18 | } 19 | 20 | Future getValue(String key) async { 21 | return await prefs.then((item) { 22 | return item.get(key); 23 | }); 24 | } 25 | 26 | Future clearPreferences() async { 27 | await prefs.then((item) { 28 | item.clear(); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/home/home_master.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_pk/caches/user.dart'; 7 | import 'package:flutter_pk/venue_detail.dart'; 8 | import 'package:flutter_pk/global.dart'; 9 | import 'package:barcode_scan/barcode_scan.dart'; 10 | import 'package:flutter_pk/registration/registration.dart'; 11 | import 'package:flutter_pk/widgets/full_screen_loader.dart'; 12 | import 'package:url_launcher/url_launcher.dart'; 13 | import 'package:rflutter_alert/rflutter_alert.dart'; 14 | import 'package:flutter_pk/schedule/schedule_page.dart'; 15 | 16 | class HomePageMaster extends StatefulWidget { 17 | @override 18 | HomePageMasterState createState() { 19 | return new HomePageMasterState(); 20 | } 21 | } 22 | 23 | class HomePageMasterState extends State { 24 | int _selectedIndex = 0; 25 | String floatingButtonLabel = 'Register'; 26 | IconData floatingButtonIcon = Icons.group_work; 27 | bool _isLoading = false; 28 | bool _isUserPresent = false; 29 | InAppUser _user = new InAppUser(); 30 | List widgets = [ 31 | SchedulePage(), 32 | Center( 33 | child: Text('Hello two'), 34 | ), 35 | VenueDetailPage() 36 | ]; 37 | 38 | @override 39 | void initState() { 40 | // TODO: implement initState 41 | super.initState(); 42 | _setUser(true); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | // TODO: implement build 48 | return AnnotatedRegion( 49 | value: SystemUiOverlayStyle.dark, 50 | sized: false, 51 | child: Scaffold( 52 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 53 | floatingActionButton: _isLoading 54 | ? null 55 | : FloatingActionButton.extended( 56 | onPressed: _floatingButtonTapModerator, 57 | icon: Icon(floatingButtonIcon), 58 | label: Text(floatingButtonLabel), 59 | ), 60 | body: Stack( 61 | children: [ 62 | widgets.elementAt(_selectedIndex), 63 | _isLoading ? FullScreenLoader() : Container() 64 | ], 65 | ), 66 | bottomNavigationBar: _isLoading 67 | ? null 68 | : BottomNavigationBar( 69 | onTap: (value) { 70 | floatingButtonLabel = 71 | _user.isRegistered ? 'Scan QR' : 'Register'; 72 | floatingButtonIcon = _user.isRegistered 73 | ? Icons.center_focus_weak 74 | : Icons.group_work; 75 | if (value == 2) { 76 | floatingButtonLabel = 'Navigate'; 77 | floatingButtonIcon = Icons.my_location; 78 | } 79 | if (value != 1) 80 | setState(() { 81 | _selectedIndex = value; 82 | }); 83 | }, 84 | currentIndex: _selectedIndex, 85 | items: [ 86 | BottomNavigationBarItem( 87 | icon: Icon(Icons.date_range), title: Text('Schedule')), 88 | BottomNavigationBarItem( 89 | icon: Icon( 90 | Icons.date_range, 91 | color: Colors.transparent, 92 | ), 93 | title: Text(' ')), 94 | BottomNavigationBarItem( 95 | icon: Icon(Icons.location_on), title: Text('Venue')), 96 | ], 97 | ), 98 | ), 99 | ); 100 | } 101 | 102 | void _floatingButtonTapModerator() { 103 | if (_selectedIndex == 2) { 104 | _navigateToGoogleMaps(); 105 | } else if (_user.isRegistered) { 106 | if (!_isUserPresent) { 107 | if (DateTime.now().isBefore(eventDateTimeCache.eventDateTime)) { 108 | Alert( 109 | context: context, 110 | type: AlertType.info, 111 | title: "Information!", 112 | desc: "You will be able to scan a QR on the event day!\nCheers!", 113 | buttons: [ 114 | DialogButton( 115 | child: Text("Cool!", 116 | style: Theme.of(context).textTheme.title.copyWith( 117 | color: Colors.white, 118 | )), 119 | color: Colors.green, 120 | onPressed: () { 121 | Navigator.of(context).pop(); 122 | }, 123 | ) 124 | ], 125 | ).show(); 126 | } else { 127 | _scanQr(); 128 | } 129 | } else { 130 | Alert( 131 | context: context, 132 | type: AlertType.info, 133 | title: "Information!", 134 | desc: "You are already marked present! \nEnjoy the event!", 135 | buttons: [ 136 | DialogButton( 137 | child: Text("Cool!", 138 | style: Theme.of(context).textTheme.title.copyWith( 139 | color: Colors.white, 140 | )), 141 | color: Colors.green, 142 | onPressed: () { 143 | Navigator.of(context).pop(); 144 | }, 145 | ) 146 | ], 147 | ).show(); 148 | } 149 | } else { 150 | _navigateToRegistration(context); 151 | } 152 | } 153 | 154 | void _navigateToGoogleMaps() async { 155 | bool isIOS = Theme.of(context).platform == TargetPlatform.iOS; 156 | String googleUrl = ''; 157 | if (isIOS) { 158 | googleUrl = 159 | 'comgooglemapsurl://maps.google.com/maps?f=d&daddr=${locationCache.latitude},${locationCache.longitude}&sspn=0.2,0.1'; 160 | String appleMapsUrl = 161 | 'https://maps.apple.com/?sll=${locationCache.latitude},${locationCache.longitude}'; 162 | if (await canLaunch("comgooglemaps://")) { 163 | print('launching com googleUrl'); 164 | await launch(googleUrl); 165 | } else if (await canLaunch(appleMapsUrl)) { 166 | print('launching apple url'); 167 | await launch(appleMapsUrl); 168 | } else { 169 | await launch( 170 | 'https://www.google.com/maps/search/?api=1&query=${locationCache.latitude},${locationCache.longitude}'); 171 | } 172 | } else { 173 | googleUrl = 174 | 'google.navigation:q=${locationCache.latitude},${locationCache.longitude}&mode=d'; 175 | if (await canLaunch(googleUrl)) { 176 | await launch(googleUrl); 177 | } else { 178 | await launch( 179 | 'https://www.google.com/maps/search/?api=1&query=${locationCache.latitude},${locationCache.longitude}'); 180 | } 181 | } 182 | } 183 | 184 | Future _scanQr() async { 185 | try { 186 | var qrDataString = await BarcodeScanner.scan(); 187 | print(qrDataString.rawContent); 188 | if (qrDataString.rawContent == GlobalConstants.qrKey) { 189 | setState(() { 190 | _isLoading = true; 191 | }); 192 | DocumentReference attendanceReference = Firestore.instance 193 | .collection(FireStoreKeys.attendanceCollection) 194 | .document(FireStoreKeys.dateReferenceString); 195 | 196 | CollectionReference attendeeCollectionReference = 197 | attendanceReference.collection(FireStoreKeys.attendeesCollection); 198 | 199 | int attendanceCount; 200 | await Firestore.instance 201 | .collection(FireStoreKeys.attendanceCollection) 202 | .document(FireStoreKeys.dateReferenceString) 203 | .get() 204 | .then((onValue) { 205 | attendanceCount = onValue.data()['attendanceCount']; 206 | }); 207 | 208 | await attendanceReference.set( 209 | {'attendanceCount': attendanceCount + 1}, SetOptions(merge: true)); 210 | 211 | await attendeeCollectionReference.doc(userCache.user.id).set( 212 | {'userName': userCache.user.name}, 213 | SetOptions(merge: true), 214 | ); 215 | CollectionReference reference = 216 | Firestore.instance.collection(FireStoreKeys.userCollection); 217 | 218 | await reference 219 | .doc(userCache.user.id) 220 | .set({"isPresent": true}, SetOptions(merge: true)); 221 | _setUser(true); 222 | Alert( 223 | context: context, 224 | type: AlertType.success, 225 | title: "Yayy!", 226 | desc: "You have been marked present! Enjoy the event!", 227 | buttons: [ 228 | DialogButton( 229 | child: Text("Cool!", 230 | style: Theme.of(context).textTheme.title.copyWith( 231 | color: Colors.white, 232 | )), 233 | color: Colors.green, 234 | onPressed: () { 235 | setState(() { 236 | _isUserPresent = true; 237 | }); 238 | Navigator.of(context).pop(); 239 | }, 240 | ) 241 | ], 242 | ).show(); 243 | } else { 244 | Alert( 245 | context: context, 246 | type: AlertType.warning, 247 | title: "Oops!", 248 | desc: "Looks like you scanned an invalid QR", 249 | buttons: [ 250 | DialogButton( 251 | child: Text("Dismiss", 252 | style: Theme.of(context).textTheme.title.copyWith( 253 | color: Colors.white, 254 | )), 255 | color: Colors.blueGrey, 256 | onPressed: () { 257 | Navigator.of(context).pop(); 258 | }, 259 | ) 260 | ], 261 | ).show(); 262 | } 263 | } catch (ex) { 264 | print(ex); 265 | Alert( 266 | context: context, 267 | type: AlertType.error, 268 | title: "Oops!", 269 | desc: "An error has occurred", 270 | buttons: [ 271 | DialogButton( 272 | child: Text("Dismiss", 273 | style: Theme.of(context).textTheme.title.copyWith( 274 | color: Colors.white, 275 | )), 276 | color: Colors.red, 277 | onPressed: () { 278 | Navigator.of(context).pop(); 279 | }, 280 | ) 281 | ], 282 | ).show(); 283 | } finally { 284 | setState(() { 285 | _isLoading = false; 286 | }); 287 | } 288 | } 289 | 290 | Future _navigateToRegistration(BuildContext context) async { 291 | await Navigator.of(context).push( 292 | MaterialPageRoute( 293 | builder: (context) => RegistrationPage(), 294 | fullscreenDialog: true, 295 | ), 296 | ); 297 | var user = await userCache.getUser(userCache.user.id, useCached: false); 298 | setState(() { 299 | _user = user; 300 | }); 301 | _setUser(false); 302 | } 303 | 304 | Future _setUser(bool useCached) async { 305 | var user = await userCache.getUser( 306 | userCache.user.id, 307 | useCached: useCached, 308 | ); 309 | setState(() { 310 | _user = user; 311 | _isUserPresent = user.isPresent; 312 | floatingButtonLabel = _user.isRegistered ? 'Scan QR' : 'Register'; 313 | floatingButtonIcon = 314 | _user.isRegistered ? Icons.center_focus_weak : Icons.group_work; 315 | }); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /lib/home/login.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:flutter_pk/caches/user.dart'; 4 | import 'package:flutter_pk/global.dart'; 5 | import 'package:google_sign_in/google_sign_in.dart'; 6 | 7 | class LoginApi { 8 | Future initiateLogin() async { 9 | var user = await signInWithGoogle(); 10 | 11 | await _setUserToFireStore(user.user); 12 | 13 | return user.user.uid; 14 | } 15 | 16 | Future signInWithGoogle() async { 17 | 18 | // Trigger the authentication flow 19 | final GoogleSignInAccount googleUser = await GoogleSignIn().signIn(); 20 | 21 | // Obtain the auth details from the request 22 | final GoogleSignInAuthentication googleAuth = 23 | await googleUser.authentication; 24 | 25 | // Create a new credential 26 | final GoogleAuthCredential credential = GoogleAuthProvider.credential( 27 | accessToken: googleAuth.accessToken, 28 | idToken: googleAuth.idToken, 29 | ); 30 | 31 | // Once signed in, return the UserCredential 32 | return await FirebaseAuth.instance.signInWithCredential(credential); 33 | } 34 | 35 | Future _setUserToFireStore(User user) async { 36 | CollectionReference reference = 37 | FirebaseFirestore.instance.collection(FireStoreKeys.userCollection); 38 | 39 | await reference.doc(user.uid).get().then((snap) async { 40 | if (!snap.exists) { 41 | InAppUser _user = InAppUser( 42 | name: user.displayName, 43 | mobileNumber: user.phoneNumber, 44 | id: user.uid, 45 | photoUrl: user.photoUrl, 46 | email: user.email); 47 | 48 | await reference 49 | .doc(user.uid) 50 | .set(_user.toJson(), SetOptions(merge: true)); 51 | } 52 | }); 53 | } 54 | 55 | Future _handleGoogleSignIn() async { 56 | GoogleSignInAccount googleUser = await googleSignIn.signIn(); 57 | GoogleSignInAuthentication googleAuth = await googleUser.authentication; 58 | return googleAuth; 59 | } 60 | 61 | // initialize() { 62 | // Firestore.instance.settings( 63 | // // TODO: Update this according to the new implementation 64 | //// timestampsInSnapshotsEnabled: true, 65 | // ); 66 | // } 67 | } 68 | -------------------------------------------------------------------------------- /lib/home/onboarding.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pk/helpers/shared_preferences.dart'; 3 | import 'package:flutter_pk/home/login.dart'; 4 | import 'package:flutter_swiper/flutter_swiper.dart'; 5 | import 'package:rflutter_alert/rflutter_alert.dart'; 6 | import 'package:sprung/sprung.dart'; 7 | 8 | import '../contribution/contribution_dialog.dart'; 9 | import '../global.dart'; 10 | import '../widgets/full_screen_loader.dart'; 11 | import '../widgets/sprung_box.dart'; 12 | 13 | class OnboardingPage extends StatefulWidget { 14 | OnboardingPage({Key key, this.title}) : super(key: key); 15 | final String title; 16 | 17 | @override 18 | _OnboardingPageState createState() => _OnboardingPageState(); 19 | } 20 | 21 | class _OnboardingPageState extends State { 22 | bool _isLoading = false; 23 | bool _showSwipeText = false; 24 | bool _isFetchingSharedPreferences = false; 25 | SharedPreferencesHandler preferences; 26 | 27 | LoginApi api = LoginApi(); 28 | 29 | @override 30 | void initState() { 31 | // TODO: implement initState 32 | super.initState(); 33 | 34 | preferences = SharedPreferencesHandler(); 35 | 36 | _getSharedPreferences(); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Stack( 42 | children: [ 43 | Scaffold( 44 | body: _buildBody(context), 45 | ), 46 | _isLoading ? FullScreenLoader() : Container() 47 | ], 48 | ); 49 | } 50 | 51 | SafeArea _buildBody(BuildContext context) { 52 | return SafeArea( 53 | child: new Swiper.children( 54 | autoplay: false, 55 | loop: false, 56 | physics: _isFetchingSharedPreferences 57 | ? NeverScrollableScrollPhysics() 58 | : ScrollPhysics(), 59 | pagination: _isFetchingSharedPreferences 60 | ? SwiperPagination() 61 | : new SwiperPagination( 62 | margin: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0), 63 | builder: new DotSwiperPaginationBuilder( 64 | color: Theme.of(context).hintColor, 65 | activeColor: Theme.of(context).primaryColor, 66 | size: 10.0, 67 | activeSize: 15.0), 68 | ), 69 | children: [ 70 | _buildFirstSwiperControlPage(context), 71 | _buildSecondSwiperControlPage(context), 72 | ], 73 | ), 74 | ); 75 | } 76 | 77 | Center _buildSecondSwiperControlPage(BuildContext context) { 78 | return Center( 79 | child: Column( 80 | mainAxisAlignment: MainAxisAlignment.spaceAround, 81 | children: [ 82 | Padding( 83 | padding: const EdgeInsets.only(left: 64.0, right: 64.0), 84 | child: _isLoading 85 | ? Container() 86 | : Image( 87 | image: AssetImage('assets/loader.png'), 88 | ), 89 | ), 90 | Column( 91 | children: [ 92 | Text( 93 | 'Register | Attend | Build', 94 | style: Theme.of(context).textTheme.title, 95 | ), 96 | Padding( 97 | padding: 98 | const EdgeInsets.only(top: 32.0, left: 32.0, right: 32.0), 99 | child: Text( 100 | 'Get information about events, their agendas and register yourself as an attendee', 101 | textAlign: TextAlign.center, 102 | ), 103 | ), 104 | Padding( 105 | padding: const EdgeInsets.only(top: 32.0, bottom: 32.0), 106 | child: RaisedButton( 107 | onPressed: _handleSignIn, 108 | textColor: Colors.white, 109 | child: Text('Get started'), 110 | ), 111 | ) 112 | ], 113 | ), 114 | ], 115 | ), 116 | ); 117 | } 118 | 119 | Center _buildFirstSwiperControlPage(BuildContext context) { 120 | return Center( 121 | child: Column( 122 | crossAxisAlignment: CrossAxisAlignment.center, 123 | mainAxisAlignment: MainAxisAlignment.spaceAround, 124 | children: [ 125 | SprungBox( 126 | callback: (bool value) {}, 127 | ), 128 | Column( 129 | children: [ 130 | Text( 131 | 'Welcome to Flutter Pakistan', 132 | style: Theme.of(context).textTheme.title, 133 | ), 134 | AnimatedCrossFade( 135 | crossFadeState: _showSwipeText 136 | ? CrossFadeState.showFirst 137 | : CrossFadeState.showSecond, 138 | duration: Duration(milliseconds: 800), 139 | firstChild: Padding( 140 | padding: const EdgeInsets.only(top: 32.0), 141 | child: Text('Swipe left to proceed'), 142 | ), 143 | secondChild: Padding( 144 | padding: const EdgeInsets.only(top: 32.0), 145 | child: Text( 146 | 'Please wait ...', 147 | ), 148 | ), 149 | ), 150 | ], 151 | ), 152 | ], 153 | ), 154 | ); 155 | } 156 | 157 | Future _handleSignIn() async { 158 | userCache.clear(); 159 | await preferences.clearPreferences(); 160 | 161 | setState(() => _isLoading = true); 162 | 163 | try { 164 | String userId = await api.initiateLogin(); 165 | await preferences.setPreference( 166 | SharedPreferencesKeys.firebaseUserId, userId); 167 | await userCache.getUser(userId); 168 | 169 | if (!userCache.user.isContributor) { 170 | await Navigator.of(context).push( 171 | MaterialPageRoute( 172 | builder: (context) => FullScreenContributionDialog(), 173 | fullscreenDialog: true, 174 | ), 175 | ); 176 | } 177 | 178 | Navigator.of(context).pushNamedAndRemoveUntil( 179 | Routes.home_master, 180 | ModalRoute.withName(Routes.main), 181 | ); 182 | } catch (ex) { 183 | print(ex); 184 | Alert( 185 | context: context, 186 | type: AlertType.error, 187 | title: "Oops!", 188 | desc: "An error has occurred", 189 | buttons: [ 190 | DialogButton( 191 | child: Text("Dismiss", 192 | style: Theme.of(context).textTheme.title.copyWith( 193 | color: Colors.white, 194 | )), 195 | color: Colors.red, 196 | onPressed: () { 197 | Navigator.of(context).pop(); 198 | }, 199 | ) 200 | ], 201 | ).show(); 202 | } finally { 203 | setState(() => _isLoading = false); 204 | } 205 | } 206 | 207 | void _getSharedPreferences() async { 208 | setState(() => _isFetchingSharedPreferences = true); 209 | try { 210 | var userId = await preferences.getValue( 211 | SharedPreferencesKeys.firebaseUserId); 212 | if (userId != null) { 213 | await userCache.getUser(userId); 214 | await Navigator.of(context).pushNamedAndRemoveUntil( 215 | Routes.home_master, 216 | ModalRoute.withName(Routes.main), 217 | ); 218 | } 219 | } catch (ex) { 220 | print(ex); 221 | Alert( 222 | context: context, 223 | type: AlertType.error, 224 | title: "Oops!", 225 | desc: "An error has occurred", 226 | buttons: [ 227 | DialogButton( 228 | child: Text("Dismiss", 229 | style: Theme.of(context).textTheme.title.copyWith( 230 | color: Colors.white, 231 | )), 232 | color: Colors.red, 233 | onPressed: () { 234 | Navigator.of(context).pop(); 235 | }, 236 | ) 237 | ], 238 | ).show(); 239 | } finally { 240 | setState(() { 241 | _isFetchingSharedPreferences = false; 242 | _showSwipeText = true; 243 | }); 244 | } 245 | } 246 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_core/firebase_core.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_pk/global.dart'; 5 | import 'package:flutter_pk/home/onboarding.dart'; 6 | import 'package:flutter_pk/home/home_master.dart'; 7 | import 'package:flutter_pk/theme.dart'; 8 | 9 | void main() async { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | await Firebase.initializeApp(); 12 | runApp(MyApp()); 13 | } 14 | 15 | class MyApp extends StatelessWidget { 16 | @override 17 | Widget build(BuildContext context) { 18 | SystemChrome.setSystemUIOverlayStyle( 19 | SystemUiOverlayStyle.dark, 20 | ); 21 | return MaterialApp( 22 | debugShowCheckedModeBanner: false, 23 | title: 'Flutter Pakistan', 24 | theme: theme, 25 | home: OnboardingPage(title: 'Flutter Pakistan'), 26 | routes: { 27 | Routes.home_master: (context) => new HomePageMaster(), 28 | Routes.main: (context) => OnboardingPage() 29 | }, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/profile/profile_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_pk/caches/user.dart'; 5 | import 'package:flutter_pk/contribution/contribution_dialog.dart'; 6 | import 'package:flutter_pk/global.dart'; 7 | import 'package:flutter_pk/helpers/shared_preferences.dart'; 8 | import 'package:rflutter_alert/rflutter_alert.dart'; 9 | 10 | class FullScreenProfileDialog extends StatefulWidget { 11 | @override 12 | FullScreenProfileDialogState createState() { 13 | return new FullScreenProfileDialogState(); 14 | } 15 | } 16 | 17 | class FullScreenProfileDialogState extends State { 18 | InAppUser _user = new InAppUser(); 19 | SharedPreferencesHandler preferences; 20 | 21 | @override 22 | void initState() { 23 | // TODO: implement initState 24 | super.initState(); 25 | preferences = SharedPreferencesHandler(); 26 | _setUser(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | body: SafeArea( 33 | child: Column( 34 | crossAxisAlignment: CrossAxisAlignment.center, 35 | children: [ 36 | _buildCustomAppBarSpace(context), 37 | _buildBody(), 38 | ], 39 | ), 40 | ), 41 | ); 42 | } 43 | 44 | Padding _buildCustomAppBarSpace(BuildContext context) { 45 | return Padding( 46 | padding: const EdgeInsets.all(8.0), 47 | child: Padding( 48 | padding: const EdgeInsets.only(top: 16.0), 49 | child: Row( 50 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 51 | children: [ 52 | GestureDetector( 53 | child: Icon(Icons.clear), 54 | onTap: () => Navigator.of(context).pop(), 55 | ), 56 | GestureDetector( 57 | child: Text( 58 | 'SIGN OUT', 59 | style: Theme.of(context).textTheme.subhead.copyWith( 60 | color: Theme.of(context).accentColor, 61 | ), 62 | ), 63 | onTap: () async { 64 | try { 65 | await googleSignIn.signOut(); 66 | await auth.signOut(); 67 | preferences.clearPreferences(); 68 | userCache.clear(); 69 | Navigator.of(context).pushNamedAndRemoveUntil( 70 | Routes.main, 71 | ModalRoute.withName(Routes.home_master), 72 | ); 73 | } catch (ex) { 74 | print(ex); 75 | Alert( 76 | context: context, 77 | type: AlertType.error, 78 | title: "Oops!", 79 | desc: "An error has occurred", 80 | buttons: [ 81 | DialogButton( 82 | child: Text("Dismiss", 83 | style: Theme.of(context).textTheme.title.copyWith( 84 | color: Colors.white, 85 | )), 86 | color: Colors.red, 87 | onPressed: () { 88 | Navigator.of(context).pop(); 89 | Navigator.of(context).pop(); 90 | }, 91 | ) 92 | ], 93 | ).show(); 94 | } 95 | }, 96 | ) 97 | ], 98 | ), 99 | ), 100 | ); 101 | } 102 | 103 | Widget _buildBody() { 104 | return Expanded( 105 | child: Column( 106 | crossAxisAlignment: CrossAxisAlignment.center, 107 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 108 | children: [ 109 | Column( 110 | children: [ 111 | Container( 112 | height: 70.0, 113 | width: 70.0, 114 | decoration: new BoxDecoration( 115 | shape: BoxShape.circle, 116 | image: new DecorationImage( 117 | fit: BoxFit.fill, 118 | image: NetworkImage(_user.photoUrl), 119 | ), 120 | ), 121 | ), 122 | Padding( 123 | padding: const EdgeInsets.only(top: 8.0), 124 | child: Text( 125 | _user.name, 126 | style: Theme.of(context) 127 | .textTheme 128 | .title 129 | .copyWith(color: Colors.grey), 130 | ), 131 | ), 132 | Text( 133 | _user.email, 134 | style: Theme.of(context) 135 | .textTheme 136 | .subhead 137 | .copyWith(color: Colors.black38), 138 | ), 139 | ], 140 | ), 141 | Padding( 142 | padding: const EdgeInsets.all(32.0), 143 | child: Text( 144 | 'You can provide session feedback after the event day ends.', 145 | textAlign: TextAlign.center, 146 | ), 147 | ), 148 | Expanded( 149 | child: Column( 150 | mainAxisAlignment: MainAxisAlignment.center, 151 | children: [], 152 | ), 153 | ), 154 | !_user.isContributor 155 | ? ListTile( 156 | title: Center(child: Text('Want to contribute?')), 157 | onTap: () async { 158 | await Navigator.of(context).push( 159 | MaterialPageRoute( 160 | builder: (context) => FullScreenContributionDialog(), 161 | fullscreenDialog: true, 162 | ), 163 | ); 164 | var user = await userCache.getUser( 165 | userCache.user.id, 166 | useCached: false, 167 | ); 168 | Timer(Duration(seconds: 2), () { 169 | setState(() { 170 | _user = user; 171 | }); 172 | _setUser(); 173 | }); 174 | }, 175 | ) 176 | : Container(), 177 | Padding( 178 | padding: const EdgeInsets.only(left: 16.0, right: 16.0), 179 | child: SizedBox( 180 | height: 50.0, 181 | child: Image( 182 | image: AssetImage('assets/feature.png'), 183 | ), 184 | ), 185 | ), 186 | Padding( 187 | padding: const EdgeInsets.all(16.0), 188 | child: Text('Built with Flutter & Material'), 189 | ), 190 | ], 191 | ), 192 | ); 193 | } 194 | 195 | Future _setUser() async { 196 | var user = await userCache.getUser(userCache.user.id); 197 | setState(() { 198 | _user = user; 199 | }); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /lib/registration/registration.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_pk/global.dart'; 4 | import 'package:flutter_pk/helpers/regex-helpers.dart'; 5 | import 'package:flutter_pk/widgets/dots_indicator.dart'; 6 | import 'package:rflutter_alert/rflutter_alert.dart'; 7 | import 'package:flutter_pk/widgets/full_screen_loader.dart'; 8 | 9 | class RegistrationPage extends StatefulWidget { 10 | @override 11 | RegistrationPageState createState() { 12 | return new RegistrationPageState(); 13 | } 14 | } 15 | 16 | class RegistrationPageState extends State { 17 | PageController controller = PageController(); 18 | final GlobalKey _mobileNumberFormKey = new GlobalKey(); 19 | final GlobalKey _studentProfessionalFormKey = 20 | new GlobalKey(); 21 | final GlobalKey _designationFormKey = new GlobalKey(); 22 | FocusNode focusNode = FocusNode(); 23 | TextEditingController mobileNumberController = TextEditingController(); 24 | TextEditingController designationController = TextEditingController(); 25 | TextEditingController studentProfessionalController = TextEditingController(); 26 | int pageViewItemCount = 3; 27 | bool _isStudent = false; 28 | bool _isLoading = false; 29 | 30 | @override 31 | void initState() { 32 | // TODO: implement initState 33 | super.initState(); 34 | mobileNumberController.text = userCache.user.mobileNumber == null 35 | ? '+92' 36 | : userCache.user.mobileNumber; 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | // TODO: implement build 42 | return Stack( 43 | children: [ 44 | Scaffold( 45 | body: SafeArea( 46 | child: Column( 47 | mainAxisAlignment: MainAxisAlignment.start, 48 | crossAxisAlignment: CrossAxisAlignment.center, 49 | children: [ 50 | Row( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | IconButton( 54 | icon: Icon(Icons.clear), 55 | onPressed: () => Navigator.of(context).pop(), 56 | ), 57 | Text( 58 | 'Registration', 59 | style: Theme.of(context).textTheme.title, 60 | ), 61 | SizedBox(width: 48), 62 | ], 63 | ), 64 | Expanded( 65 | child: PageView( 66 | controller: controller, 67 | children: [ 68 | userCache.user.mobileNumber == null 69 | ? _buildNumberSetupView( 70 | context, 71 | GlobalConstants.addNumberDisplayText, 72 | ) 73 | : _buildNumberSetupView( 74 | context, 75 | GlobalConstants.editNumberDisplayText, 76 | ), 77 | _buildStudentProfessionalView(), 78 | _buildWorkInstituteEntryView(), 79 | _buildDesignationEntryView() 80 | ], 81 | physics: NeverScrollableScrollPhysics(), 82 | scrollDirection: Axis.horizontal, 83 | ), 84 | ), 85 | DotsIndicator( 86 | controller: controller, 87 | itemCount: pageViewItemCount, 88 | activeColor: Theme.of(context).primaryColor, 89 | inactiveColor: Colors.grey, 90 | ), 91 | ], 92 | ), 93 | ), 94 | ), 95 | _isLoading ? FullScreenLoader() : Container() 96 | ], 97 | ); 98 | } 99 | 100 | Widget _buildNumberSetupView(BuildContext context, String displayText) { 101 | return Center( 102 | child: SingleChildScrollView( 103 | child: Column( 104 | mainAxisSize: MainAxisSize.min, 105 | mainAxisAlignment: MainAxisAlignment.center, 106 | children: [ 107 | Padding( 108 | padding: const EdgeInsets.only(left: 128.0, right: 128.0), 109 | child: Center( 110 | child: Image( 111 | image: AssetImage('assets/ic_phone_setup.png'), 112 | color: Theme.of(context).primaryColor, 113 | width: 120, 114 | ), 115 | ), 116 | ), 117 | Padding( 118 | padding: const EdgeInsets.only( 119 | top: 16.0, 120 | bottom: 16.0, 121 | left: 32.0, 122 | right: 32.0, 123 | ), 124 | child: Text( 125 | displayText, 126 | textAlign: TextAlign.center, 127 | style: Theme.of(context).textTheme.subhead, 128 | ), 129 | ), 130 | Form( 131 | key: _mobileNumberFormKey, 132 | child: ListTile( 133 | title: TextFormField( 134 | focusNode: focusNode, 135 | controller: mobileNumberController, 136 | maxLength: GlobalConstants.phoneNumberMaxLength, 137 | validator: (value) => _validatePhoneNumber(value), 138 | keyboardType: TextInputType.phone, 139 | decoration: InputDecoration( 140 | border: OutlineInputBorder( 141 | borderRadius: BorderRadius.circular(10.0)), 142 | hintText: 'Enter mobile number', 143 | labelText: 'Mobile number'), 144 | ), 145 | ), 146 | ), 147 | Divider(), 148 | Row( 149 | children: [ 150 | Expanded( 151 | child: FlatButton( 152 | child: Row( 153 | mainAxisAlignment: MainAxisAlignment.end, 154 | children: [ 155 | Text('NEXT'), 156 | Icon( 157 | Icons.arrow_forward, 158 | size: 24.0, 159 | ) 160 | ], 161 | ), 162 | textColor: Theme.of(context).primaryColor, 163 | onPressed: () { 164 | if (_mobileNumberFormKey.currentState.validate()) { 165 | focusNode.unfocus(); 166 | controller.animateToPage(1, 167 | duration: Duration(milliseconds: 500), 168 | curve: Curves.fastOutSlowIn); 169 | } 170 | }, 171 | ), 172 | ) 173 | ], 174 | ) 175 | ], 176 | ), 177 | ), 178 | ); 179 | } 180 | 181 | Center _buildStudentProfessionalView() { 182 | return Center( 183 | child: SingleChildScrollView( 184 | child: Column( 185 | mainAxisSize: MainAxisSize.min, 186 | mainAxisAlignment: MainAxisAlignment.center, 187 | children: [ 188 | Center( 189 | child: IconTheme( 190 | data: IconThemeData(color: Colors.blueGrey), 191 | child: Row( 192 | mainAxisAlignment: MainAxisAlignment.center, 193 | children: [ 194 | Icon( 195 | Icons.work, 196 | size: 48.0, 197 | ), 198 | SizedBox(width: 8), 199 | Icon( 200 | Icons.laptop_mac, 201 | size: 80.0, 202 | ), 203 | SizedBox(width: 8), 204 | Icon( 205 | Icons.school, 206 | size: 48.0, 207 | ), 208 | ], 209 | ), 210 | ), 211 | ), 212 | Padding( 213 | padding: const EdgeInsets.only( 214 | top: 16.0, 215 | bottom: 16.0, 216 | left: 32.0, 217 | right: 32.0, 218 | ), 219 | child: Text( 220 | 'Which one of the following best describes you?', 221 | textAlign: TextAlign.center, 222 | style: Theme.of(context).textTheme.title, 223 | ), 224 | ), 225 | ButtonBar( 226 | mainAxisSize: MainAxisSize.min, 227 | children: [ 228 | RaisedButton( 229 | child: Text('STUDENT'), 230 | onPressed: () { 231 | setState(() { 232 | _isStudent = true; 233 | pageViewItemCount = 3; 234 | }); 235 | controller.animateToPage(2, 236 | duration: Duration(milliseconds: 500), 237 | curve: Curves.fastOutSlowIn); 238 | }, 239 | ), 240 | RaisedButton( 241 | child: Text('PROFESSIONAL'), 242 | onPressed: () { 243 | setState(() { 244 | _isStudent = false; 245 | pageViewItemCount = 4; 246 | }); 247 | controller.animateToPage(2, 248 | duration: Duration(milliseconds: 500), 249 | curve: Curves.fastOutSlowIn); 250 | }, 251 | ), 252 | ], 253 | ), 254 | Divider(), 255 | Row( 256 | children: [ 257 | Expanded( 258 | child: FlatButton( 259 | child: Row( 260 | mainAxisAlignment: MainAxisAlignment.start, 261 | children: [ 262 | Icon( 263 | Icons.arrow_back, 264 | size: 24.0, 265 | ), 266 | Text('BACK'), 267 | ], 268 | ), 269 | textColor: Theme.of(context).primaryColor, 270 | onPressed: () { 271 | controller.animateToPage(0, 272 | duration: Duration(milliseconds: 500), 273 | curve: Curves.fastOutSlowIn); 274 | }, 275 | ), 276 | ) 277 | ], 278 | ) 279 | ], 280 | ), 281 | ), 282 | ); 283 | } 284 | 285 | Center _buildWorkInstituteEntryView() { 286 | return Center( 287 | child: SingleChildScrollView( 288 | child: Column( 289 | mainAxisSize: MainAxisSize.min, 290 | mainAxisAlignment: MainAxisAlignment.center, 291 | children: [ 292 | Icon( 293 | _isStudent ? Icons.school : Icons.work, 294 | size: 100.0, 295 | color: Theme.of(context).primaryColor, 296 | ), 297 | Padding( 298 | padding: const EdgeInsets.only( 299 | top: 16.0, 300 | bottom: 16.0, 301 | left: 32.0, 302 | right: 32.0, 303 | ), 304 | child: Text( 305 | 'Where do you ${_isStudent ? 'study' : 'work'}?', 306 | textAlign: TextAlign.center, 307 | style: Theme.of(context).textTheme.title, 308 | ), 309 | ), 310 | Form( 311 | key: _studentProfessionalFormKey, 312 | child: ListTile( 313 | title: TextFormField( 314 | focusNode: focusNode, 315 | controller: studentProfessionalController, 316 | maxLength: GlobalConstants.entryMaxLength, 317 | validator: (value) => 318 | _validateStudentProfessionalEntry(value), 319 | decoration: InputDecoration( 320 | border: OutlineInputBorder( 321 | borderRadius: BorderRadius.circular(10.0)), 322 | hintText: 323 | 'Enter ${_isStudent ? 'institute' : 'workplace'}', 324 | labelText: '${_isStudent ? 'Institute' : 'Workplace'}'), 325 | ), 326 | ), 327 | ), 328 | Divider(), 329 | Row( 330 | children: [ 331 | Expanded( 332 | child: FlatButton( 333 | child: Row( 334 | mainAxisAlignment: MainAxisAlignment.start, 335 | children: [ 336 | Icon( 337 | Icons.arrow_back, 338 | size: 24.0, 339 | ), 340 | Text('BACK'), 341 | ], 342 | ), 343 | textColor: Theme.of(context).primaryColor, 344 | onPressed: () { 345 | controller.animateToPage(1, 346 | duration: Duration(milliseconds: 500), 347 | curve: Curves.fastOutSlowIn); 348 | }, 349 | ), 350 | ), 351 | Expanded( 352 | child: FlatButton( 353 | child: Row( 354 | mainAxisAlignment: MainAxisAlignment.end, 355 | children: [ 356 | Text(_isStudent ? 'DONE' : 'NEXT'), 357 | Icon( 358 | _isStudent ? Icons.check_circle : Icons.arrow_forward, 359 | size: 24.0, 360 | ), 361 | ], 362 | ), 363 | textColor: Theme.of(context).primaryColor, 364 | onPressed: () async { 365 | focusNode.unfocus(); 366 | if (_studentProfessionalFormKey.currentState.validate()) { 367 | if (_isStudent) { 368 | await _submitDataToFirestore(); 369 | Alert( 370 | context: context, 371 | type: AlertType.success, 372 | title: "Success!", 373 | desc: 374 | "Your are registered successfully!\nYou will receive a confirmation message soon!", 375 | buttons: [ 376 | DialogButton( 377 | child: Text("COOL!", 378 | style: Theme.of(context) 379 | .textTheme 380 | .title 381 | .copyWith( 382 | color: Colors.white, 383 | )), 384 | onPressed: () { 385 | Navigator.of(context).pop(); 386 | Navigator.of(context).pop(); 387 | }, 388 | ) 389 | ], 390 | ).show(); 391 | } else { 392 | controller.animateToPage(3, 393 | duration: Duration(milliseconds: 500), 394 | curve: Curves.fastOutSlowIn); 395 | } 396 | } 397 | }, 398 | ), 399 | ) 400 | ], 401 | ) 402 | ], 403 | ), 404 | ), 405 | ); 406 | } 407 | 408 | Center _buildDesignationEntryView() { 409 | return Center( 410 | child: SingleChildScrollView( 411 | child: Column( 412 | mainAxisSize: MainAxisSize.min, 413 | mainAxisAlignment: MainAxisAlignment.center, 414 | children: [ 415 | Icon( 416 | Icons.account_box, 417 | size: 100.0, 418 | color: Theme.of(context).primaryColor, 419 | ), 420 | Padding( 421 | padding: const EdgeInsets.only( 422 | top: 16.0, 423 | bottom: 16.0, 424 | left: 32.0, 425 | right: 32.0, 426 | ), 427 | child: Text( 428 | 'Your designation at ${studentProfessionalController.text}', 429 | textAlign: TextAlign.center, 430 | style: Theme.of(context).textTheme.title, 431 | ), 432 | ), 433 | Form( 434 | key: _designationFormKey, 435 | child: ListTile( 436 | title: TextFormField( 437 | focusNode: focusNode, 438 | controller: designationController, 439 | maxLength: GlobalConstants.entryMaxLength, 440 | validator: (value) => _validateDesignation(value), 441 | decoration: InputDecoration( 442 | border: OutlineInputBorder( 443 | borderRadius: BorderRadius.circular(10.0)), 444 | hintText: 'Enter designation', 445 | labelText: 'Designation', 446 | ), 447 | ), 448 | ), 449 | ), 450 | Divider(), 451 | Row( 452 | children: [ 453 | Expanded( 454 | child: FlatButton( 455 | child: Row( 456 | mainAxisAlignment: MainAxisAlignment.start, 457 | children: [ 458 | Icon( 459 | Icons.arrow_back, 460 | size: 24.0, 461 | ), 462 | Text('BACK'), 463 | ], 464 | ), 465 | textColor: Theme.of(context).primaryColor, 466 | onPressed: () { 467 | controller.animateToPage(1, 468 | duration: Duration(milliseconds: 500), 469 | curve: Curves.fastOutSlowIn); 470 | }, 471 | ), 472 | ), 473 | Expanded( 474 | child: FlatButton( 475 | child: Row( 476 | mainAxisAlignment: MainAxisAlignment.end, 477 | children: [ 478 | Text('DONE'), 479 | Icon( 480 | Icons.check_circle, 481 | size: 24.0, 482 | ), 483 | ], 484 | ), 485 | textColor: Theme.of(context).primaryColor, 486 | onPressed: () async { 487 | focusNode.unfocus(); 488 | if (_designationFormKey.currentState.validate()) { 489 | await _submitDataToFirestore(); 490 | Alert( 491 | context: context, 492 | type: AlertType.success, 493 | title: "Success!", 494 | desc: 495 | "Your are registered successfully!\nYou will receive a confirmation message soon!", 496 | buttons: [ 497 | DialogButton( 498 | child: Text("COOL!", 499 | style: Theme.of(context) 500 | .textTheme 501 | .title 502 | .copyWith( 503 | color: Colors.white, 504 | )), 505 | onPressed: () { 506 | Navigator.of(context).pop(); 507 | Navigator.of(context).pop(); 508 | }, 509 | ) 510 | ], 511 | ).show(); 512 | } 513 | }, 514 | ), 515 | ) 516 | ], 517 | ) 518 | ], 519 | ), 520 | ), 521 | ); 522 | } 523 | 524 | String _validatePhoneNumber(String number) { 525 | if (number.isEmpty) return 'Phone number required'; 526 | if (number.length < GlobalConstants.phoneNumberMaxLength || 527 | !RegexHelpers.phoneNumberRegex.hasMatch(number)) 528 | return 'You wouldn\'t want to miss any important update! \nPlease enter a valid mobile number'; 529 | } 530 | 531 | String _validateDesignation(String number) { 532 | if (number.isEmpty) return 'Designation required'; 533 | } 534 | 535 | String _validateStudentProfessionalEntry(String number) { 536 | if (number.isEmpty) 537 | return '${_isStudent ? 'Institute' : 'Workplace'} required'; 538 | return null; 539 | } 540 | 541 | Future _submitDataToFirestore() async { 542 | setState(() => _isLoading = true); 543 | try { 544 | FirebaseFirestore.instance.runTransaction((transaction) async { 545 | await transaction.update(userCache.user.reference, { 546 | 'registration': Registration( 547 | occupation: _isStudent ? 'Student' : 'Professional', 548 | workOrInstitute: studentProfessionalController.text, 549 | designation: designationController.text, 550 | ).toJson(), 551 | 'mobileNumber': mobileNumberController.text, 552 | 'isRegistered': true 553 | }); 554 | }); 555 | 556 | await userCache.getUser(userCache.user.id, useCached: false); 557 | } catch (ex) { 558 | print(ex); 559 | Alert( 560 | context: context, 561 | type: AlertType.error, 562 | title: "Oops!", 563 | desc: "An error has occurred", 564 | buttons: [ 565 | DialogButton( 566 | child: Text("Dismiss", 567 | style: Theme.of(context).textTheme.title.copyWith( 568 | color: Colors.white, 569 | )), 570 | color: Colors.red, 571 | onPressed: () { 572 | Navigator.of(context).pop(); 573 | Navigator.of(context).pop(); 574 | }, 575 | ) 576 | ], 577 | ).show(); 578 | } finally { 579 | setState(() => _isLoading = false); 580 | } 581 | } 582 | } 583 | 584 | class Registration { 585 | final String occupation; 586 | final String workOrInstitute; 587 | final String designation; 588 | final DocumentReference reference; 589 | 590 | Registration({ 591 | this.occupation, 592 | this.designation = 'not applicable', 593 | this.workOrInstitute, 594 | this.reference, 595 | }); 596 | 597 | Registration.fromMap(Map map, {this.reference}) 598 | : occupation = map['occupation'], 599 | designation = map['designation'], 600 | workOrInstitute = map['workOrInstitute']; 601 | 602 | Map toJson() => { 603 | "occupation": this.occupation, 604 | "workOrInstitute": this.workOrInstitute, 605 | "designation": this.designation, 606 | }; 607 | 608 | Registration.fromSnapshot(DocumentSnapshot snapshot) 609 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 610 | } 611 | -------------------------------------------------------------------------------- /lib/schedule/model.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter_pk/global.dart'; 3 | import 'package:flutter_pk/util.dart'; 4 | 5 | class ScheduleApi { 6 | Future> getSessionList() async { 7 | var date = await FirebaseFirestore.instance 8 | .collection(FireStoreKeys.dateCollection) 9 | .snapshots() 10 | .first; 11 | 12 | var sessionCollection = date.docs.first.reference 13 | .collection(FireStoreKeys.sessionCollection); 14 | 15 | var sessionList = await sessionCollection.get(); 16 | 17 | return sessionList.docs 18 | .map((item) => Session.fromSnapshot(item)) 19 | .toList(); 20 | } 21 | } 22 | 23 | class Speaker { 24 | final String id; 25 | final String name; 26 | final String photoUrl; 27 | final String description; 28 | final DocumentReference reference; 29 | 30 | Speaker({ 31 | this.name, 32 | this.id, 33 | this.description, 34 | this.reference, 35 | this.photoUrl, 36 | }); 37 | 38 | Speaker.fromMap(Map map, {this.reference}) 39 | : id = map['id'], 40 | description = map['description'], 41 | name = map['name'], 42 | photoUrl = map['photoUrl']; 43 | 44 | Map toJson() => { 45 | "id": id, 46 | "name": name, 47 | "description": description, 48 | "photoUrl": photoUrl, 49 | }; 50 | 51 | Speaker.fromSnapshot(DocumentSnapshot snapshot) 52 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 53 | } 54 | 55 | class Session { 56 | final String id; 57 | final String title; 58 | final DateTime startDateTime; 59 | final DateTime endDateTime; 60 | final String color; 61 | final String textColor; 62 | final String description; 63 | final String speakerId; 64 | final List speakers; 65 | final DocumentReference reference; 66 | 67 | Session( 68 | {this.id, 69 | this.title, 70 | this.endDateTime, 71 | this.startDateTime, 72 | this.color, 73 | this.textColor, 74 | this.description, 75 | this.speakerId, 76 | this.reference, 77 | this.speakers}); 78 | 79 | Session.fromMap(Map map, {this.reference}) 80 | : id = map['id'], 81 | title = map['title'], 82 | endDateTime = toDateTime(map['endDateTime']), 83 | startDateTime = toDateTime(map['startDateTime']), 84 | color = map['color'], 85 | textColor = map['textColor'], 86 | speakerId = map['speakerId'], 87 | speakers = map['speakers'], 88 | description = map['description']; 89 | 90 | Session.fromSnapshot(DocumentSnapshot snapshot) 91 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 92 | } 93 | -------------------------------------------------------------------------------- /lib/schedule/schedule_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_pk/helpers/formatters.dart'; 3 | import 'package:flutter_pk/schedule/model.dart'; 4 | import 'package:flutter_pk/schedule/session_detail.dart'; 5 | import 'package:flutter_pk/theme.dart'; 6 | import 'package:flutter_pk/widgets/custom_app_bar.dart'; 7 | import 'package:progress_indicators/progress_indicators.dart'; 8 | 9 | class SchedulePage extends StatefulWidget { 10 | @override 11 | SchedulePageState createState() { 12 | return new SchedulePageState(); 13 | } 14 | } 15 | 16 | class SchedulePageState extends State 17 | with SingleTickerProviderStateMixin { 18 | ScheduleApi api = ScheduleApi(); 19 | 20 | @override 21 | void initState() { 22 | // TODO: implement initState 23 | super.initState(); 24 | _fetchSessions(); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Scaffold( 30 | body: SafeArea( 31 | child: Column( 32 | children: [ 33 | CustomAppBar( 34 | title: 'Schedule', 35 | ), 36 | _buildList() 37 | ], 38 | ), 39 | ), 40 | ); 41 | } 42 | 43 | Widget _buildList() { 44 | return Expanded( 45 | child: FutureBuilder>( 46 | future: _fetchSessions(), 47 | builder: (context, snapshot) { 48 | if (!snapshot.hasData) { 49 | return Center( 50 | child: HeartbeatProgressIndicator( 51 | child: SizedBox( 52 | height: 40.0, 53 | width: 40.0, 54 | child: Image(image: AssetImage('assets/loader.png')), 55 | ), 56 | ), 57 | ); 58 | } else { 59 | var sessions = snapshot.data; 60 | return ListView.builder( 61 | itemCount: sessions.length, 62 | itemBuilder: (BuildContext context, int index) { 63 | return _buildListItem(context, sessions[index]); 64 | }, 65 | padding: const EdgeInsets.only(top: 20.0), 66 | ); 67 | } 68 | }, 69 | ), 70 | ); 71 | } 72 | 73 | Widget _buildListItem(BuildContext context, Session session) { 74 | var hour = session.startDateTime?.hour; 75 | var minute = session.startDateTime?.minute; 76 | 77 | if (hour == null || minute == null) 78 | return Center( 79 | child: Text( 80 | 'Nothing found!', 81 | style: Theme.of(context).textTheme.title.copyWith( 82 | color: Colors.black26, 83 | fontWeight: FontWeight.bold, 84 | ), 85 | ), 86 | ); 87 | 88 | return Column( 89 | children: [ 90 | Row( 91 | children: [ 92 | Padding( 93 | padding: const EdgeInsets.only(left: 16.0, right: 16.0), 94 | child: Column( 95 | children: [ 96 | Text( 97 | '${_formatTimeDigit(hour)}:${_formatTimeDigit(minute)}', 98 | style: TextStyle( 99 | color: Theme.of(context).primaryColor, 100 | fontWeight: FontWeight.bold), 101 | ), 102 | Text( 103 | 'HRS', 104 | style: TextStyle( 105 | color: Theme.of(context).primaryColor, 106 | fontWeight: FontWeight.bold), 107 | ) 108 | ], 109 | ), 110 | ), 111 | Expanded( 112 | child: Container( 113 | decoration: BoxDecoration( 114 | color: ColorDictionary.stringToColor[session?.color], 115 | borderRadius: BorderRadius.only( 116 | topLeft: Radius.circular(10.0), 117 | bottomLeft: Radius.circular(10.0), 118 | ), 119 | ), 120 | child: ListTile( 121 | title: Text( 122 | session.title, 123 | style: TextStyle( 124 | color: ColorDictionary 125 | .stringToColor[session?.textColor]), 126 | ), 127 | subtitle: Text( 128 | '${formatDate( 129 | session?.startDateTime, 130 | DateFormats.shortUiDateTimeFormat, 131 | )} - ${formatDate( 132 | session?.endDateTime, 133 | DateFormats.shortUiTimeFormat, 134 | )}', 135 | style: TextStyle( 136 | color: 137 | ColorDictionary.stringToColor[session?.textColor], 138 | ), 139 | ), 140 | onTap: () => _handleListTileOnTap(context, session)), 141 | ), 142 | ), 143 | ], 144 | ), 145 | Padding( 146 | padding: const EdgeInsets.only(top: 16.0), 147 | ), 148 | ], 149 | ); 150 | } 151 | 152 | String _formatTimeDigit(int timeValue) => 153 | timeValue < 10 ? '0' + timeValue.toString() : timeValue.toString(); 154 | 155 | Future _handleListTileOnTap(BuildContext context, Session session) async { 156 | var isDescriptionAvailable = 157 | session.description != null && session.description.isNotEmpty; 158 | if (isDescriptionAvailable) 159 | await Navigator.of(context).push( 160 | MaterialPageRoute( 161 | builder: (context) => SessionDetailPage( 162 | session: session, 163 | ), 164 | ), 165 | ); 166 | } 167 | 168 | Future> _fetchSessions() async { 169 | try { 170 | var response = await api.getSessionList(); 171 | return response; 172 | } catch (ex) { 173 | print(ex); 174 | return null; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /lib/schedule/session_detail.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_pk/feedback/feedback.dart'; 6 | import 'package:flutter_pk/global.dart'; 7 | import 'package:flutter_pk/schedule/model.dart'; 8 | import 'package:cached_network_image/cached_network_image.dart'; 9 | import 'package:flutter_pk/theme.dart'; 10 | import 'package:rflutter_alert/rflutter_alert.dart'; 11 | 12 | class SessionDetailPage extends StatelessWidget { 13 | final Session session; 14 | SessionDetailPage({@required this.session}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | var textColor = ColorDictionary.stringToColor[session.textColor]; 19 | 20 | return Scaffold( 21 | appBar: AppBar( 22 | brightness: Brightness.dark, 23 | iconTheme: IconThemeData(color: textColor), 24 | title: Text( 25 | session.title, 26 | style: TextStyle(color: textColor), 27 | ), 28 | backgroundColor: ColorDictionary.stringToColor[session.color], 29 | ), 30 | floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, 31 | floatingActionButton: _buildFeedbackButton(context), 32 | body: _buildBody(showSpeakerInfo: session.speakers != null && session.speakers.length > 0), 33 | ); 34 | } 35 | 36 | FloatingActionButton _buildFeedbackButton(BuildContext context) { 37 | return FloatingActionButton.extended( 38 | onPressed: () { 39 | if (DateTime.now().isAfter( 40 | eventDateTimeCache.eventDateTime.add( 41 | Duration(days: 1), 42 | ), 43 | )) { 44 | _navigateToFeedback(context); 45 | } else { 46 | Alert( 47 | context: context, 48 | type: AlertType.info, 49 | title: "Information!", 50 | desc: 51 | "You will be able to provide feedback once the event day ends!", 52 | buttons: [ 53 | DialogButton( 54 | child: Text("Cool!", 55 | style: Theme.of(context).textTheme.title.copyWith( 56 | color: Colors.white, 57 | )), 58 | color: Colors.green, 59 | onPressed: () { 60 | Navigator.of(context).pop(); 61 | }, 62 | ) 63 | ], 64 | ).show(); 65 | } 66 | }, 67 | icon: Icon( 68 | Icons.rate_review, 69 | color: ColorDictionary.stringToColor[session.textColor], 70 | ), 71 | label: Text( 72 | 'Feedback', 73 | style: TextStyle( 74 | color: ColorDictionary.stringToColor[session.textColor]), 75 | ), 76 | backgroundColor: ColorDictionary.stringToColor[session.color], 77 | ); 78 | } 79 | 80 | Widget _buildBody({@required bool showSpeakerInfo}) { 81 | var bodyWidgets = []; 82 | 83 | if (showSpeakerInfo) { 84 | bodyWidgets.addAll( 85 | [ 86 | Padding( 87 | padding: const EdgeInsets.only(top: 8.0), 88 | child: Center( 89 | child: Text( 90 | 'About ${session.speakers.length > 1 ? 'speakers' : 'speaker'}'), 91 | ), 92 | ), 93 | session.speakers.length > 1 94 | ? _buildMultiSpeakerDetail() 95 | : _buildSingleSpeakerDetail(), 96 | Divider(), 97 | ], 98 | ); 99 | } 100 | 101 | bodyWidgets.addAll( 102 | [ 103 | Padding( 104 | padding: const EdgeInsets.all(16.0), 105 | child: Container( 106 | decoration: BoxDecoration( 107 | color: ColorDictionary.stringToColor[session.color], 108 | borderRadius: BorderRadius.circular(10.0)), 109 | child: ListTile( 110 | title: Padding( 111 | padding: const EdgeInsets.only(top: 8.0), 112 | child: Center( 113 | child: Text( 114 | session.title, 115 | style: TextStyle( 116 | fontWeight: FontWeight.bold, 117 | color: ColorDictionary.stringToColor[session.textColor], 118 | ), 119 | ), 120 | ), 121 | ), 122 | subtitle: Padding( 123 | padding: const EdgeInsets.only(top: 16.0, bottom: 8.0), 124 | child: Text( 125 | session.description, 126 | style: TextStyle( 127 | color: ColorDictionary.stringToColor[session.textColor]), 128 | ), 129 | ), 130 | ), 131 | ), 132 | ), 133 | SizedBox( 134 | height: 60.0, 135 | ), 136 | ], 137 | ); 138 | 139 | return SingleChildScrollView( 140 | child: Padding( 141 | padding: const EdgeInsets.only(top: 8.0), 142 | child: Column( 143 | children: [ 144 | Center( 145 | child: Column( 146 | mainAxisAlignment: MainAxisAlignment.center, 147 | children: bodyWidgets, 148 | ), 149 | ), 150 | ], 151 | ), 152 | ), 153 | ); 154 | } 155 | 156 | void _navigateToFeedback(BuildContext context) { 157 | Navigator.of(context).push( 158 | MaterialPageRoute( 159 | builder: (context) => FullScreenFeedbackDialog( 160 | session: session, 161 | ), 162 | fullscreenDialog: true), 163 | ); 164 | } 165 | 166 | FutureBuilder> _buildMultiSpeakerDetail() { 167 | return FutureBuilder>( 168 | future: _getMultiSpeakerData(), 169 | builder: (context, snapshot) { 170 | if (snapshot.hasData) { 171 | var speakerOne = snapshot.data.first; 172 | var speakerTwo = snapshot.data[1]; 173 | return Row( 174 | crossAxisAlignment: CrossAxisAlignment.start, 175 | children: [ 176 | Expanded(child: _buildSpeakerView(speakerOne)), 177 | Expanded(child: _buildSpeakerView(speakerTwo)), 178 | ], 179 | ); 180 | } else { 181 | return Center( 182 | child: Text( 183 | 'Fetching speaker details', 184 | style: Theme.of(context) 185 | .textTheme 186 | .title 187 | .copyWith(color: Colors.grey), 188 | ), 189 | ); 190 | } 191 | }); 192 | } 193 | 194 | FutureBuilder _buildSingleSpeakerDetail() { 195 | return FutureBuilder( 196 | future: _getData(), 197 | builder: (context, snapshot) { 198 | if (snapshot.hasData) { 199 | return _buildSpeakerView(snapshot.data); 200 | } else { 201 | return Center( 202 | child: Text( 203 | 'Fetching speaker details', 204 | style: Theme.of(context) 205 | .textTheme 206 | .title 207 | .copyWith(color: Colors.grey), 208 | ), 209 | ); 210 | } 211 | }); 212 | } 213 | 214 | Column _buildSpeakerView(Speaker speaker) { 215 | return Column( 216 | children: [ 217 | Padding( 218 | padding: const EdgeInsets.only(top: 16.0), 219 | child: Center( 220 | child: Stack( 221 | children: [ 222 | Container( 223 | height: 70.0, 224 | width: 70.0, 225 | decoration: new BoxDecoration( 226 | shape: BoxShape.circle, 227 | image: DecorationImage( 228 | image: AssetImage( 229 | 'assets/ic_person.png', 230 | ), 231 | ), 232 | ), 233 | ), 234 | Container( 235 | height: 70.0, 236 | width: 70.0, 237 | decoration: new BoxDecoration( 238 | shape: BoxShape.circle, 239 | image: DecorationImage( 240 | image: CachedNetworkImageProvider( 241 | speaker.photoUrl, 242 | ), 243 | ), 244 | ), 245 | ), 246 | ], 247 | ), 248 | ), 249 | ), 250 | ListTile( 251 | title: Padding( 252 | padding: const EdgeInsets.all(0.0), 253 | child: Column( 254 | children: [ 255 | Text( 256 | speaker.name, 257 | style: TextStyle( 258 | fontWeight: FontWeight.bold, 259 | ), 260 | ), 261 | Padding( 262 | padding: const EdgeInsets.only(top: 16.0), 263 | child: Text( 264 | speaker.description, 265 | ), 266 | ), 267 | ], 268 | ), 269 | ), 270 | ), 271 | ], 272 | ); 273 | } 274 | 275 | Future _getData() async { 276 | CollectionReference reference = 277 | await FirebaseFirestore.instance.collection(FireStoreKeys.speakerCollection); 278 | var speaker = 279 | Speaker.fromSnapshot(await reference.document(session.speakerId).get()); 280 | return speaker; 281 | } 282 | 283 | Future> _getMultiSpeakerData() async { 284 | CollectionReference reference = 285 | await FirebaseFirestore.instance.collection(FireStoreKeys.speakerCollection); 286 | var speakerOne = Speaker.fromSnapshot( 287 | await reference.document(session.speakers[0]).get()); 288 | var speakerTwo = Speaker.fromSnapshot( 289 | await reference.document(session.speakers[1]).get()); 290 | List list = List(); 291 | list.addAll([ 292 | speakerOne, 293 | speakerTwo, 294 | ]); 295 | return list; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /lib/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class ColorDictionary { 4 | static const Map stringToColor = { 5 | 'indigo': Colors.indigo, 6 | 'green': Colors.green, 7 | 'amber': Colors.amber, 8 | 'blue': Colors.blue, 9 | 'white': Colors.white, 10 | 'black': Colors.black, 11 | 'blueGrey': Colors.blueGrey, 12 | 'lightBlue': Colors.lightBlue, 13 | 'brown': Colors.brown, 14 | 'teal': Colors.teal, 15 | 'indigoAccent': Colors.indigoAccent, 16 | 'grey': Colors.grey 17 | }; 18 | } 19 | 20 | const Color kBlue = Colors.blue; 21 | final theme = _buildTheme(); 22 | 23 | ThemeData _buildTheme() { 24 | var base = ThemeData.light(); 25 | print('font size of theme: ${base.primaryTextTheme.title.fontSize}'); 26 | 27 | return base.copyWith( 28 | primaryColor: kBlue, 29 | accentColor: kBlue, 30 | canvasColor: Colors.white, 31 | scaffoldBackgroundColor: Colors.white, 32 | appBarTheme: AppBarTheme( 33 | brightness: Brightness.light, 34 | iconTheme: base.iconTheme.copyWith(color: Colors.black), 35 | color: Colors.white, 36 | textTheme: base.primaryTextTheme.copyWith( 37 | title: TextStyle( 38 | fontWeight: FontWeight.w500, 39 | fontSize: 20, 40 | ) 41 | ).apply(bodyColor: Colors.black), 42 | ), 43 | buttonTheme: base.buttonTheme.copyWith( 44 | buttonColor: kBlue, 45 | textTheme: ButtonTextTheme.primary, 46 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), 47 | ), 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /lib/util.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | 3 | DateTime toDateTime(Timestamp timestamp) => timestamp.toDate(); -------------------------------------------------------------------------------- /lib/venue_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_pk/global.dart'; 4 | import 'package:flutter_pk/venue_model.dart'; 5 | import 'package:google_maps_flutter/google_maps_flutter.dart'; 6 | import 'package:progress_indicators/progress_indicators.dart'; 7 | 8 | class VenueDetailPage extends StatefulWidget { 9 | @override 10 | VenueDetailPageState createState() { 11 | return new VenueDetailPageState(); 12 | } 13 | } 14 | 15 | class VenueDetailPageState extends State { 16 | GoogleMapController mapController; 17 | bool _isLoading = false; 18 | Venue _venue = new Venue(); 19 | Map markers = {}; 20 | 21 | void _onMapCreated(GoogleMapController controller) { 22 | mapController = controller; 23 | _addMarker(_venue); 24 | } 25 | 26 | @override 27 | void initState() { 28 | // TODO: implement initState 29 | super.initState(); 30 | _getData(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | // TODO: implement build 36 | return Scaffold( 37 | body: Column( 38 | children: [_buildBody()], 39 | ), 40 | ); 41 | } 42 | 43 | Widget _buildBody() { 44 | return Expanded( 45 | child: _isLoading 46 | ? Center( 47 | child: HeartbeatProgressIndicator( 48 | child: SizedBox( 49 | height: 40.0, 50 | width: 40.0, 51 | child: Image( 52 | image: AssetImage('assets/map.png'), 53 | color: Theme.of(context).primaryColor, 54 | ), 55 | )), 56 | ) 57 | : Stack( 58 | children: [ 59 | GoogleMap( 60 | onMapCreated: _onMapCreated, 61 | mapType: MapType.normal, 62 | zoomGesturesEnabled: true, 63 | compassEnabled: true, 64 | rotateGesturesEnabled: true, 65 | scrollGesturesEnabled: true, 66 | markers: Set.of(markers.values), 67 | initialCameraPosition: CameraPosition( 68 | target: LatLng( 69 | _venue.location.latitude, 70 | _venue.location.longitude, 71 | ), 72 | zoom: 15.0), 73 | ), 74 | SafeArea( 75 | child: Padding( 76 | padding: const EdgeInsets.all(8.0), 77 | child: Row( 78 | children: [ 79 | Expanded( 80 | child: Card( 81 | elevation: 4.0, 82 | child: Column( 83 | mainAxisSize: MainAxisSize.min, 84 | children: [ 85 | Padding( 86 | padding: const EdgeInsets.only( 87 | left: 64.0, right: 64.0, top: 16.0), 88 | child: Image( 89 | image: NetworkImage( 90 | _venue.imageUrl, 91 | scale: 0.5, 92 | ), 93 | ), 94 | ), 95 | Padding( 96 | padding: const EdgeInsets.all(8.0), 97 | child: Text( 98 | _venue.address, 99 | textAlign: TextAlign.center, 100 | ), 101 | ), 102 | ], 103 | ), 104 | ), 105 | ), 106 | ], 107 | ), 108 | ), 109 | ) 110 | ], 111 | ), 112 | ); 113 | } 114 | 115 | _getData() { 116 | setState(() { 117 | _isLoading = true; 118 | }); 119 | EventDetails _eventDetails = new EventDetails(); 120 | var eventDetails = FirebaseFirestore.instance 121 | .collection(FireStoreKeys.dateCollection) 122 | .snapshots() 123 | .first; 124 | eventDetails.then((onValue) { 125 | locationCache.setLocation( 126 | onValue.docs.first.data()['venue']['location']['longitude'].toString(), 127 | onValue.docs.first.data()['venue']['location']['latitude'].toString(), 128 | ); 129 | _eventDetails = EventDetails( 130 | reference: onValue.docs.first.reference, 131 | venue: Venue( 132 | address: onValue.docs.first.data()['venue']['address'], 133 | title: onValue.docs.first.data()['venue']['title'], 134 | city: onValue.docs.first.data()['venue']['city'], 135 | imageUrl: onValue.docs.first.data()['venue']['imageUrl'], 136 | location: Location( 137 | latitude: onValue.docs.first.data()['venue']['location'] 138 | ['latitude'], 139 | longitude: onValue.docs.first.data()['venue']['location'] 140 | ['longitude'], 141 | ))); 142 | setState(() { 143 | _venue = _eventDetails.venue; 144 | _isLoading = false; 145 | }); 146 | }); 147 | } 148 | 149 | void _addMarker(Venue venue) { 150 | final int markerCount = markers.length; 151 | 152 | if (markerCount == 12) { 153 | return; 154 | } 155 | 156 | final MarkerId markerId = MarkerId(venue.city); 157 | 158 | final Marker marker = Marker( 159 | markerId: markerId, 160 | position: LatLng(venue.location.latitude, venue.location.longitude), 161 | infoWindow: InfoWindow(title: venue.title, snippet: venue.city), 162 | icon: BitmapDescriptor.defaultMarkerWithHue( 163 | BitmapDescriptor.hueAzure, 164 | ), 165 | ); 166 | 167 | setState(() { 168 | markers[markerId] = marker; 169 | }); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /lib/venue_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter_pk/helpers/formatters.dart'; 3 | 4 | class EventDetails { 5 | final String date; 6 | final String eventTitle; 7 | final Venue venue; 8 | final DocumentReference reference; 9 | 10 | EventDetails({this.date, this.eventTitle, this.venue, this.reference}); 11 | 12 | EventDetails.fromMap(Map map, {this.reference}) 13 | : eventTitle = map['eventTitle'], 14 | date = formatDate( 15 | map['date'], 16 | DateFormats.shortUiDateFormat, 17 | ), 18 | venue = Venue.fromMap(map['venue']); 19 | EventDetails.fromSnapshot(DocumentSnapshot snapshot) 20 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 21 | } 22 | 23 | class Venue { 24 | final String title; 25 | final String address; 26 | final String city; 27 | final String imageUrl; 28 | final Location location; 29 | final DocumentReference reference; 30 | 31 | Venue({ 32 | this.address, 33 | this.title, 34 | this.imageUrl, 35 | this.location, 36 | this.city, 37 | this.reference, 38 | }); 39 | 40 | Venue.fromMap(Map map, {this.reference}) 41 | : title = map['title'], 42 | address = map['address'], 43 | city = map['city'], 44 | imageUrl = map['imageUrl'], 45 | location = Location.fromMap(map['location']); 46 | 47 | Venue.fromSnapshot(DocumentSnapshot snapshot) 48 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 49 | } 50 | 51 | class Location { 52 | final double latitude; 53 | final double longitude; 54 | final DocumentReference reference; 55 | 56 | Location({ 57 | this.latitude, 58 | this.longitude, 59 | this.reference, 60 | }); 61 | 62 | Location.fromMap(Map map, {this.reference}) 63 | : latitude = map['latitude'], 64 | longitude = map['longitude']; 65 | 66 | Location.fromSnapshot(DocumentSnapshot snapshot) 67 | : this.fromMap(snapshot.data(), reference: snapshot.reference); 68 | } -------------------------------------------------------------------------------- /lib/widgets/capsule_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CapsuleText extends StatelessWidget { 4 | final Color color; 5 | final String title; 6 | final Color textColor; 7 | 8 | CapsuleText({this.color, this.title, this.textColor = Colors.white}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Card( 13 | elevation: 0.0, 14 | shape: StadiumBorder(), 15 | color: color, 16 | child: Padding( 17 | padding: const EdgeInsets.symmetric( 18 | horizontal: 8.0, 19 | vertical: 2.0, 20 | ), 21 | child: Text( 22 | title, 23 | style: Theme.of(context).textTheme.caption.copyWith( 24 | color: textColor, 25 | fontSize: 14.0 26 | ), 27 | ), 28 | ), 29 | ); 30 | } 31 | } -------------------------------------------------------------------------------- /lib/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_pk/global.dart'; 4 | import 'package:flutter_pk/helpers/formatters.dart'; 5 | import 'package:flutter_pk/profile/profile_dialog.dart'; 6 | import 'package:flutter_pk/util.dart'; 7 | 8 | class CustomAppBar extends StatefulWidget { 9 | final String title; 10 | CustomAppBar({@required this.title}); 11 | @override 12 | CustomAppBarState createState() { 13 | return new CustomAppBarState(); 14 | } 15 | } 16 | 17 | class CustomAppBarState extends State { 18 | String eventDate = ''; 19 | String eventTitle = ''; 20 | @override 21 | void initState() { 22 | super.initState(); 23 | var eventDetails = FirebaseFirestore.instance 24 | .collection(FireStoreKeys.dateCollection) 25 | .snapshots() 26 | .first; 27 | eventDetails.then((onValue) { 28 | setState(() { 29 | eventDate = formatDate( 30 | toDateTime(onValue.docs.first.data()['date']), 31 | DateFormats.shortUiDateFormat, 32 | ); 33 | eventDateTimeCache 34 | .setDateTime(toDateTime(onValue.docs.first.data()['date'])); 35 | eventTitle = onValue.docs.first.data()['eventTitle']; 36 | }); 37 | }); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Padding( 43 | padding: const EdgeInsets.only(left: 8, top: 40, right: 8, bottom: 8), 44 | child: Row( 45 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 46 | children: [ 47 | GestureDetector( 48 | onTap: () async { 49 | await Navigator.of(context).push( 50 | MaterialPageRoute( 51 | builder: (context) => FullScreenProfileDialog(), 52 | fullscreenDialog: true, 53 | ), 54 | ); 55 | }, 56 | child: Padding( 57 | padding: const EdgeInsets.only(left: 8.0), 58 | child: CircleAvatar( 59 | backgroundImage: NetworkImage(userCache.user.photoUrl)), 60 | ), 61 | ), 62 | Expanded( 63 | child: Column( 64 | mainAxisAlignment: MainAxisAlignment.center, 65 | crossAxisAlignment: CrossAxisAlignment.center, 66 | children: [ 67 | Text( 68 | eventTitle, 69 | style: Theme.of(context).textTheme.title, 70 | textAlign: TextAlign.center, 71 | overflow: TextOverflow.clip, 72 | ), 73 | Text(eventDate) 74 | ], 75 | ), 76 | ), 77 | SizedBox(width: 48), 78 | ], 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/widgets/dots_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class DotsIndicator extends AnimatedWidget { 6 | DotsIndicator({ 7 | this.controller, 8 | this.itemCount, 9 | this.onPageSelected, 10 | this.activeColor, 11 | this.inactiveColor: Colors.white, 12 | }) : super(listenable: controller); 13 | 14 | /// The PageController that this DotsIndicator is representing. 15 | final PageController controller; 16 | 17 | /// The number of items managed by the PageController 18 | final int itemCount; 19 | 20 | /// Called when a dot is tapped 21 | final ValueChanged onPageSelected; 22 | 23 | /// The color of the dots. 24 | /// 25 | /// Defaults to `Colors.white`. 26 | final Color inactiveColor; 27 | 28 | final Color activeColor; 29 | 30 | // The base size of the dots 31 | static const double _kDotSize = 8.0; 32 | 33 | // The increase in the size of the selected dot 34 | static const double _kMaxZoom = 1.5; 35 | 36 | // The distance between the center of each dot 37 | static const double _kDotSpacing = 18.0; 38 | 39 | Widget _buildDot(int index, BuildContext context) { 40 | double selectedness = Curves.easeOut.transform( 41 | max( 42 | 0.0, 43 | 1.0 - ((controller.page ?? controller.initialPage) - index).abs(), 44 | ), 45 | ); 46 | double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness; 47 | return new Container( 48 | width: _kDotSpacing, 49 | child: new Center( 50 | child: new Material( 51 | color: index == (controller.page == null ? 0 : controller.page) 52 | ? activeColor 53 | : inactiveColor, 54 | type: MaterialType.circle, 55 | child: new Container( 56 | width: _kDotSize * zoom, 57 | height: _kDotSize * zoom, 58 | ), 59 | ), 60 | ), 61 | ); 62 | } 63 | 64 | Widget build(BuildContext context) { 65 | return SizedBox( 66 | height: 20.0, 67 | child: Row( 68 | mainAxisAlignment: MainAxisAlignment.spaceAround, 69 | children: [ 70 | Expanded( 71 | child: Center( 72 | child: ListView.builder( 73 | itemBuilder: (context, int index) { 74 | return _buildDot(index, context); 75 | }, 76 | itemCount: itemCount, 77 | scrollDirection: Axis.horizontal, 78 | shrinkWrap: true, 79 | ), 80 | ), 81 | ), 82 | ], 83 | ), 84 | ); 85 | } 86 | } -------------------------------------------------------------------------------- /lib/widgets/full_screen_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:progress_indicators/progress_indicators.dart'; 3 | 4 | class FullScreenLoader extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | // TODO: implement build 8 | return Container( 9 | height: MediaQuery.of(context).size.height, 10 | width: MediaQuery.of(context).size.width, 11 | decoration: BoxDecoration( 12 | color: Colors.black54, 13 | backgroundBlendMode: BlendMode.darken, 14 | ), 15 | child: Center( 16 | child: HeartbeatProgressIndicator( 17 | child: SizedBox( 18 | height: 40.0, 19 | width: 40.0, 20 | child: Image(image: AssetImage('assets/loader.png')), 21 | )), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/widgets/sprung_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:sprung/sprung.dart'; 3 | 4 | typedef void BoolCallback(bool val); 5 | 6 | class SprungBox extends StatefulWidget { 7 | final Duration duration; 8 | final BoolCallback callback; 9 | 10 | SprungBox({ 11 | this.callback, 12 | duration, 13 | }) : this.duration = duration ?? Duration(milliseconds: 3500); 14 | 15 | @override 16 | _SprungBoxState createState() => _SprungBoxState(); 17 | } 18 | 19 | class _SprungBoxState extends State 20 | with SingleTickerProviderStateMixin { 21 | bool _isOffset = false; 22 | bool showFlag = false; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | _toggleOffset(); 28 | } 29 | 30 | void _toggleOffset() async { 31 | await Future.delayed( 32 | new Duration(milliseconds: 500), 33 | () => setState(() { 34 | this._isOffset = !this._isOffset; 35 | }), 36 | ); 37 | await Future.delayed( 38 | new Duration(milliseconds: 1500), 39 | () => widget.callback(true), 40 | ); 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return LayoutBuilder( 46 | builder: (context, constraints) { 47 | final height = constraints.maxWidth * 2; 48 | final left = !this._isOffset ? height + 100.0 : 40.0; 49 | 50 | final width = MediaQuery.of(context).size.width * 2; 51 | 52 | return Padding( 53 | padding: const EdgeInsets.only(right: 48.0), 54 | child: AnimatedContainer( 55 | duration: this.widget.duration, 56 | curve: Sprung(), 57 | margin: EdgeInsets.only( 58 | left: left, 59 | ), 60 | height: 250.0, 61 | width: width, 62 | color: Colors.transparent, 63 | child: SizedBox( 64 | height: 250.0, 65 | width: 250.0, 66 | child: Image( 67 | image: AssetImage('assets/flutterKarachi.png'), 68 | ), 69 | ), 70 | ), 71 | ); 72 | }, 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_pk 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+5 11 | 12 | environment: 13 | sdk: ">=2.1.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | flutter_swiper: ^1.1.6 19 | firebase_auth: ^0.18.0+1 20 | firebase_core: ^0.5.0 21 | cloud_firestore: ^0.14.0+2 22 | sprung: ^2.0.0+12 23 | intl: ^0.16.1 24 | rounded_modal: ^1.0.1 25 | progress_indicators: ^0.1.5 26 | google_sign_in: ^4.5.3 27 | cached_network_image: ^2.2.0+1 28 | rflutter_alert: ^1.0.3 29 | shared_preferences: ^0.5.10 30 | smooth_star_rating: 1.0.1 31 | barcode_scan: ^3.0.1 32 | google_maps_flutter: ^0.5.30 33 | url_launcher: ^5.5.0 34 | 35 | # The following adds the Cupertino Icons font to your application. 36 | # Use with the CupertinoIcons class for iOS style icons. 37 | cupertino_icons: ^0.1.3 38 | 39 | dev_dependencies: 40 | flutter_test: 41 | sdk: flutter 42 | flutter_launcher_icons: ^0.7.5 43 | 44 | flutter_icons: 45 | android: "launcher_icon" 46 | ios: true 47 | image_path: "assets/icon/icon.png" 48 | 49 | 50 | # For information on the generic Dart part of this file, see the 51 | # following page: https://www.dartlang.org/tools/pub/pubspec 52 | 53 | # The following section is specific to Flutter. 54 | flutter: 55 | 56 | # The following line ensures that the Material Icons font is 57 | # included with your application, so that you can use the icons in 58 | # the material Icons class. 59 | uses-material-design: true 60 | assets: 61 | - assets/ 62 | 63 | # To add assets to your application, add an assets section, like this: 64 | # assets: 65 | # - images/a_dot_burr.jpeg 66 | # - images/a_dot_ham.jpeg 67 | 68 | # An image asset can refer to one or more resolution-specific "variants", see 69 | # https://flutter.io/assets-and-images/#resolution-aware. 70 | 71 | # For details regarding adding assets from package dependencies, see 72 | # https://flutter.io/assets-and-images/#from-packages 73 | 74 | # To add custom fonts to your application, add a fonts section here, 75 | # in this "flutter" section. Each entry in this list should have a 76 | # "family" key with the font family name, and a "fonts" key with a 77 | # list giving the asset and other descriptors for the font. For 78 | # example: 79 | # fonts: 80 | # - family: Schyler 81 | # fonts: 82 | # - asset: fonts/Schyler-Regular.ttf 83 | # - asset: fonts/Schyler-Italic.ttf 84 | # style: italic 85 | # - family: Trajan Pro 86 | # fonts: 87 | # - asset: fonts/TrajanPro.ttf 88 | # - asset: fonts/TrajanPro_Bold.ttf 89 | # weight: 700 90 | # 91 | # For details regarding fonts from package dependencies, 92 | # see https://flutter.io/custom-fonts/#from-packages 93 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_pk/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------