├── .firebaserc
├── .gitignore
├── .metadata
├── README.md
├── android
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── flutter_instagram_clone
│ │ │ │ └── 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
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── fonts
│ └── Billabong.ttf
└── images
│ └── user_placeholder.jpg
├── firebase.json
├── functions
├── .eslintrc.json
├── .gitignore
├── index.js
├── package-lock.json
└── package.json
├── ios
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── flutter_export_environment.sh
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── 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
├── main.dart
├── models
│ ├── activity_model.dart
│ ├── comment_model.dart
│ ├── post_model.dart
│ ├── user_data.dart
│ └── user_model.dart
├── screens
│ ├── activity_screen.dart
│ ├── comments_screen.dart
│ ├── create_post_screen.dart
│ ├── edit_profile_screen.dart
│ ├── feed_screen.dart
│ ├── home_screen.dart
│ ├── login_screen.dart
│ ├── profile_screen.dart
│ ├── search_screen.dart
│ └── signup_screen.dart
├── services
│ ├── auth_service.dart
│ ├── database_service.dart
│ └── storage_service.dart
├── utilities
│ └── constants.dart
└── widgets
│ └── post_view.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── widget_test.dart
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "flutter-instagram-clone-596b4"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | android/app/google-services.json
2 | ios/Runner/GoogleService-Info.plist
3 |
4 | # Miscellaneous
5 | *.class
6 | *.log
7 | *.pyc
8 | *.swp
9 | .DS_Store
10 | .atom/
11 | .buildlog/
12 | .history
13 | .svn/
14 |
15 | # IntelliJ related
16 | *.iml
17 | *.ipr
18 | *.iws
19 | .idea/
20 |
21 | # The .vscode folder contains launch configuration and tasks you configure in
22 | # VS Code which you may wish to be included in version control, so this line
23 | # is commented out by default.
24 | #.vscode/
25 |
26 | # Flutter/Dart/Pub related
27 | **/doc/api/
28 | .dart_tool/
29 | .flutter-plugins
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Android related
36 | **/android/**/gradle-wrapper.jar
37 | **/android/.gradle
38 | **/android/captures/
39 | **/android/gradlew
40 | **/android/gradlew.bat
41 | **/android/local.properties
42 | **/android/**/GeneratedPluginRegistrant.java
43 |
44 | # iOS/XCode related
45 | **/ios/**/*.mode1v3
46 | **/ios/**/*.mode2v3
47 | **/ios/**/*.moved-aside
48 | **/ios/**/*.pbxuser
49 | **/ios/**/*.perspectivev3
50 | **/ios/**/*sync/
51 | **/ios/**/.sconsign.dblite
52 | **/ios/**/.tags*
53 | **/ios/**/.vagrant/
54 | **/ios/**/DerivedData/
55 | **/ios/**/Icon?
56 | **/ios/**/Pods/
57 | **/ios/**/.symlinks/
58 | **/ios/**/profile
59 | **/ios/**/xcuserdata
60 | **/ios/.generated/
61 | **/ios/Flutter/App.framework
62 | **/ios/Flutter/Flutter.framework
63 | **/ios/Flutter/Generated.xcconfig
64 | **/ios/Flutter/app.flx
65 | **/ios/Flutter/app.zip
66 | **/ios/Flutter/flutter_assets/
67 | **/ios/ServiceDefinitions.json
68 | **/ios/Runner/GeneratedPluginRegistrant.*
69 |
70 | # Exceptions to above rules.
71 | !**/ios/**/default.mode1v3
72 | !**/ios/**/default.mode2v3
73 | !**/ios/**/default.pbxuser
74 | !**/ios/**/default.perspectivev3
75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
76 |
--------------------------------------------------------------------------------
/.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: 20e59316b8b8474554b38493b8ca888794b0234a
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Flutter Firebase Instagram Tutorial](https://marcus-ng.com/p/flutter-firebase-instagram-tutorial)
2 |
--------------------------------------------------------------------------------
/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.example.flutterInstagramClone"
42 | minSdkVersion 16
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | testInstrumentationRunner "android.support.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 'com.google.firebase:firebase-analytics:17.2.0'
66 | implementation 'androidx.multidex:multidex:2.0.0'
67 | testImplementation 'junit:junit:4.12'
68 | androidTestImplementation 'com.androidx.support.test:runner:1.1.0'
69 | androidTestImplementation 'com.androidx.support.test.espresso:espresso-core:3.1.0'
70 | }
71 |
72 | apply plugin: 'com.google.gms.google-services'
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
13 |
20 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/flutter_instagram_clone/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_instagram_clone
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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | 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.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | classpath 'com.google.gms:google-services:4.3.2'
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.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/assets/fonts/Billabong.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/assets/fonts/Billabong.ttf
--------------------------------------------------------------------------------
/assets/images/user_placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/assets/images/user_placeholder.jpg
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": {
3 | "predeploy": [
4 | "npm --prefix \"$RESOURCE_DIR\" run lint"
5 | ]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/functions/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | // Required for certain syntax usages
4 | "ecmaVersion": 2017
5 | },
6 | "plugins": [
7 | "promise"
8 | ],
9 | "extends": "eslint:recommended",
10 | "rules": {
11 | // Removed rule "disallow the use of console" from recommended eslint rules
12 | "no-console": "off",
13 |
14 | // Removed rule "disallow multiple spaces in regular expressions" from recommended eslint rules
15 | "no-regex-spaces": "off",
16 |
17 | // Removed rule "disallow the use of debugger" from recommended eslint rules
18 | "no-debugger": "off",
19 |
20 | // Removed rule "disallow unused variables" from recommended eslint rules
21 | "no-unused-vars": "off",
22 |
23 | // Removed rule "disallow mixed spaces and tabs for indentation" from recommended eslint rules
24 | "no-mixed-spaces-and-tabs": "off",
25 |
26 | // Removed rule "disallow the use of undeclared variables unless mentioned in /*global */ comments" from recommended eslint rules
27 | "no-undef": "off",
28 |
29 | // Warn against template literal placeholder syntax in regular strings
30 | "no-template-curly-in-string": 1,
31 |
32 | // Warn if return statements do not either always or never specify values
33 | "consistent-return": 1,
34 |
35 | // Warn if no return statements in callbacks of array methods
36 | "array-callback-return": 1,
37 |
38 | // Require the use of === and !==
39 | "eqeqeq": 2,
40 |
41 | // Disallow the use of alert, confirm, and prompt
42 | "no-alert": 2,
43 |
44 | // Disallow the use of arguments.caller or arguments.callee
45 | "no-caller": 2,
46 |
47 | // Disallow null comparisons without type-checking operators
48 | "no-eq-null": 2,
49 |
50 | // Disallow the use of eval()
51 | "no-eval": 2,
52 |
53 | // Warn against extending native types
54 | "no-extend-native": 1,
55 |
56 | // Warn against unnecessary calls to .bind()
57 | "no-extra-bind": 1,
58 |
59 | // Warn against unnecessary labels
60 | "no-extra-label": 1,
61 |
62 | // Disallow leading or trailing decimal points in numeric literals
63 | "no-floating-decimal": 2,
64 |
65 | // Warn against shorthand type conversions
66 | "no-implicit-coercion": 1,
67 |
68 | // Warn against function declarations and expressions inside loop statements
69 | "no-loop-func": 1,
70 |
71 | // Disallow new operators with the Function object
72 | "no-new-func": 2,
73 |
74 | // Warn against new operators with the String, Number, and Boolean objects
75 | "no-new-wrappers": 1,
76 |
77 | // Disallow throwing literals as exceptions
78 | "no-throw-literal": 2,
79 |
80 | // Require using Error objects as Promise rejection reasons
81 | "prefer-promise-reject-errors": 2,
82 |
83 | // Enforce “for” loop update clause moving the counter in the right direction
84 | "for-direction": 2,
85 |
86 | // Enforce return statements in getters
87 | "getter-return": 2,
88 |
89 | // Disallow await inside of loops
90 | "no-await-in-loop": 2,
91 |
92 | // Disallow comparing against -0
93 | "no-compare-neg-zero": 2,
94 |
95 | // Warn against catch clause parameters from shadowing variables in the outer scope
96 | "no-catch-shadow": 1,
97 |
98 | // Disallow identifiers from shadowing restricted names
99 | "no-shadow-restricted-names": 2,
100 |
101 | // Enforce return statements in callbacks of array methods
102 | "callback-return": 2,
103 |
104 | // Require error handling in callbacks
105 | "handle-callback-err": 2,
106 |
107 | // Warn against string concatenation with __dirname and __filename
108 | "no-path-concat": 1,
109 |
110 | // Prefer using arrow functions for callbacks
111 | "prefer-arrow-callback": 1,
112 |
113 | // Return inside each then() to create readable and reusable Promise chains.
114 | // Forces developers to return console logs and http calls in promises.
115 | "promise/always-return": 2,
116 |
117 | //Enforces the use of catch() on un-returned promises
118 | "promise/catch-or-return": 2,
119 |
120 | // Warn against nested then() or catch() statements
121 | "promise/no-nesting": 1
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/functions/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/functions/index.js:
--------------------------------------------------------------------------------
1 | const functions = require('firebase-functions');
2 | const admin = require('firebase-admin');
3 | admin.initializeApp();
4 |
5 | exports.onFollowUser = functions.firestore
6 | .document('/followers/{userId}/userFollowers/{followerId}')
7 | .onCreate(async (snapshot, context) => {
8 | console.log(snapshot.data());
9 | const userId = context.params.userId;
10 | const followerId = context.params.followerId;
11 | const followedUserPostsRef = admin
12 | .firestore()
13 | .collection('posts')
14 | .doc(userId)
15 | .collection('userPosts');
16 | const userFeedRef = admin
17 | .firestore()
18 | .collection('feeds')
19 | .doc(followerId)
20 | .collection('userFeed');
21 | const followedUserPostsSnapshot = await followedUserPostsRef.get();
22 | followedUserPostsSnapshot.forEach(doc => {
23 | if (doc.exists) {
24 | userFeedRef.doc(doc.id).set(doc.data());
25 | }
26 | });
27 | });
28 |
29 | exports.onUnfollowUser = functions.firestore
30 | .document('/followers/{userId}/userFollowers/{followerId}')
31 | .onDelete(async (snapshot, context) => {
32 | const userId = context.params.userId;
33 | const followerId = context.params.followerId;
34 | const userFeedRef = admin
35 | .firestore()
36 | .collection('feeds')
37 | .doc(followerId)
38 | .collection('userFeed')
39 | .where('authorId', '==', userId);
40 | const userPostsSnapshot = await userFeedRef.get();
41 | userPostsSnapshot.forEach(doc => {
42 | if (doc.exists) {
43 | doc.ref.delete();
44 | }
45 | });
46 | });
47 |
48 | exports.onUploadPost = functions.firestore
49 | .document('/posts/{userId}/userPosts/{postId}')
50 | .onCreate(async (snapshot, context) => {
51 | console.log(snapshot.data());
52 | const userId = context.params.userId;
53 | const postId = context.params.postId;
54 | const userFollowersRef = admin
55 | .firestore()
56 | .collection('followers')
57 | .doc(userId)
58 | .collection('userFollowers');
59 | const userFollowersSnapshot = await userFollowersRef.get();
60 | userFollowersSnapshot.forEach(doc => {
61 | admin
62 | .firestore()
63 | .collection('feeds')
64 | .doc(doc.id)
65 | .collection('userFeed')
66 | .doc(postId)
67 | .set(snapshot.data());
68 | });
69 | });
70 |
71 | exports.onUpdatePost = functions.firestore
72 | .document('/posts/{userId}/userPosts/{postId}')
73 | .onUpdate(async (snapshot, context) => {
74 | const userId = context.params.userId;
75 | const postId = context.params.postId;
76 | const newPostData = snapshot.after.data();
77 | console.log(newPostData);
78 | const userFollowersRef = admin
79 | .firestore()
80 | .collection('followers')
81 | .doc(userId)
82 | .collection('userFollowers');
83 | const userFollowersSnapshot = await userFollowersRef.get();
84 | userFollowersSnapshot.forEach(async userDoc => {
85 | const postRef = admin
86 | .firestore()
87 | .collection('feeds')
88 | .doc(userDoc.id)
89 | .collection('userFeed');
90 | const postDoc = await postRef.doc(postId).get();
91 | if (postDoc.exists) {
92 | postDoc.ref.update(newPostData);
93 | }
94 | });
95 | });
96 |
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "lint": "eslint .",
6 | "serve": "firebase serve --only functions",
7 | "shell": "firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "engines": {
13 | "node": "8"
14 | },
15 | "dependencies": {
16 | "firebase-admin": "^8.6.0",
17 | "firebase-functions": "^3.3.0"
18 | },
19 | "devDependencies": {
20 | "eslint": "^5.12.0",
21 | "eslint-plugin-promise": "^4.0.1",
22 | "firebase-functions-test": "^0.1.6"
23 | },
24 | "private": true
25 | }
26 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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/Flutter/flutter_export_environment.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # This is a generated file; do not edit or check into version control.
3 | export "FLUTTER_ROOT=/Users/marcusng/Downloads/flutter"
4 | export "FLUTTER_APPLICATION_PATH=/Users/marcusng/Development/YouTube/Tutorials/flutter_instagram_clone"
5 | export "FLUTTER_TARGET=/Users/marcusng/Development/YouTube/Tutorials/flutter_instagram_clone/lib/main.dart"
6 | export "FLUTTER_BUILD_DIR=build"
7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios"
8 | export "FLUTTER_FRAMEWORK_DIR=/Users/marcusng/Downloads/flutter/bin/cache/artifacts/engine/ios"
9 | export "FLUTTER_BUILD_NAME=1.0.0"
10 | export "FLUTTER_BUILD_NUMBER=1"
11 | export "TRACK_WIDGET_CREATION=true"
12 |
--------------------------------------------------------------------------------
/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 | generated_key_values = {}
19 | skip_line_start_symbols = ["#", "/"]
20 | File.foreach(file_abs_path) do |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 | generated_key_values[podname] = podpath
28 | else
29 | puts "Invalid plugin specification: #{line}"
30 | end
31 | end
32 | generated_key_values
33 | end
34 |
35 | target 'Runner' do
36 | use_frameworks!
37 | use_modular_headers!
38 |
39 | # Flutter Pod
40 |
41 | copied_flutter_dir = File.join(__dir__, 'Flutter')
42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
48 |
49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
50 | unless File.exist?(generated_xcode_build_settings_path)
51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
52 | end
53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
55 |
56 | unless File.exist?(copied_framework_path)
57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
58 | end
59 | unless File.exist?(copied_podspec_path)
60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
61 | end
62 | end
63 |
64 | # Keep pod path relative so it can be checked into Podfile.lock.
65 | pod 'Flutter', :path => 'Flutter'
66 |
67 | # Plugin Pods
68 |
69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
70 | # referring to absolute paths on developers' machines.
71 | system('rm -rf .symlinks')
72 | system('mkdir -p .symlinks/plugins')
73 | plugin_pods = parse_KV_file('../.flutter-plugins')
74 | plugin_pods.each do |name, path|
75 | symlink = File.join('.symlinks', 'plugins', name)
76 | File.symlink(path, symlink)
77 | pod name, :path => File.join(symlink, 'ios')
78 | end
79 | end
80 |
81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
82 | install! 'cocoapods', :disable_input_output_paths => true
83 |
84 | post_install do |installer|
85 | installer.pods_project.targets.each do |target|
86 | target.build_configurations.each do |config|
87 | config.build_settings['ENABLE_BITCODE'] = 'NO'
88 | end
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - abseil/algorithm (0.20190808):
3 | - abseil/algorithm/algorithm (= 0.20190808)
4 | - abseil/algorithm/container (= 0.20190808)
5 | - abseil/algorithm/algorithm (0.20190808)
6 | - abseil/algorithm/container (0.20190808):
7 | - abseil/algorithm/algorithm
8 | - abseil/base/core_headers
9 | - abseil/meta/type_traits
10 | - abseil/base (0.20190808):
11 | - abseil/base/atomic_hook (= 0.20190808)
12 | - abseil/base/base (= 0.20190808)
13 | - abseil/base/base_internal (= 0.20190808)
14 | - abseil/base/bits (= 0.20190808)
15 | - abseil/base/config (= 0.20190808)
16 | - abseil/base/core_headers (= 0.20190808)
17 | - abseil/base/dynamic_annotations (= 0.20190808)
18 | - abseil/base/endian (= 0.20190808)
19 | - abseil/base/log_severity (= 0.20190808)
20 | - abseil/base/malloc_internal (= 0.20190808)
21 | - abseil/base/pretty_function (= 0.20190808)
22 | - abseil/base/spinlock_wait (= 0.20190808)
23 | - abseil/base/throw_delegate (= 0.20190808)
24 | - abseil/base/atomic_hook (0.20190808)
25 | - abseil/base/base (0.20190808):
26 | - abseil/base/atomic_hook
27 | - abseil/base/base_internal
28 | - abseil/base/config
29 | - abseil/base/core_headers
30 | - abseil/base/dynamic_annotations
31 | - abseil/base/log_severity
32 | - abseil/base/spinlock_wait
33 | - abseil/meta/type_traits
34 | - abseil/base/base_internal (0.20190808):
35 | - abseil/meta/type_traits
36 | - abseil/base/bits (0.20190808):
37 | - abseil/base/core_headers
38 | - abseil/base/config (0.20190808)
39 | - abseil/base/core_headers (0.20190808):
40 | - abseil/base/config
41 | - abseil/base/dynamic_annotations (0.20190808)
42 | - abseil/base/endian (0.20190808):
43 | - abseil/base/config
44 | - abseil/base/core_headers
45 | - abseil/base/log_severity (0.20190808):
46 | - abseil/base/core_headers
47 | - abseil/base/malloc_internal (0.20190808):
48 | - abseil/base/base
49 | - abseil/base/config
50 | - abseil/base/core_headers
51 | - abseil/base/dynamic_annotations
52 | - abseil/base/spinlock_wait
53 | - abseil/base/pretty_function (0.20190808)
54 | - abseil/base/spinlock_wait (0.20190808):
55 | - abseil/base/core_headers
56 | - abseil/base/throw_delegate (0.20190808):
57 | - abseil/base/base
58 | - abseil/base/config
59 | - abseil/memory (0.20190808):
60 | - abseil/memory/memory (= 0.20190808)
61 | - abseil/memory/memory (0.20190808):
62 | - abseil/base/core_headers
63 | - abseil/meta/type_traits
64 | - abseil/meta (0.20190808):
65 | - abseil/meta/type_traits (= 0.20190808)
66 | - abseil/meta/type_traits (0.20190808):
67 | - abseil/base/config
68 | - abseil/numeric/int128 (0.20190808):
69 | - abseil/base/config
70 | - abseil/base/core_headers
71 | - abseil/strings/internal (0.20190808):
72 | - abseil/base/core_headers
73 | - abseil/base/endian
74 | - abseil/meta/type_traits
75 | - abseil/strings/strings (0.20190808):
76 | - abseil/base/base
77 | - abseil/base/bits
78 | - abseil/base/config
79 | - abseil/base/core_headers
80 | - abseil/base/endian
81 | - abseil/base/throw_delegate
82 | - abseil/memory/memory
83 | - abseil/meta/type_traits
84 | - abseil/numeric/int128
85 | - abseil/strings/internal
86 | - abseil/time (0.20190808):
87 | - abseil/time/internal (= 0.20190808)
88 | - abseil/time/time (= 0.20190808)
89 | - abseil/time/internal (0.20190808):
90 | - abseil/time/internal/cctz (= 0.20190808)
91 | - abseil/time/internal/cctz (0.20190808):
92 | - abseil/time/internal/cctz/civil_time (= 0.20190808)
93 | - abseil/time/internal/cctz/includes (= 0.20190808)
94 | - abseil/time/internal/cctz/time_zone (= 0.20190808)
95 | - abseil/time/internal/cctz/civil_time (0.20190808)
96 | - abseil/time/internal/cctz/includes (0.20190808)
97 | - abseil/time/internal/cctz/time_zone (0.20190808):
98 | - abseil/time/internal/cctz/civil_time
99 | - abseil/time/time (0.20190808):
100 | - abseil/base/base
101 | - abseil/base/core_headers
102 | - abseil/numeric/int128
103 | - abseil/strings/strings
104 | - abseil/time/internal/cctz/civil_time
105 | - abseil/time/internal/cctz/time_zone
106 | - abseil/types (0.20190808):
107 | - abseil/types/any (= 0.20190808)
108 | - abseil/types/bad_any_cast (= 0.20190808)
109 | - abseil/types/bad_any_cast_impl (= 0.20190808)
110 | - abseil/types/bad_optional_access (= 0.20190808)
111 | - abseil/types/bad_variant_access (= 0.20190808)
112 | - abseil/types/compare (= 0.20190808)
113 | - abseil/types/optional (= 0.20190808)
114 | - abseil/types/span (= 0.20190808)
115 | - abseil/types/variant (= 0.20190808)
116 | - abseil/types/any (0.20190808):
117 | - abseil/base/config
118 | - abseil/base/core_headers
119 | - abseil/meta/type_traits
120 | - abseil/types/bad_any_cast
121 | - abseil/utility/utility
122 | - abseil/types/bad_any_cast (0.20190808):
123 | - abseil/base/config
124 | - abseil/types/bad_any_cast_impl
125 | - abseil/types/bad_any_cast_impl (0.20190808):
126 | - abseil/base/base
127 | - abseil/base/config
128 | - abseil/types/bad_optional_access (0.20190808):
129 | - abseil/base/base
130 | - abseil/base/config
131 | - abseil/types/bad_variant_access (0.20190808):
132 | - abseil/base/base
133 | - abseil/base/config
134 | - abseil/types/compare (0.20190808):
135 | - abseil/base/core_headers
136 | - abseil/meta/type_traits
137 | - abseil/types/optional (0.20190808):
138 | - abseil/base/base_internal
139 | - abseil/base/config
140 | - abseil/base/core_headers
141 | - abseil/memory/memory
142 | - abseil/meta/type_traits
143 | - abseil/types/bad_optional_access
144 | - abseil/utility/utility
145 | - abseil/types/span (0.20190808):
146 | - abseil/algorithm/algorithm
147 | - abseil/base/core_headers
148 | - abseil/base/throw_delegate
149 | - abseil/meta/type_traits
150 | - abseil/types/variant (0.20190808):
151 | - abseil/base/base_internal
152 | - abseil/base/config
153 | - abseil/base/core_headers
154 | - abseil/meta/type_traits
155 | - abseil/types/bad_variant_access
156 | - abseil/utility/utility
157 | - abseil/utility/utility (0.20190808):
158 | - abseil/base/base_internal
159 | - abseil/base/config
160 | - abseil/meta/type_traits
161 | - BoringSSL-GRPC (0.0.3):
162 | - BoringSSL-GRPC/Implementation (= 0.0.3)
163 | - BoringSSL-GRPC/Interface (= 0.0.3)
164 | - BoringSSL-GRPC/Implementation (0.0.3):
165 | - BoringSSL-GRPC/Interface (= 0.0.3)
166 | - BoringSSL-GRPC/Interface (0.0.3)
167 | - cloud_firestore (0.0.1):
168 | - Firebase/Core
169 | - Firebase/Firestore (~> 6.0)
170 | - Flutter
171 | - Firebase/Analytics (6.13.0):
172 | - Firebase/Core
173 | - Firebase/Auth (6.13.0):
174 | - Firebase/CoreOnly
175 | - FirebaseAuth (~> 6.4.0)
176 | - Firebase/Core (6.13.0):
177 | - Firebase/CoreOnly
178 | - FirebaseAnalytics (= 6.1.6)
179 | - Firebase/CoreOnly (6.13.0):
180 | - FirebaseCore (= 6.4.0)
181 | - Firebase/Firestore (6.13.0):
182 | - Firebase/CoreOnly
183 | - FirebaseFirestore (~> 1.8.0)
184 | - Firebase/Storage (6.13.0):
185 | - Firebase/CoreOnly
186 | - FirebaseStorage (~> 3.4.2)
187 | - firebase_analytics (0.0.1):
188 | - Firebase/Analytics (~> 6.0)
189 | - Firebase/Core
190 | - Flutter
191 | - firebase_auth (0.0.1):
192 | - Firebase/Auth (~> 6.0)
193 | - Firebase/Core
194 | - Flutter
195 | - firebase_core (0.0.1):
196 | - Firebase/Core
197 | - Flutter
198 | - firebase_core_web (0.1.0):
199 | - Flutter
200 | - firebase_storage (0.0.1):
201 | - Firebase/Storage
202 | - Flutter
203 | - FirebaseAnalytics (6.1.6):
204 | - FirebaseCore (~> 6.4)
205 | - FirebaseInstanceID (~> 4.2)
206 | - GoogleAppMeasurement (= 6.1.6)
207 | - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
208 | - GoogleUtilities/MethodSwizzler (~> 6.0)
209 | - GoogleUtilities/Network (~> 6.0)
210 | - "GoogleUtilities/NSData+zlib (~> 6.0)"
211 | - nanopb (= 0.3.9011)
212 | - FirebaseAuth (6.4.0):
213 | - FirebaseAuthInterop (~> 1.0)
214 | - FirebaseCore (~> 6.2)
215 | - GoogleUtilities/AppDelegateSwizzler (~> 6.2)
216 | - GoogleUtilities/Environment (~> 6.2)
217 | - GTMSessionFetcher/Core (~> 1.1)
218 | - FirebaseAuthInterop (1.0.0)
219 | - FirebaseCore (6.4.0):
220 | - FirebaseCoreDiagnostics (~> 1.0)
221 | - FirebaseCoreDiagnosticsInterop (~> 1.0)
222 | - GoogleUtilities/Environment (~> 6.2)
223 | - GoogleUtilities/Logger (~> 6.2)
224 | - FirebaseCoreDiagnostics (1.1.2):
225 | - FirebaseCoreDiagnosticsInterop (~> 1.0)
226 | - GoogleDataTransportCCTSupport (~> 1.0)
227 | - GoogleUtilities/Environment (~> 6.2)
228 | - GoogleUtilities/Logger (~> 6.2)
229 | - nanopb (~> 0.3.901)
230 | - FirebaseCoreDiagnosticsInterop (1.1.0)
231 | - FirebaseFirestore (1.8.1):
232 | - abseil/algorithm (= 0.20190808)
233 | - abseil/base (= 0.20190808)
234 | - abseil/memory (= 0.20190808)
235 | - abseil/meta (= 0.20190808)
236 | - abseil/strings/strings (= 0.20190808)
237 | - abseil/time (= 0.20190808)
238 | - abseil/types (= 0.20190808)
239 | - FirebaseAuthInterop (~> 1.0)
240 | - FirebaseCore (~> 6.2)
241 | - "gRPC-C++ (= 0.0.9)"
242 | - leveldb-library (~> 1.22)
243 | - nanopb (~> 0.3.901)
244 | - FirebaseInstanceID (4.2.7):
245 | - FirebaseCore (~> 6.0)
246 | - GoogleUtilities/Environment (~> 6.0)
247 | - GoogleUtilities/UserDefaults (~> 6.0)
248 | - FirebaseStorage (3.4.2):
249 | - FirebaseAuthInterop (~> 1.0)
250 | - FirebaseCore (~> 6.0)
251 | - GTMSessionFetcher/Core (~> 1.1)
252 | - Flutter (1.0.0)
253 | - flutter_image_compress (0.0.1):
254 | - Flutter
255 | - Mantle
256 | - SDWebImageWebPCoder
257 | - flutter_plugin_android_lifecycle (0.0.1):
258 | - Flutter
259 | - FMDB (2.7.5):
260 | - FMDB/standard (= 2.7.5)
261 | - FMDB/standard (2.7.5)
262 | - GoogleAppMeasurement (6.1.6):
263 | - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
264 | - GoogleUtilities/MethodSwizzler (~> 6.0)
265 | - GoogleUtilities/Network (~> 6.0)
266 | - "GoogleUtilities/NSData+zlib (~> 6.0)"
267 | - nanopb (= 0.3.9011)
268 | - GoogleDataTransport (3.2.0)
269 | - GoogleDataTransportCCTSupport (1.2.2):
270 | - GoogleDataTransport (~> 3.2)
271 | - nanopb (~> 0.3.901)
272 | - GoogleUtilities/AppDelegateSwizzler (6.3.2):
273 | - GoogleUtilities/Environment
274 | - GoogleUtilities/Logger
275 | - GoogleUtilities/Network
276 | - GoogleUtilities/Environment (6.3.2)
277 | - GoogleUtilities/Logger (6.3.2):
278 | - GoogleUtilities/Environment
279 | - GoogleUtilities/MethodSwizzler (6.3.2):
280 | - GoogleUtilities/Logger
281 | - GoogleUtilities/Network (6.3.2):
282 | - GoogleUtilities/Logger
283 | - "GoogleUtilities/NSData+zlib"
284 | - GoogleUtilities/Reachability
285 | - "GoogleUtilities/NSData+zlib (6.3.2)"
286 | - GoogleUtilities/Reachability (6.3.2):
287 | - GoogleUtilities/Logger
288 | - GoogleUtilities/UserDefaults (6.3.2):
289 | - GoogleUtilities/Logger
290 | - "gRPC-C++ (0.0.9)":
291 | - "gRPC-C++/Implementation (= 0.0.9)"
292 | - "gRPC-C++/Interface (= 0.0.9)"
293 | - "gRPC-C++/Implementation (0.0.9)":
294 | - "gRPC-C++/Interface (= 0.0.9)"
295 | - gRPC-Core (= 1.21.0)
296 | - nanopb (~> 0.3)
297 | - "gRPC-C++/Interface (0.0.9)"
298 | - gRPC-Core (1.21.0):
299 | - gRPC-Core/Implementation (= 1.21.0)
300 | - gRPC-Core/Interface (= 1.21.0)
301 | - gRPC-Core/Implementation (1.21.0):
302 | - BoringSSL-GRPC (= 0.0.3)
303 | - gRPC-Core/Interface (= 1.21.0)
304 | - nanopb (~> 0.3)
305 | - gRPC-Core/Interface (1.21.0)
306 | - GTMSessionFetcher/Core (1.3.0)
307 | - image_cropper (0.0.2):
308 | - Flutter
309 | - TOCropViewController (~> 2.5.2)
310 | - image_picker (0.0.1):
311 | - Flutter
312 | - leveldb-library (1.22)
313 | - libwebp (1.1.0):
314 | - libwebp/demux (= 1.1.0)
315 | - libwebp/mux (= 1.1.0)
316 | - libwebp/webp (= 1.1.0)
317 | - libwebp/demux (1.1.0):
318 | - libwebp/webp
319 | - libwebp/mux (1.1.0):
320 | - libwebp/demux
321 | - libwebp/webp (1.1.0)
322 | - Mantle (2.1.0):
323 | - Mantle/extobjc (= 2.1.0)
324 | - Mantle/extobjc (2.1.0)
325 | - nanopb (0.3.9011):
326 | - nanopb/decode (= 0.3.9011)
327 | - nanopb/encode (= 0.3.9011)
328 | - nanopb/decode (0.3.9011)
329 | - nanopb/encode (0.3.9011)
330 | - path_provider (0.0.1):
331 | - Flutter
332 | - path_provider_macos (0.0.1):
333 | - Flutter
334 | - SDWebImage/Core (5.8.0)
335 | - SDWebImageWebPCoder (0.6.1):
336 | - libwebp (~> 1.0)
337 | - SDWebImage/Core (~> 5.7)
338 | - sqflite (0.0.1):
339 | - Flutter
340 | - FMDB (~> 2.7.2)
341 | - TOCropViewController (2.5.2)
342 |
343 | DEPENDENCIES:
344 | - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
345 | - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
346 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
347 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
348 | - firebase_core_web (from `.symlinks/plugins/firebase_core_web/ios`)
349 | - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
350 | - Flutter (from `Flutter`)
351 | - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`)
352 | - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`)
353 | - image_cropper (from `.symlinks/plugins/image_cropper/ios`)
354 | - image_picker (from `.symlinks/plugins/image_picker/ios`)
355 | - path_provider (from `.symlinks/plugins/path_provider/ios`)
356 | - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
357 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
358 |
359 | SPEC REPOS:
360 | https://github.com/CocoaPods/Specs.git:
361 | - abseil
362 | - BoringSSL-GRPC
363 | - Firebase
364 | - FirebaseAnalytics
365 | - FirebaseAuth
366 | - FirebaseAuthInterop
367 | - FirebaseCore
368 | - FirebaseCoreDiagnostics
369 | - FirebaseCoreDiagnosticsInterop
370 | - FirebaseFirestore
371 | - FirebaseInstanceID
372 | - FirebaseStorage
373 | - FMDB
374 | - GoogleAppMeasurement
375 | - GoogleDataTransport
376 | - GoogleDataTransportCCTSupport
377 | - GoogleUtilities
378 | - "gRPC-C++"
379 | - gRPC-Core
380 | - GTMSessionFetcher
381 | - leveldb-library
382 | - Mantle
383 | - nanopb
384 | - TOCropViewController
385 | trunk:
386 | - libwebp
387 | - SDWebImage
388 | - SDWebImageWebPCoder
389 |
390 | EXTERNAL SOURCES:
391 | cloud_firestore:
392 | :path: ".symlinks/plugins/cloud_firestore/ios"
393 | firebase_analytics:
394 | :path: ".symlinks/plugins/firebase_analytics/ios"
395 | firebase_auth:
396 | :path: ".symlinks/plugins/firebase_auth/ios"
397 | firebase_core:
398 | :path: ".symlinks/plugins/firebase_core/ios"
399 | firebase_core_web:
400 | :path: ".symlinks/plugins/firebase_core_web/ios"
401 | firebase_storage:
402 | :path: ".symlinks/plugins/firebase_storage/ios"
403 | Flutter:
404 | :path: Flutter
405 | flutter_image_compress:
406 | :path: ".symlinks/plugins/flutter_image_compress/ios"
407 | flutter_plugin_android_lifecycle:
408 | :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios"
409 | image_cropper:
410 | :path: ".symlinks/plugins/image_cropper/ios"
411 | image_picker:
412 | :path: ".symlinks/plugins/image_picker/ios"
413 | path_provider:
414 | :path: ".symlinks/plugins/path_provider/ios"
415 | path_provider_macos:
416 | :path: ".symlinks/plugins/path_provider_macos/ios"
417 | sqflite:
418 | :path: ".symlinks/plugins/sqflite/ios"
419 |
420 | SPEC CHECKSUMS:
421 | abseil: 18063d773f5366ff8736a050fe035a28f635fd27
422 | BoringSSL-GRPC: db8764df3204ccea016e1c8dd15d9a9ad63ff318
423 | cloud_firestore: 10fd4a09ebffc33efd64708106e2e7de701fe9ae
424 | Firebase: 458d109512200d1aca2e1b9b6cf7d68a869a4a46
425 | firebase_analytics: dacdcfc524d722fff13dcff942f0dfa47e6be567
426 | firebase_auth: d99b993c1405096e66c58211b1cd956c23eed1c5
427 | firebase_core: 0d8be0e0d14c4902953aeb5ac5d7316d1fe4b978
428 | firebase_core_web: d501d8b946b60c8af265428ce483b0fff5ad52d1
429 | firebase_storage: 5e931af5cdef32331676c659bdd1ebcaba9dc78a
430 | FirebaseAnalytics: 45f36d9c429fc91d206283900ab75390cd05ee8a
431 | FirebaseAuth: 7d0f84873926f6648bbd1391a318dfb1a26b5e4f
432 | FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc
433 | FirebaseCore: 307ea2508df730c5865334e41965bd9ea344b0e5
434 | FirebaseCoreDiagnostics: 511f4f3ed7d440bb69127e8b97c2bc8befae639e
435 | FirebaseCoreDiagnosticsInterop: e9b1b023157e3a2fc6418b5cb601e79b9af7b3a0
436 | FirebaseFirestore: 2e92e977280d63ecbf3fd58bdbfaf9145abb880f
437 | FirebaseInstanceID: ebd2ea79ee38db0cb5f5167b17a0d387e1cc7b6e
438 | FirebaseStorage: 046abe9ac4e2a1a0e47d72f536490ffa50896632
439 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
440 | flutter_image_compress: 082f8daaf6c1b0c9fe798251c750ef0ecd98d7ae
441 | flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35
442 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
443 | GoogleAppMeasurement: dfe55efa543e899d906309eaaac6ca26d249862f
444 | GoogleDataTransport: 8e9b210c97d55fbff306cc5468ff91b9cb32dcf5
445 | GoogleDataTransportCCTSupport: ef79a4728b864946a8aafdbab770d5294faf3b5f
446 | GoogleUtilities: 547a86735c6f0ee30ad17e94df4fc21f616b71cb
447 | "gRPC-C++": 9dfe7b44821e7b3e44aacad2af29d2c21f7cde83
448 | gRPC-Core: c9aef9a261a1247e881b18059b84d597293c9947
449 | GTMSessionFetcher: 43b8b64263023d4f32caa0b40f4c8bfa3c5f36d8
450 | image_cropper: 3c16d7651730ffe85897f5a1c4e2547e6b54989a
451 | image_picker: 66aa71bc96850a90590a35d4c4a2907b0d823109
452 | leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7
453 | libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
454 | Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b
455 | nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd
456 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
457 | path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
458 | SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc
459 | SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
460 | sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0
461 | TOCropViewController: e9da34f484aedd4e5d5a8ab230ba217cfe16c729
462 |
463 | PODFILE CHECKSUM: 1b66dae606f75376c5f2135a8290850eeb09ae83
464 |
465 | COCOAPODS: 1.9.1
466 |
--------------------------------------------------------------------------------
/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 | 5FBE55EB23349021000F9E35 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5FBE55EA23349021000F9E35 /* GoogleService-Info.plist */; };
13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
14 | 78793A16DF1F1A82170B3DE9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 865E1BE3AA309E4275C1DB71 /* Pods_Runner.framework */; };
15 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
16 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
17 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
18 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
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 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
38 | 3E039AA066500859DF7905B8 /* 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 = ""; };
39 | 5FBE55EA23349021000F9E35 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
40 | 643161AA6B9F36D1527B62B8 /* 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 = ""; };
41 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
42 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
43 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
44 | 865E1BE3AA309E4275C1DB71 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
45 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
46 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
47 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
48 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
49 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
51 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | C1E78B0CE9FB62F5B43A8522 /* 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 = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | 78793A16DF1F1A82170B3DE9 /* Pods_Runner.framework in Frameworks */,
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | /* End PBXFrameworksBuildPhase section */
65 |
66 | /* Begin PBXGroup section */
67 | 3D844193235670F462BED8D2 /* Frameworks */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 865E1BE3AA309E4275C1DB71 /* Pods_Runner.framework */,
71 | );
72 | name = Frameworks;
73 | sourceTree = "";
74 | };
75 | 68E51E5CEF045D3B87612DC0 /* Pods */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 643161AA6B9F36D1527B62B8 /* Pods-Runner.debug.xcconfig */,
79 | 3E039AA066500859DF7905B8 /* Pods-Runner.release.xcconfig */,
80 | C1E78B0CE9FB62F5B43A8522 /* Pods-Runner.profile.xcconfig */,
81 | );
82 | path = Pods;
83 | sourceTree = "";
84 | };
85 | 9740EEB11CF90186004384FC /* Flutter */ = {
86 | isa = PBXGroup;
87 | children = (
88 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
89 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
90 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
91 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
92 | );
93 | name = Flutter;
94 | sourceTree = "";
95 | };
96 | 97C146E51CF9000F007C117D = {
97 | isa = PBXGroup;
98 | children = (
99 | 9740EEB11CF90186004384FC /* Flutter */,
100 | 97C146F01CF9000F007C117D /* Runner */,
101 | 97C146EF1CF9000F007C117D /* Products */,
102 | 68E51E5CEF045D3B87612DC0 /* Pods */,
103 | 3D844193235670F462BED8D2 /* Frameworks */,
104 | );
105 | sourceTree = "";
106 | };
107 | 97C146EF1CF9000F007C117D /* Products */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 97C146EE1CF9000F007C117D /* Runner.app */,
111 | );
112 | name = Products;
113 | sourceTree = "";
114 | };
115 | 97C146F01CF9000F007C117D /* Runner */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
119 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
120 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
121 | 97C147021CF9000F007C117D /* Info.plist */,
122 | 5FBE55EA23349021000F9E35 /* GoogleService-Info.plist */,
123 | 97C146F11CF9000F007C117D /* Supporting Files */,
124 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
125 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
126 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
127 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
128 | );
129 | path = Runner;
130 | sourceTree = "";
131 | };
132 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
133 | isa = PBXGroup;
134 | children = (
135 | );
136 | name = "Supporting Files";
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 | E95A4F4A575A15A18A85F306 /* [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 | 890BBE6022408E9DA3C0CCC9 /* [CP] Embed Pods Frameworks */,
154 | );
155 | buildRules = (
156 | );
157 | dependencies = (
158 | );
159 | name = Runner;
160 | productName = Runner;
161 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
162 | productType = "com.apple.product-type.application";
163 | };
164 | /* End PBXNativeTarget section */
165 |
166 | /* Begin PBXProject section */
167 | 97C146E61CF9000F007C117D /* Project object */ = {
168 | isa = PBXProject;
169 | attributes = {
170 | LastUpgradeCheck = 1020;
171 | ORGANIZATIONNAME = "The Chromium Authors";
172 | TargetAttributes = {
173 | 97C146ED1CF9000F007C117D = {
174 | CreatedOnToolsVersion = 7.3.1;
175 | LastSwiftMigration = 0910;
176 | };
177 | };
178 | };
179 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
180 | compatibilityVersion = "Xcode 3.2";
181 | developmentRegion = en;
182 | hasScannedForEncodings = 0;
183 | knownRegions = (
184 | en,
185 | Base,
186 | );
187 | mainGroup = 97C146E51CF9000F007C117D;
188 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
189 | projectDirPath = "";
190 | projectRoot = "";
191 | targets = (
192 | 97C146ED1CF9000F007C117D /* Runner */,
193 | );
194 | };
195 | /* End PBXProject section */
196 |
197 | /* Begin PBXResourcesBuildPhase section */
198 | 97C146EC1CF9000F007C117D /* Resources */ = {
199 | isa = PBXResourcesBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
203 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
204 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
205 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
206 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
207 | 5FBE55EB23349021000F9E35 /* GoogleService-Info.plist in Resources */,
208 | );
209 | runOnlyForDeploymentPostprocessing = 0;
210 | };
211 | /* End PBXResourcesBuildPhase section */
212 |
213 | /* Begin PBXShellScriptBuildPhase section */
214 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
215 | isa = PBXShellScriptBuildPhase;
216 | buildActionMask = 2147483647;
217 | files = (
218 | );
219 | inputPaths = (
220 | );
221 | name = "Thin Binary";
222 | outputPaths = (
223 | );
224 | runOnlyForDeploymentPostprocessing = 0;
225 | shellPath = /bin/sh;
226 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
227 | };
228 | 890BBE6022408E9DA3C0CCC9 /* [CP] Embed Pods Frameworks */ = {
229 | isa = PBXShellScriptBuildPhase;
230 | buildActionMask = 2147483647;
231 | files = (
232 | );
233 | inputPaths = (
234 | );
235 | name = "[CP] Embed Pods Frameworks";
236 | outputPaths = (
237 | );
238 | runOnlyForDeploymentPostprocessing = 0;
239 | shellPath = /bin/sh;
240 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
241 | showEnvVarsInLog = 0;
242 | };
243 | 9740EEB61CF901F6004384FC /* Run Script */ = {
244 | isa = PBXShellScriptBuildPhase;
245 | buildActionMask = 2147483647;
246 | files = (
247 | );
248 | inputPaths = (
249 | );
250 | name = "Run Script";
251 | outputPaths = (
252 | );
253 | runOnlyForDeploymentPostprocessing = 0;
254 | shellPath = /bin/sh;
255 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
256 | };
257 | E95A4F4A575A15A18A85F306 /* [CP] Check Pods Manifest.lock */ = {
258 | isa = PBXShellScriptBuildPhase;
259 | buildActionMask = 2147483647;
260 | files = (
261 | );
262 | inputFileListPaths = (
263 | );
264 | inputPaths = (
265 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
266 | "${PODS_ROOT}/Manifest.lock",
267 | );
268 | name = "[CP] Check Pods Manifest.lock";
269 | outputFileListPaths = (
270 | );
271 | outputPaths = (
272 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | shellPath = /bin/sh;
276 | 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";
277 | showEnvVarsInLog = 0;
278 | };
279 | /* End PBXShellScriptBuildPhase section */
280 |
281 | /* Begin PBXSourcesBuildPhase section */
282 | 97C146EA1CF9000F007C117D /* Sources */ = {
283 | isa = PBXSourcesBuildPhase;
284 | buildActionMask = 2147483647;
285 | files = (
286 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
287 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
288 | );
289 | runOnlyForDeploymentPostprocessing = 0;
290 | };
291 | /* End PBXSourcesBuildPhase section */
292 |
293 | /* Begin PBXVariantGroup section */
294 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
295 | isa = PBXVariantGroup;
296 | children = (
297 | 97C146FB1CF9000F007C117D /* Base */,
298 | );
299 | name = Main.storyboard;
300 | sourceTree = "";
301 | };
302 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
303 | isa = PBXVariantGroup;
304 | children = (
305 | 97C147001CF9000F007C117D /* Base */,
306 | );
307 | name = LaunchScreen.storyboard;
308 | sourceTree = "";
309 | };
310 | /* End PBXVariantGroup section */
311 |
312 | /* Begin XCBuildConfiguration section */
313 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
314 | isa = XCBuildConfiguration;
315 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
316 | buildSettings = {
317 | ALWAYS_SEARCH_USER_PATHS = NO;
318 | CLANG_ANALYZER_NONNULL = YES;
319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
320 | CLANG_CXX_LIBRARY = "libc++";
321 | CLANG_ENABLE_MODULES = YES;
322 | CLANG_ENABLE_OBJC_ARC = YES;
323 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
324 | CLANG_WARN_BOOL_CONVERSION = YES;
325 | CLANG_WARN_COMMA = YES;
326 | CLANG_WARN_CONSTANT_CONVERSION = YES;
327 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
328 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
329 | CLANG_WARN_EMPTY_BODY = YES;
330 | CLANG_WARN_ENUM_CONVERSION = YES;
331 | CLANG_WARN_INFINITE_RECURSION = YES;
332 | CLANG_WARN_INT_CONVERSION = YES;
333 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
334 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
335 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
337 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
338 | CLANG_WARN_STRICT_PROTOTYPES = YES;
339 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
340 | CLANG_WARN_UNREACHABLE_CODE = YES;
341 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
342 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
343 | COPY_PHASE_STRIP = NO;
344 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
345 | ENABLE_NS_ASSERTIONS = NO;
346 | ENABLE_STRICT_OBJC_MSGSEND = YES;
347 | GCC_C_LANGUAGE_STANDARD = gnu99;
348 | GCC_NO_COMMON_BLOCKS = YES;
349 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
350 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
351 | GCC_WARN_UNDECLARED_SELECTOR = YES;
352 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
353 | GCC_WARN_UNUSED_FUNCTION = YES;
354 | GCC_WARN_UNUSED_VARIABLE = YES;
355 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
356 | MTL_ENABLE_DEBUG_INFO = NO;
357 | SDKROOT = iphoneos;
358 | TARGETED_DEVICE_FAMILY = "1,2";
359 | VALIDATE_PRODUCT = YES;
360 | };
361 | name = Profile;
362 | };
363 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
364 | isa = XCBuildConfiguration;
365 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
366 | buildSettings = {
367 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
368 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
369 | DEVELOPMENT_TEAM = "";
370 | ENABLE_BITCODE = NO;
371 | FRAMEWORK_SEARCH_PATHS = (
372 | "$(inherited)",
373 | "$(PROJECT_DIR)/Flutter",
374 | );
375 | INFOPLIST_FILE = Runner/Info.plist;
376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
377 | LIBRARY_SEARCH_PATHS = (
378 | "$(inherited)",
379 | "$(PROJECT_DIR)/Flutter",
380 | );
381 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterInstagramClone;
382 | PRODUCT_NAME = "$(TARGET_NAME)";
383 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
384 | SWIFT_VERSION = 4.0;
385 | VERSIONING_SYSTEM = "apple-generic";
386 | };
387 | name = Profile;
388 | };
389 | 97C147031CF9000F007C117D /* Debug */ = {
390 | isa = XCBuildConfiguration;
391 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
392 | buildSettings = {
393 | ALWAYS_SEARCH_USER_PATHS = NO;
394 | CLANG_ANALYZER_NONNULL = YES;
395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
396 | CLANG_CXX_LIBRARY = "libc++";
397 | CLANG_ENABLE_MODULES = YES;
398 | CLANG_ENABLE_OBJC_ARC = YES;
399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
400 | CLANG_WARN_BOOL_CONVERSION = YES;
401 | CLANG_WARN_COMMA = YES;
402 | CLANG_WARN_CONSTANT_CONVERSION = YES;
403 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
405 | CLANG_WARN_EMPTY_BODY = YES;
406 | CLANG_WARN_ENUM_CONVERSION = YES;
407 | CLANG_WARN_INFINITE_RECURSION = YES;
408 | CLANG_WARN_INT_CONVERSION = YES;
409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
410 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
414 | CLANG_WARN_STRICT_PROTOTYPES = YES;
415 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
416 | CLANG_WARN_UNREACHABLE_CODE = YES;
417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
418 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
419 | COPY_PHASE_STRIP = NO;
420 | DEBUG_INFORMATION_FORMAT = dwarf;
421 | ENABLE_STRICT_OBJC_MSGSEND = YES;
422 | ENABLE_TESTABILITY = YES;
423 | GCC_C_LANGUAGE_STANDARD = gnu99;
424 | GCC_DYNAMIC_NO_PIC = NO;
425 | GCC_NO_COMMON_BLOCKS = YES;
426 | GCC_OPTIMIZATION_LEVEL = 0;
427 | GCC_PREPROCESSOR_DEFINITIONS = (
428 | "DEBUG=1",
429 | "$(inherited)",
430 | );
431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
433 | GCC_WARN_UNDECLARED_SELECTOR = YES;
434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
435 | GCC_WARN_UNUSED_FUNCTION = YES;
436 | GCC_WARN_UNUSED_VARIABLE = YES;
437 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
438 | MTL_ENABLE_DEBUG_INFO = YES;
439 | ONLY_ACTIVE_ARCH = YES;
440 | SDKROOT = iphoneos;
441 | TARGETED_DEVICE_FAMILY = "1,2";
442 | };
443 | name = Debug;
444 | };
445 | 97C147041CF9000F007C117D /* Release */ = {
446 | isa = XCBuildConfiguration;
447 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
448 | buildSettings = {
449 | ALWAYS_SEARCH_USER_PATHS = NO;
450 | CLANG_ANALYZER_NONNULL = YES;
451 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
452 | CLANG_CXX_LIBRARY = "libc++";
453 | CLANG_ENABLE_MODULES = YES;
454 | CLANG_ENABLE_OBJC_ARC = YES;
455 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
456 | CLANG_WARN_BOOL_CONVERSION = YES;
457 | CLANG_WARN_COMMA = YES;
458 | CLANG_WARN_CONSTANT_CONVERSION = YES;
459 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
460 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
461 | CLANG_WARN_EMPTY_BODY = YES;
462 | CLANG_WARN_ENUM_CONVERSION = YES;
463 | CLANG_WARN_INFINITE_RECURSION = YES;
464 | CLANG_WARN_INT_CONVERSION = YES;
465 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
466 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
467 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
469 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
470 | CLANG_WARN_STRICT_PROTOTYPES = YES;
471 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
472 | CLANG_WARN_UNREACHABLE_CODE = YES;
473 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
474 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
475 | COPY_PHASE_STRIP = NO;
476 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
477 | ENABLE_NS_ASSERTIONS = NO;
478 | ENABLE_STRICT_OBJC_MSGSEND = YES;
479 | GCC_C_LANGUAGE_STANDARD = gnu99;
480 | GCC_NO_COMMON_BLOCKS = YES;
481 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
482 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
483 | GCC_WARN_UNDECLARED_SELECTOR = YES;
484 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
485 | GCC_WARN_UNUSED_FUNCTION = YES;
486 | GCC_WARN_UNUSED_VARIABLE = YES;
487 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
488 | MTL_ENABLE_DEBUG_INFO = NO;
489 | SDKROOT = iphoneos;
490 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
491 | TARGETED_DEVICE_FAMILY = "1,2";
492 | VALIDATE_PRODUCT = YES;
493 | };
494 | name = Release;
495 | };
496 | 97C147061CF9000F007C117D /* Debug */ = {
497 | isa = XCBuildConfiguration;
498 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
499 | buildSettings = {
500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
501 | CLANG_ENABLE_MODULES = YES;
502 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
503 | DEVELOPMENT_TEAM = "";
504 | ENABLE_BITCODE = NO;
505 | FRAMEWORK_SEARCH_PATHS = (
506 | "$(inherited)",
507 | "$(PROJECT_DIR)/Flutter",
508 | );
509 | INFOPLIST_FILE = Runner/Info.plist;
510 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
511 | LIBRARY_SEARCH_PATHS = (
512 | "$(inherited)",
513 | "$(PROJECT_DIR)/Flutter",
514 | );
515 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterInstagramClone;
516 | PRODUCT_NAME = "$(TARGET_NAME)";
517 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
518 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
519 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
520 | SWIFT_VERSION = 4.0;
521 | VERSIONING_SYSTEM = "apple-generic";
522 | };
523 | name = Debug;
524 | };
525 | 97C147071CF9000F007C117D /* Release */ = {
526 | isa = XCBuildConfiguration;
527 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
528 | buildSettings = {
529 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
530 | CLANG_ENABLE_MODULES = YES;
531 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
532 | DEVELOPMENT_TEAM = "";
533 | ENABLE_BITCODE = NO;
534 | FRAMEWORK_SEARCH_PATHS = (
535 | "$(inherited)",
536 | "$(PROJECT_DIR)/Flutter",
537 | );
538 | INFOPLIST_FILE = Runner/Info.plist;
539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
540 | LIBRARY_SEARCH_PATHS = (
541 | "$(inherited)",
542 | "$(PROJECT_DIR)/Flutter",
543 | );
544 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterInstagramClone;
545 | PRODUCT_NAME = "$(TARGET_NAME)";
546 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
547 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
548 | SWIFT_VERSION = 4.0;
549 | VERSIONING_SYSTEM = "apple-generic";
550 | };
551 | name = Release;
552 | };
553 | /* End XCBuildConfiguration section */
554 |
555 | /* Begin XCConfigurationList section */
556 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
557 | isa = XCConfigurationList;
558 | buildConfigurations = (
559 | 97C147031CF9000F007C117D /* Debug */,
560 | 97C147041CF9000F007C117D /* Release */,
561 | 249021D3217E4FDB00AE95B9 /* Profile */,
562 | );
563 | defaultConfigurationIsVisible = 0;
564 | defaultConfigurationName = Release;
565 | };
566 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
567 | isa = XCConfigurationList;
568 | buildConfigurations = (
569 | 97C147061CF9000F007C117D /* Debug */,
570 | 97C147071CF9000F007C117D /* Release */,
571 | 249021D4217E4FDB00AE95B9 /* Profile */,
572 | );
573 | defaultConfigurationIsVisible = 0;
574 | defaultConfigurationName = Release;
575 | };
576 | /* End XCConfigurationList section */
577 | };
578 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
579 | }
580 |
--------------------------------------------------------------------------------
/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 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcusNg/flutter_instagram_clone/c15b9e5ddc9c0ec66d7f3b84c0d4e9440f5abdac/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 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_instagram_clone
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | NSPhotoLibraryUsageDescription
45 | Post and share photos
46 | NSCameraUsageDescription
47 | Post and share photos
48 |
49 |
50 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:firebase_auth/firebase_auth.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_instagram_clone/models/user_data.dart';
4 | import 'package:flutter_instagram_clone/screens/feed_screen.dart';
5 | import 'package:flutter_instagram_clone/screens/home_screen.dart';
6 | import 'package:flutter_instagram_clone/screens/login_screen.dart';
7 | import 'package:flutter_instagram_clone/screens/signup_screen.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | void main() => runApp(MyApp());
11 |
12 | class MyApp extends StatelessWidget {
13 | Widget _getScreenId() {
14 | return StreamBuilder(
15 | stream: FirebaseAuth.instance.onAuthStateChanged,
16 | builder: (BuildContext context, snapshot) {
17 | if (snapshot.hasData) {
18 | Provider.of(context).currentUserId = snapshot.data.uid;
19 | return HomeScreen();
20 | } else {
21 | return LoginScreen();
22 | }
23 | },
24 | );
25 | }
26 |
27 | // This widget is the root of your application.
28 | @override
29 | Widget build(BuildContext context) {
30 | return ChangeNotifierProvider(
31 | create: (context) => UserData(),
32 | child: MaterialApp(
33 | title: 'Instagram Clone',
34 | debugShowCheckedModeBanner: false,
35 | theme: ThemeData(
36 | primaryIconTheme: Theme.of(context).primaryIconTheme.copyWith(
37 | color: Colors.black,
38 | ),
39 | ),
40 | home: _getScreenId(),
41 | routes: {
42 | LoginScreen.id: (context) => LoginScreen(),
43 | SignupScreen.id: (context) => SignupScreen(),
44 | FeedScreen.id: (context) => FeedScreen(),
45 | },
46 | ),
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/models/activity_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 |
3 | class Activity {
4 | final String id;
5 | final String fromUserId;
6 | final String postId;
7 | final String postImageUrl;
8 | final String comment;
9 | final Timestamp timestamp;
10 |
11 | Activity({
12 | this.id,
13 | this.fromUserId,
14 | this.postId,
15 | this.postImageUrl,
16 | this.comment,
17 | this.timestamp,
18 | });
19 |
20 | factory Activity.fromDoc(DocumentSnapshot doc) {
21 | return Activity(
22 | id: doc.documentID,
23 | fromUserId: doc['fromUserId'],
24 | postId: doc['postId'],
25 | postImageUrl: doc['postImageUrl'],
26 | comment: doc['comment'],
27 | timestamp: doc['timestamp'],
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/models/comment_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 |
3 | class Comment {
4 | final String id;
5 | final String content;
6 | final String authorId;
7 | final Timestamp timestamp;
8 |
9 | Comment({
10 | this.id,
11 | this.content,
12 | this.authorId,
13 | this.timestamp,
14 | });
15 |
16 | factory Comment.fromDoc(DocumentSnapshot doc) {
17 | return Comment(
18 | id: doc.documentID,
19 | content: doc['content'],
20 | authorId: doc['authorId'],
21 | timestamp: doc['timestamp'],
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/models/post_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 |
3 | class Post {
4 | final String id;
5 | final String imageUrl;
6 | final String caption;
7 | final int likeCount;
8 | final String authorId;
9 | final Timestamp timestamp;
10 |
11 | Post({
12 | this.id,
13 | this.imageUrl,
14 | this.caption,
15 | this.likeCount,
16 | this.authorId,
17 | this.timestamp,
18 | });
19 |
20 | factory Post.fromDoc(DocumentSnapshot doc) {
21 | return Post(
22 | id: doc.documentID,
23 | imageUrl: doc['imageUrl'],
24 | caption: doc['caption'],
25 | likeCount: doc['likeCount'],
26 | authorId: doc['authorId'],
27 | timestamp: doc['timestamp'],
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/models/user_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 |
3 | class UserData extends ChangeNotifier {
4 |
5 | String currentUserId;
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/lib/models/user_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 |
3 | class User {
4 | final String id;
5 | final String name;
6 | final String profileImageUrl;
7 | final String email;
8 | final String bio;
9 |
10 | User({
11 | this.id,
12 | this.name,
13 | this.profileImageUrl,
14 | this.email,
15 | this.bio,
16 | });
17 |
18 | factory User.fromDoc(DocumentSnapshot doc) {
19 | return User(
20 | id: doc.documentID,
21 | name: doc['name'],
22 | profileImageUrl: doc['profileImageUrl'],
23 | email: doc['email'],
24 | bio: doc['bio'] ?? '',
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/screens/activity_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_instagram_clone/models/activity_model.dart';
4 | import 'package:flutter_instagram_clone/models/post_model.dart';
5 | import 'package:flutter_instagram_clone/models/user_data.dart';
6 | import 'package:flutter_instagram_clone/models/user_model.dart';
7 | import 'package:flutter_instagram_clone/screens/comments_screen.dart';
8 | import 'package:flutter_instagram_clone/services/database_service.dart';
9 | import 'package:intl/intl.dart';
10 | import 'package:provider/provider.dart';
11 |
12 | class ActivityScreen extends StatefulWidget {
13 | final String currentUserId;
14 |
15 | ActivityScreen({this.currentUserId});
16 |
17 | @override
18 | _ActivityScreenState createState() => _ActivityScreenState();
19 | }
20 |
21 | class _ActivityScreenState extends State {
22 | List _activities = [];
23 |
24 | @override
25 | void initState() {
26 | super.initState();
27 | _setupActivities();
28 | }
29 |
30 | _setupActivities() async {
31 | List activities =
32 | await DatabaseService.getActivities(widget.currentUserId);
33 | if (mounted) {
34 | setState(() {
35 | _activities = activities;
36 | });
37 | }
38 | }
39 |
40 | _buildActivity(Activity activity) {
41 | return FutureBuilder(
42 | future: DatabaseService.getUserWithId(activity.fromUserId),
43 | builder: (BuildContext context, AsyncSnapshot snapshot) {
44 | if (!snapshot.hasData) {
45 | return SizedBox.shrink();
46 | }
47 | User user = snapshot.data;
48 | return ListTile(
49 | leading: CircleAvatar(
50 | radius: 20.0,
51 | backgroundColor: Colors.grey,
52 | backgroundImage: user.profileImageUrl.isEmpty
53 | ? AssetImage('assets/images/user_placeholder.jpg')
54 | : CachedNetworkImageProvider(user.profileImageUrl),
55 | ),
56 | title: activity.comment != null
57 | ? Text('${user.name} commented: "${activity.comment}"')
58 | : Text('${user.name} liked your post'),
59 | subtitle: Text(
60 | DateFormat.yMd().add_jm().format(activity.timestamp.toDate()),
61 | ),
62 | trailing: CachedNetworkImage(
63 | imageUrl: activity.postImageUrl,
64 | height: 40.0,
65 | width: 40.0,
66 | fit: BoxFit.cover,
67 | ),
68 | onTap: () async {
69 | String currentUserId = Provider.of(context).currentUserId;
70 | Post post = await DatabaseService.getUserPost(
71 | currentUserId,
72 | activity.postId,
73 | );
74 | Navigator.push(
75 | context,
76 | MaterialPageRoute(
77 | builder: (_) => CommentsScreen(
78 | post: post,
79 | likeCount: post.likeCount,
80 | ),
81 | ),
82 | );
83 | },
84 | );
85 | },
86 | );
87 | }
88 |
89 | @override
90 | Widget build(BuildContext context) {
91 | return Scaffold(
92 | appBar: AppBar(
93 | backgroundColor: Colors.white,
94 | title: Text(
95 | 'Instagram',
96 | style: TextStyle(
97 | color: Colors.black,
98 | fontFamily: 'Billabong',
99 | fontSize: 35.0,
100 | ),
101 | ),
102 | ),
103 | body: RefreshIndicator(
104 | onRefresh: () => _setupActivities(),
105 | child: ListView.builder(
106 | itemCount: _activities.length,
107 | itemBuilder: (BuildContext context, int index) {
108 | Activity activity = _activities[index];
109 | return _buildActivity(activity);
110 | },
111 | ),
112 | ),
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/screens/comments_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_instagram_clone/models/comment_model.dart';
4 | import 'package:flutter_instagram_clone/models/post_model.dart';
5 | import 'package:flutter_instagram_clone/models/user_data.dart';
6 | import 'package:flutter_instagram_clone/models/user_model.dart';
7 | import 'package:flutter_instagram_clone/services/database_service.dart';
8 | import 'package:flutter_instagram_clone/utilities/constants.dart';
9 | import 'package:intl/intl.dart';
10 | import 'package:provider/provider.dart';
11 |
12 | class CommentsScreen extends StatefulWidget {
13 | final Post post;
14 | final int likeCount;
15 |
16 | CommentsScreen({this.post, this.likeCount});
17 |
18 | @override
19 | _CommentsScreenState createState() => _CommentsScreenState();
20 | }
21 |
22 | class _CommentsScreenState extends State {
23 | final TextEditingController _commentController = TextEditingController();
24 | bool _isCommenting = false;
25 |
26 | _buildComment(Comment comment) {
27 | return FutureBuilder(
28 | future: DatabaseService.getUserWithId(comment.authorId),
29 | builder: (BuildContext context, AsyncSnapshot snapshot) {
30 | if (!snapshot.hasData) {
31 | return SizedBox.shrink();
32 | }
33 | User author = snapshot.data;
34 | return ListTile(
35 | leading: CircleAvatar(
36 | radius: 25.0,
37 | backgroundColor: Colors.grey,
38 | backgroundImage: author.profileImageUrl.isEmpty
39 | ? AssetImage('assets/images/user_placeholder.jpg')
40 | : CachedNetworkImageProvider(author.profileImageUrl),
41 | ),
42 | title: Text(author.name),
43 | subtitle: Column(
44 | crossAxisAlignment: CrossAxisAlignment.start,
45 | children: [
46 | Text(comment.content),
47 | SizedBox(height: 6.0),
48 | Text(
49 | DateFormat.yMd().add_jm().format(comment.timestamp.toDate()),
50 | ),
51 | ],
52 | ),
53 | );
54 | },
55 | );
56 | }
57 |
58 | _buildCommentTF() {
59 | final currentUserId = Provider.of(context).currentUserId;
60 | return IconTheme(
61 | data: IconThemeData(
62 | color: _isCommenting
63 | ? Theme.of(context).accentColor
64 | : Theme.of(context).disabledColor,
65 | ),
66 | child: Container(
67 | margin: EdgeInsets.symmetric(horizontal: 8.0),
68 | child: Row(
69 | crossAxisAlignment: CrossAxisAlignment.center,
70 | children: [
71 | SizedBox(width: 10.0),
72 | Expanded(
73 | child: TextField(
74 | controller: _commentController,
75 | textCapitalization: TextCapitalization.sentences,
76 | onChanged: (comment) {
77 | setState(() {
78 | _isCommenting = comment.length > 0;
79 | });
80 | },
81 | decoration:
82 | InputDecoration.collapsed(hintText: 'Write a comment...'),
83 | ),
84 | ),
85 | Container(
86 | margin: EdgeInsets.symmetric(horizontal: 4.0),
87 | child: IconButton(
88 | icon: Icon(Icons.send),
89 | onPressed: () {
90 | if (_isCommenting) {
91 | DatabaseService.commentOnPost(
92 | currentUserId: currentUserId,
93 | post: widget.post,
94 | comment: _commentController.text,
95 | );
96 | _commentController.clear();
97 | setState(() {
98 | _isCommenting = false;
99 | });
100 | }
101 | },
102 | ),
103 | ),
104 | ],
105 | ),
106 | ),
107 | );
108 | }
109 |
110 | @override
111 | Widget build(BuildContext context) {
112 | return Scaffold(
113 | appBar: AppBar(
114 | backgroundColor: Colors.white,
115 | title: Text(
116 | 'Comments',
117 | style: TextStyle(color: Colors.black),
118 | ),
119 | ),
120 | body: Column(
121 | children: [
122 | Padding(
123 | padding: EdgeInsets.all(12.0),
124 | child: Text(
125 | '${widget.likeCount} likes',
126 | style: TextStyle(
127 | fontSize: 20.0,
128 | fontWeight: FontWeight.w600,
129 | ),
130 | ),
131 | ),
132 | StreamBuilder(
133 | stream: commentsRef
134 | .document(widget.post.id)
135 | .collection('postComments')
136 | .orderBy('timestamp', descending: true)
137 | .snapshots(),
138 | builder: (BuildContext context, AsyncSnapshot snapshot) {
139 | if (!snapshot.hasData) {
140 | return Center(
141 | child: CircularProgressIndicator(),
142 | );
143 | }
144 | return Expanded(
145 | child: ListView.builder(
146 | itemCount: snapshot.data.documents.length,
147 | itemBuilder: (BuildContext context, int index) {
148 | Comment comment =
149 | Comment.fromDoc(snapshot.data.documents[index]);
150 | return _buildComment(comment);
151 | },
152 | ),
153 | );
154 | },
155 | ),
156 | Divider(height: 1.0),
157 | _buildCommentTF(),
158 | ],
159 | ),
160 | );
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/lib/screens/create_post_screen.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:cloud_firestore/cloud_firestore.dart';
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_instagram_clone/models/post_model.dart';
7 | import 'package:flutter_instagram_clone/models/user_data.dart';
8 | import 'package:flutter_instagram_clone/services/database_service.dart';
9 | import 'package:flutter_instagram_clone/services/storage_service.dart';
10 | import 'package:image_cropper/image_cropper.dart';
11 | import 'package:image_picker/image_picker.dart';
12 | import 'package:provider/provider.dart';
13 |
14 | class CreatePostScreen extends StatefulWidget {
15 | @override
16 | _CreatePostScreenState createState() => _CreatePostScreenState();
17 | }
18 |
19 | class _CreatePostScreenState extends State {
20 | File _image;
21 | TextEditingController _captionController = TextEditingController();
22 | String _caption = '';
23 | bool _isLoading = false;
24 |
25 | _showSelectImageDialog() {
26 | return Platform.isIOS ? _iosBottomSheet() : _androidDialog();
27 | }
28 |
29 | _iosBottomSheet() {
30 | showCupertinoModalPopup(
31 | context: context,
32 | builder: (BuildContext context) {
33 | return CupertinoActionSheet(
34 | title: Text('Add Photo'),
35 | actions: [
36 | CupertinoActionSheetAction(
37 | child: Text('Take Photo'),
38 | onPressed: () => _handleImage(ImageSource.camera),
39 | ),
40 | CupertinoActionSheetAction(
41 | child: Text('Choose From Gallery'),
42 | onPressed: () => _handleImage(ImageSource.gallery),
43 | ),
44 | ],
45 | cancelButton: CupertinoActionSheetAction(
46 | child: Text('Cancel'),
47 | onPressed: () => Navigator.pop(context),
48 | ),
49 | );
50 | },
51 | );
52 | }
53 |
54 | _androidDialog() {
55 | showDialog(
56 | context: context,
57 | builder: (BuildContext context) {
58 | return SimpleDialog(
59 | title: Text('Add Photo'),
60 | children: [
61 | SimpleDialogOption(
62 | child: Text('Take Photo'),
63 | onPressed: () => _handleImage(ImageSource.camera),
64 | ),
65 | SimpleDialogOption(
66 | child: Text('Choose From Gallery'),
67 | onPressed: () => _handleImage(ImageSource.gallery),
68 | ),
69 | SimpleDialogOption(
70 | child: Text(
71 | 'Cancel',
72 | style: TextStyle(
73 | color: Colors.redAccent,
74 | ),
75 | ),
76 | onPressed: () => Navigator.pop(context),
77 | ),
78 | ],
79 | );
80 | },
81 | );
82 | }
83 |
84 | _handleImage(ImageSource source) async {
85 | Navigator.pop(context);
86 | File imageFile = await ImagePicker.pickImage(source: source);
87 | if (imageFile != null) {
88 | imageFile = await _cropImage(imageFile);
89 | setState(() {
90 | _image = imageFile;
91 | });
92 | }
93 | }
94 |
95 | _cropImage(File imageFile) async {
96 | File croppedImage = await ImageCropper.cropImage(
97 | sourcePath: imageFile.path,
98 | aspectRatio: CropAspectRatio(ratioX: 1.0, ratioY: 1.0),
99 | );
100 | return croppedImage;
101 | }
102 |
103 | _submit() async {
104 | if (!_isLoading && _image != null && _caption.isNotEmpty) {
105 | setState(() {
106 | _isLoading = true;
107 | });
108 |
109 | // Create post
110 | String imageUrl = await StorageService.uploadPost(_image);
111 | Post post = Post(
112 | imageUrl: imageUrl,
113 | caption: _caption,
114 | likeCount: 0,
115 | authorId: Provider.of(context).currentUserId,
116 | timestamp: Timestamp.fromDate(DateTime.now()),
117 | );
118 | DatabaseService.createPost(post);
119 |
120 | // Reset data
121 | _captionController.clear();
122 |
123 | setState(() {
124 | _caption = '';
125 | _image = null;
126 | _isLoading = false;
127 | });
128 | }
129 | }
130 |
131 | @override
132 | Widget build(BuildContext context) {
133 | final height = MediaQuery.of(context).size.height;
134 | final width = MediaQuery.of(context).size.width;
135 | return Scaffold(
136 | appBar: AppBar(
137 | backgroundColor: Colors.white,
138 | title: Text(
139 | 'Create Post',
140 | style: TextStyle(
141 | color: Colors.black,
142 | ),
143 | ),
144 | actions: [
145 | IconButton(
146 | icon: Icon(Icons.add),
147 | onPressed: _submit,
148 | ),
149 | ],
150 | ),
151 | body: GestureDetector(
152 | onTap: () => FocusScope.of(context).unfocus(),
153 | child: SingleChildScrollView(
154 | child: Container(
155 | height: height,
156 | child: Column(
157 | children: [
158 | _isLoading
159 | ? Padding(
160 | padding: EdgeInsets.only(bottom: 10.0),
161 | child: LinearProgressIndicator(
162 | backgroundColor: Colors.blue[200],
163 | valueColor: AlwaysStoppedAnimation(Colors.blue),
164 | ),
165 | )
166 | : SizedBox.shrink(),
167 | GestureDetector(
168 | onTap: _showSelectImageDialog,
169 | child: Container(
170 | height: width,
171 | width: width,
172 | color: Colors.grey[300],
173 | child: _image == null
174 | ? Icon(
175 | Icons.add_a_photo,
176 | color: Colors.white70,
177 | size: 150.0,
178 | )
179 | : Image(
180 | image: FileImage(_image),
181 | fit: BoxFit.cover,
182 | ),
183 | ),
184 | ),
185 | SizedBox(height: 20.0),
186 | Padding(
187 | padding: EdgeInsets.symmetric(horizontal: 30.0),
188 | child: TextField(
189 | controller: _captionController,
190 | style: TextStyle(fontSize: 18.0),
191 | decoration: InputDecoration(
192 | labelText: 'Caption',
193 | ),
194 | onChanged: (input) => _caption = input,
195 | ),
196 | ),
197 | ],
198 | ),
199 | ),
200 | ),
201 | ),
202 | );
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/lib/screens/edit_profile_screen.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:cached_network_image/cached_network_image.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_instagram_clone/models/user_model.dart';
6 | import 'package:flutter_instagram_clone/services/database_service.dart';
7 | import 'package:flutter_instagram_clone/services/storage_service.dart';
8 | import 'package:image_picker/image_picker.dart';
9 |
10 | class EditProfileScreen extends StatefulWidget {
11 | final User user;
12 | final Function updateUser;
13 |
14 | EditProfileScreen({this.user, this.updateUser});
15 |
16 | @override
17 | _EditProfileScreenState createState() => _EditProfileScreenState();
18 | }
19 |
20 | class _EditProfileScreenState extends State {
21 | final _formKey = GlobalKey();
22 | File _profileImage;
23 | String _name = '';
24 | String _bio = '';
25 | bool _isLoading = false;
26 |
27 | @override
28 | void initState() {
29 | super.initState();
30 | _name = widget.user.name;
31 | _bio = widget.user.bio;
32 | }
33 |
34 | _handleImageFromGallery() async {
35 | File imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
36 | if (imageFile != null) {
37 | setState(() {
38 | _profileImage = imageFile;
39 | });
40 | }
41 | }
42 |
43 | _displayProfileImage() {
44 | // No new profile image
45 | if (_profileImage == null) {
46 | // No existing profile image
47 | if (widget.user.profileImageUrl.isEmpty) {
48 | // Display placeholder
49 | return AssetImage('assets/images/user_placeholder.jpg');
50 | } else {
51 | // User profile image exists
52 | return CachedNetworkImageProvider(widget.user.profileImageUrl);
53 | }
54 | } else {
55 | // New profile image
56 | return FileImage(_profileImage);
57 | }
58 | }
59 |
60 | _submit() async {
61 | if (_formKey.currentState.validate() && !_isLoading) {
62 | _formKey.currentState.save();
63 |
64 | setState(() {
65 | _isLoading = true;
66 | });
67 |
68 | // Update user in database
69 | String _profileImageUrl = '';
70 |
71 | if (_profileImage == null) {
72 | _profileImageUrl = widget.user.profileImageUrl;
73 | } else {
74 | _profileImageUrl = await StorageService.uploadUserProfileImage(
75 | widget.user.profileImageUrl,
76 | _profileImage,
77 | );
78 | }
79 |
80 | User user = User(
81 | id: widget.user.id,
82 | name: _name,
83 | profileImageUrl: _profileImageUrl,
84 | bio: _bio,
85 | );
86 | // Database update
87 | DatabaseService.updateUser(user);
88 |
89 | widget.updateUser(user);
90 |
91 | Navigator.pop(context);
92 | }
93 | }
94 |
95 | @override
96 | Widget build(BuildContext context) {
97 | return Scaffold(
98 | backgroundColor: Colors.white,
99 | appBar: AppBar(
100 | backgroundColor: Colors.white,
101 | title: Text(
102 | 'Edit Profile',
103 | style: TextStyle(color: Colors.black),
104 | ),
105 | ),
106 | body: GestureDetector(
107 | onTap: () => FocusScope.of(context).unfocus(),
108 | child: ListView(
109 | children: [
110 | _isLoading
111 | ? LinearProgressIndicator(
112 | backgroundColor: Colors.blue[200],
113 | valueColor: AlwaysStoppedAnimation(Colors.blue),
114 | )
115 | : SizedBox.shrink(),
116 | Padding(
117 | padding: EdgeInsets.all(30.0),
118 | child: Form(
119 | key: _formKey,
120 | child: Column(
121 | children: [
122 | CircleAvatar(
123 | radius: 60.0,
124 | backgroundColor: Colors.grey,
125 | backgroundImage: _displayProfileImage(),
126 | ),
127 | FlatButton(
128 | onPressed: _handleImageFromGallery,
129 | child: Text(
130 | 'Change Profile Image',
131 | style: TextStyle(
132 | color: Theme.of(context).accentColor,
133 | fontSize: 16.0),
134 | ),
135 | ),
136 | TextFormField(
137 | initialValue: _name,
138 | style: TextStyle(fontSize: 18.0),
139 | decoration: InputDecoration(
140 | icon: Icon(
141 | Icons.person,
142 | size: 30.0,
143 | ),
144 | labelText: 'Name',
145 | ),
146 | validator: (input) => input.trim().length < 1
147 | ? 'Please enter a valid name'
148 | : null,
149 | onSaved: (input) => _name = input,
150 | ),
151 | TextFormField(
152 | initialValue: _bio,
153 | style: TextStyle(fontSize: 18.0),
154 | decoration: InputDecoration(
155 | icon: Icon(
156 | Icons.book,
157 | size: 30.0,
158 | ),
159 | labelText: 'Bio',
160 | ),
161 | validator: (input) => input.trim().length > 150
162 | ? 'Please enter a bio less than 150 characters'
163 | : null,
164 | onSaved: (input) => _bio = input,
165 | ),
166 | Container(
167 | margin: EdgeInsets.all(40.0),
168 | height: 40.0,
169 | width: 250.0,
170 | child: FlatButton(
171 | onPressed: _submit,
172 | color: Colors.blue,
173 | textColor: Colors.white,
174 | child: Text(
175 | 'Save Profile',
176 | style: TextStyle(fontSize: 18.0),
177 | ),
178 | ),
179 | ),
180 | ],
181 | ),
182 | ),
183 | ),
184 | ],
185 | ),
186 | ),
187 | );
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/lib/screens/feed_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_instagram_clone/models/post_model.dart';
3 | import 'package:flutter_instagram_clone/models/user_model.dart';
4 | import 'package:flutter_instagram_clone/services/database_service.dart';
5 | import 'package:flutter_instagram_clone/widgets/post_view.dart';
6 |
7 | class FeedScreen extends StatefulWidget {
8 | static final String id = 'feed_screen';
9 | final String currentUserId;
10 |
11 | FeedScreen({this.currentUserId});
12 |
13 | @override
14 | _FeedScreenState createState() => _FeedScreenState();
15 | }
16 |
17 | class _FeedScreenState extends State {
18 | /// SETUP FEED is unnecessary because we switched it from a FutureBuilder to a StreamBuilder
19 |
20 | // List _posts = [];
21 |
22 | // @override
23 | // void initState() {
24 | // super.initState();
25 | // _setupFeed();
26 | // }
27 |
28 | // _setupFeed() async {
29 | // List posts = await DatabaseService.getFeedPosts(widget.currentUserId);
30 | // setState(() {
31 | // _posts = posts;
32 | // });
33 | // }
34 |
35 | @override
36 | Widget build(BuildContext context) {
37 | return Scaffold(
38 | appBar: AppBar(
39 | backgroundColor: Colors.white,
40 | title: Text(
41 | 'Instagram',
42 | style: TextStyle(
43 | color: Colors.black,
44 | fontFamily: 'Billabong',
45 | fontSize: 35.0,
46 | ),
47 | ),
48 | ),
49 | body: StreamBuilder(
50 | stream: DatabaseService.getFeedPosts(widget.currentUserId),
51 | builder: (BuildContext context, AsyncSnapshot snapshot) {
52 | if (!snapshot.hasData) {
53 | return SizedBox.shrink();
54 | }
55 | final List posts = snapshot.data;
56 | return ListView.builder(
57 | itemCount: posts.length,
58 | itemBuilder: (BuildContext context, int index) {
59 | Post post = posts[index];
60 | return FutureBuilder(
61 | future: DatabaseService.getUserWithId(post.authorId),
62 | builder: (BuildContext context, AsyncSnapshot snapshot) {
63 | if (!snapshot.hasData) {
64 | return SizedBox.shrink();
65 | }
66 | User author = snapshot.data;
67 | return PostView(
68 | currentUserId: widget.currentUserId,
69 | post: post,
70 | author: author,
71 | );
72 | },
73 | );
74 | },
75 | );
76 | },
77 | ),
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/lib/screens/home_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_instagram_clone/models/user_data.dart';
4 | import 'package:flutter_instagram_clone/screens/activity_screen.dart';
5 | import 'package:flutter_instagram_clone/screens/create_post_screen.dart';
6 | import 'package:flutter_instagram_clone/screens/feed_screen.dart';
7 | import 'package:flutter_instagram_clone/screens/profile_screen.dart';
8 | import 'package:flutter_instagram_clone/screens/search_screen.dart';
9 | import 'package:provider/provider.dart';
10 |
11 | class HomeScreen extends StatefulWidget {
12 | @override
13 | _HomeScreenState createState() => _HomeScreenState();
14 | }
15 |
16 | class _HomeScreenState extends State {
17 | int _currentTab = 0;
18 | PageController _pageController;
19 |
20 | @override
21 | void initState() {
22 | super.initState();
23 | _pageController = PageController();
24 | }
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | final String currentUserId = Provider.of(context).currentUserId;
29 | return Scaffold(
30 | body: PageView(
31 | controller: _pageController,
32 | children: [
33 | FeedScreen(currentUserId: currentUserId),
34 | SearchScreen(),
35 | CreatePostScreen(),
36 | ActivityScreen(currentUserId: currentUserId),
37 | ProfileScreen(
38 | currentUserId: currentUserId,
39 | userId: currentUserId,
40 | ),
41 | ],
42 | onPageChanged: (int index) {
43 | setState(() {
44 | _currentTab = index;
45 | });
46 | },
47 | ),
48 | bottomNavigationBar: CupertinoTabBar(
49 | currentIndex: _currentTab,
50 | onTap: (int index) {
51 | setState(() {
52 | _currentTab = index;
53 | });
54 | _pageController.animateToPage(
55 | index,
56 | duration: Duration(milliseconds: 200),
57 | curve: Curves.easeIn,
58 | );
59 | },
60 | activeColor: Colors.black,
61 | items: [
62 | BottomNavigationBarItem(
63 | icon: Icon(
64 | Icons.home,
65 | size: 32.0,
66 | ),
67 | ),
68 | BottomNavigationBarItem(
69 | icon: Icon(
70 | Icons.search,
71 | size: 32.0,
72 | ),
73 | ),
74 | BottomNavigationBarItem(
75 | icon: Icon(
76 | Icons.photo_camera,
77 | size: 32.0,
78 | ),
79 | ),
80 | BottomNavigationBarItem(
81 | icon: Icon(
82 | Icons.notifications,
83 | size: 32.0,
84 | ),
85 | ),
86 | BottomNavigationBarItem(
87 | icon: Icon(
88 | Icons.account_circle,
89 | size: 32.0,
90 | ),
91 | ),
92 | ],
93 | ),
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/lib/screens/login_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_instagram_clone/screens/signup_screen.dart';
3 | import 'package:flutter_instagram_clone/services/auth_service.dart';
4 |
5 | class LoginScreen extends StatefulWidget {
6 | static final String id = 'login_screen';
7 |
8 | @override
9 | _LoginScreenState createState() => _LoginScreenState();
10 | }
11 |
12 | class _LoginScreenState extends State {
13 | final _formKey = GlobalKey();
14 | String _email, _password;
15 |
16 | _submit() {
17 | if (_formKey.currentState.validate()) {
18 | _formKey.currentState.save();
19 | // Logging in the user w/ Firebase
20 | AuthService.login(_email, _password);
21 | }
22 | }
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return Scaffold(
27 | body: SingleChildScrollView(
28 | child: Container(
29 | height: MediaQuery.of(context).size.height,
30 | child: Column(
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | crossAxisAlignment: CrossAxisAlignment.center,
33 | children: [
34 | Text(
35 | 'Instagram',
36 | style: TextStyle(
37 | fontFamily: 'Billabong',
38 | fontSize: 50.0,
39 | ),
40 | ),
41 | Form(
42 | key: _formKey,
43 | child: Column(
44 | mainAxisSize: MainAxisSize.min,
45 | children: [
46 | Padding(
47 | padding: EdgeInsets.symmetric(
48 | horizontal: 30.0,
49 | vertical: 10.0,
50 | ),
51 | child: TextFormField(
52 | decoration: InputDecoration(labelText: 'Email'),
53 | validator: (input) => !input.contains('@')
54 | ? 'Please enter a valid email'
55 | : null,
56 | onSaved: (input) => _email = input,
57 | ),
58 | ),
59 | Padding(
60 | padding: EdgeInsets.symmetric(
61 | horizontal: 30.0,
62 | vertical: 10.0,
63 | ),
64 | child: TextFormField(
65 | decoration: InputDecoration(labelText: 'Password'),
66 | validator: (input) => input.length < 6
67 | ? 'Must be at least 6 characters'
68 | : null,
69 | onSaved: (input) => _password = input,
70 | obscureText: true,
71 | ),
72 | ),
73 | SizedBox(height: 20.0),
74 | Container(
75 | width: 250.0,
76 | child: FlatButton(
77 | onPressed: _submit,
78 | color: Colors.blue,
79 | padding: EdgeInsets.all(10.0),
80 | child: Text(
81 | 'Login',
82 | style: TextStyle(
83 | color: Colors.white,
84 | fontSize: 18.0,
85 | ),
86 | ),
87 | ),
88 | ),
89 | SizedBox(height: 20.0),
90 | Container(
91 | width: 250.0,
92 | child: FlatButton(
93 | onPressed: () =>
94 | Navigator.pushNamed(context, SignupScreen.id),
95 | color: Colors.blue,
96 | padding: EdgeInsets.all(10.0),
97 | child: Text(
98 | 'Go to Signup',
99 | style: TextStyle(
100 | color: Colors.white,
101 | fontSize: 18.0,
102 | ),
103 | ),
104 | ),
105 | ),
106 | ],
107 | ),
108 | ),
109 | ],
110 | ),
111 | ),
112 | ),
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/screens/profile_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_instagram_clone/models/post_model.dart';
4 | import 'package:flutter_instagram_clone/models/user_data.dart';
5 | import 'package:flutter_instagram_clone/models/user_model.dart';
6 | import 'package:flutter_instagram_clone/screens/edit_profile_screen.dart';
7 | import 'package:flutter_instagram_clone/services/auth_service.dart';
8 | import 'package:flutter_instagram_clone/services/database_service.dart';
9 | import 'package:flutter_instagram_clone/utilities/constants.dart';
10 | import 'package:flutter_instagram_clone/widgets/post_view.dart';
11 | import 'package:provider/provider.dart';
12 |
13 | import 'comments_screen.dart';
14 |
15 | class ProfileScreen extends StatefulWidget {
16 | final String currentUserId;
17 | final String userId;
18 |
19 | ProfileScreen({this.currentUserId, this.userId});
20 |
21 | @override
22 | _ProfileScreenState createState() => _ProfileScreenState();
23 | }
24 |
25 | class _ProfileScreenState extends State {
26 | bool _isFollowing = false;
27 | int _followerCount = 0;
28 | int _followingCount = 0;
29 | List _posts = [];
30 | int _displayPosts = 0; // 0 - grid, 1 - column
31 | User _profileUser;
32 |
33 | @override
34 | void initState() {
35 | super.initState();
36 | _setupIsFollowing();
37 | _setupFollowers();
38 | _setupFollowing();
39 | _setupPosts();
40 | _setupProfileUser();
41 | }
42 |
43 | _setupIsFollowing() async {
44 | bool isFollowingUser = await DatabaseService.isFollowingUser(
45 | currentUserId: widget.currentUserId,
46 | userId: widget.userId,
47 | );
48 | setState(() {
49 | _isFollowing = isFollowingUser;
50 | });
51 | }
52 |
53 | _setupFollowers() async {
54 | int userFollowerCount = await DatabaseService.numFollowers(widget.userId);
55 | setState(() {
56 | _followerCount = userFollowerCount;
57 | });
58 | }
59 |
60 | _setupFollowing() async {
61 | int userFollowingCount = await DatabaseService.numFollowing(widget.userId);
62 | setState(() {
63 | _followingCount = userFollowingCount;
64 | });
65 | }
66 |
67 | _setupPosts() async {
68 | List posts = await DatabaseService.getUserPosts(widget.userId);
69 | setState(() {
70 | _posts = posts;
71 | });
72 | }
73 |
74 | _setupProfileUser() async {
75 | User profileUser = await DatabaseService.getUserWithId(widget.userId);
76 | setState(() {
77 | _profileUser = profileUser;
78 | });
79 | }
80 |
81 | _followOrUnfollow() {
82 | if (_isFollowing) {
83 | _unfollowUser();
84 | } else {
85 | _followUser();
86 | }
87 | }
88 |
89 | _unfollowUser() {
90 | DatabaseService.unfollowUser(
91 | currentUserId: widget.currentUserId,
92 | userId: widget.userId,
93 | );
94 | setState(() {
95 | _isFollowing = false;
96 | _followerCount--;
97 | });
98 | }
99 |
100 | _followUser() {
101 | DatabaseService.followUser(
102 | currentUserId: widget.currentUserId,
103 | userId: widget.userId,
104 | );
105 | setState(() {
106 | _isFollowing = true;
107 | _followerCount++;
108 | });
109 | }
110 |
111 | _displayButton(User user) {
112 | return user.id == Provider.of(context).currentUserId
113 | ? Container(
114 | width: 200.0,
115 | child: FlatButton(
116 | onPressed: () => Navigator.push(
117 | context,
118 | MaterialPageRoute(
119 | builder: (_) => EditProfileScreen(
120 | user: user,
121 | updateUser: (User updateUser) {
122 | // Trigger state rebuild after editing profile
123 | User updatedUser = User(
124 | id: updateUser.id,
125 | name: updateUser.name,
126 | email: user.email,
127 | profileImageUrl: updateUser.profileImageUrl,
128 | bio: updateUser.bio,
129 | );
130 | setState(() => _profileUser = updatedUser);
131 | },
132 | ),
133 | ),
134 | ),
135 | color: Colors.blue,
136 | textColor: Colors.white,
137 | child: Text(
138 | 'Edit Profile',
139 | style: TextStyle(fontSize: 18.0),
140 | ),
141 | ),
142 | )
143 | : Container(
144 | width: 200.0,
145 | child: FlatButton(
146 | onPressed: _followOrUnfollow,
147 | color: _isFollowing ? Colors.grey[200] : Colors.blue,
148 | textColor: _isFollowing ? Colors.black : Colors.white,
149 | child: Text(
150 | _isFollowing ? 'Unfollow' : 'Follow',
151 | style: TextStyle(fontSize: 18.0),
152 | ),
153 | ),
154 | );
155 | }
156 |
157 | _buildProfileInfo(User user) {
158 | return Column(
159 | children: [
160 | Padding(
161 | padding: EdgeInsets.fromLTRB(30.0, 30.0, 30.0, 0.0),
162 | child: Row(
163 | children: [
164 | CircleAvatar(
165 | radius: 50.0,
166 | backgroundColor: Colors.grey,
167 | backgroundImage: user.profileImageUrl.isEmpty
168 | ? AssetImage('assets/images/user_placeholder.jpg')
169 | : CachedNetworkImageProvider(user.profileImageUrl),
170 | ),
171 | Expanded(
172 | child: Column(
173 | children: [
174 | Row(
175 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
176 | children: [
177 | Column(
178 | children: [
179 | Text(
180 | _posts.length.toString(),
181 | style: TextStyle(
182 | fontSize: 18.0,
183 | fontWeight: FontWeight.w600,
184 | ),
185 | ),
186 | Text(
187 | 'posts',
188 | style: TextStyle(color: Colors.black54),
189 | ),
190 | ],
191 | ),
192 | Column(
193 | children: [
194 | Text(
195 | _followerCount.toString(),
196 | style: TextStyle(
197 | fontSize: 18.0,
198 | fontWeight: FontWeight.w600,
199 | ),
200 | ),
201 | Text(
202 | 'followers',
203 | style: TextStyle(color: Colors.black54),
204 | ),
205 | ],
206 | ),
207 | Column(
208 | children: [
209 | Text(
210 | _followingCount.toString(),
211 | style: TextStyle(
212 | fontSize: 18.0,
213 | fontWeight: FontWeight.w600,
214 | ),
215 | ),
216 | Text(
217 | 'following',
218 | style: TextStyle(color: Colors.black54),
219 | ),
220 | ],
221 | ),
222 | ],
223 | ),
224 | _displayButton(user),
225 | ],
226 | ),
227 | )
228 | ],
229 | ),
230 | ),
231 | Padding(
232 | padding: EdgeInsets.symmetric(horizontal: 30.0, vertical: 10.0),
233 | child: Column(
234 | crossAxisAlignment: CrossAxisAlignment.start,
235 | children: [
236 | Text(
237 | user.name,
238 | style: TextStyle(
239 | fontSize: 18.0,
240 | fontWeight: FontWeight.bold,
241 | ),
242 | ),
243 | SizedBox(height: 5.0),
244 | Container(
245 | height: 80.0,
246 | child: Text(
247 | user.bio,
248 | style: TextStyle(fontSize: 15.0),
249 | ),
250 | ),
251 | Divider(),
252 | ],
253 | ),
254 | ),
255 | ],
256 | );
257 | }
258 |
259 | _buildToggleButtons() {
260 | return Row(
261 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
262 | children: [
263 | IconButton(
264 | icon: Icon(Icons.grid_on),
265 | iconSize: 30.0,
266 | color: _displayPosts == 0
267 | ? Theme.of(context).primaryColor
268 | : Colors.grey[300],
269 | onPressed: () => setState(() {
270 | _displayPosts = 0;
271 | }),
272 | ),
273 | IconButton(
274 | icon: Icon(Icons.list),
275 | iconSize: 30.0,
276 | color: _displayPosts == 1
277 | ? Theme.of(context).primaryColor
278 | : Colors.grey[300],
279 | onPressed: () => setState(() {
280 | _displayPosts = 1;
281 | }),
282 | ),
283 | ],
284 | );
285 | }
286 |
287 | _buildTilePost(Post post) {
288 | return GridTile(
289 | child: GestureDetector(
290 | onTap: () => Navigator.push(
291 | context,
292 | MaterialPageRoute(
293 | builder: (_) => CommentsScreen(
294 | post: post,
295 | likeCount: post.likeCount,
296 | ),
297 | ),
298 | ),
299 | child: Image(
300 | image: CachedNetworkImageProvider(post.imageUrl),
301 | fit: BoxFit.cover,
302 | ),
303 | ),
304 | );
305 | }
306 |
307 | _buildDisplayPosts() {
308 | if (_displayPosts == 0) {
309 | // Grid
310 | List tiles = [];
311 | _posts.forEach(
312 | (post) => tiles.add(_buildTilePost(post)),
313 | );
314 | return GridView.count(
315 | crossAxisCount: 3,
316 | childAspectRatio: 1.0,
317 | mainAxisSpacing: 2.0,
318 | crossAxisSpacing: 2.0,
319 | shrinkWrap: true,
320 | physics: NeverScrollableScrollPhysics(),
321 | children: tiles,
322 | );
323 | } else {
324 | // Column
325 | List postViews = [];
326 | _posts.forEach((post) {
327 | postViews.add(
328 | PostView(
329 | currentUserId: widget.currentUserId,
330 | post: post,
331 | author: _profileUser,
332 | ),
333 | );
334 | });
335 | return Column(children: postViews);
336 | }
337 | }
338 |
339 | @override
340 | Widget build(BuildContext context) {
341 | return Scaffold(
342 | backgroundColor: Colors.white,
343 | appBar: AppBar(
344 | backgroundColor: Colors.white,
345 | title: Text(
346 | 'Instagram',
347 | style: TextStyle(
348 | color: Colors.black,
349 | fontFamily: 'Billabong',
350 | fontSize: 35.0,
351 | ),
352 | ),
353 | actions: [
354 | IconButton(
355 | icon: Icon(Icons.exit_to_app),
356 | onPressed: AuthService.logout,
357 | ),
358 | ],
359 | ),
360 | body: FutureBuilder(
361 | future: usersRef.document(widget.userId).get(),
362 | builder: (BuildContext context, AsyncSnapshot snapshot) {
363 | if (!snapshot.hasData) {
364 | return Center(
365 | child: CircularProgressIndicator(),
366 | );
367 | }
368 | User user = User.fromDoc(snapshot.data);
369 | return ListView(
370 | children: [
371 | _buildProfileInfo(user),
372 | _buildToggleButtons(),
373 | Divider(),
374 | _buildDisplayPosts(),
375 | ],
376 | );
377 | },
378 | ),
379 | );
380 | }
381 | }
382 |
--------------------------------------------------------------------------------
/lib/screens/search_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:cloud_firestore/cloud_firestore.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_instagram_clone/models/user_data.dart';
5 | import 'package:flutter_instagram_clone/models/user_model.dart';
6 | import 'package:flutter_instagram_clone/screens/profile_screen.dart';
7 | import 'package:flutter_instagram_clone/services/database_service.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | class SearchScreen extends StatefulWidget {
11 | @override
12 | _SearchScreenState createState() => _SearchScreenState();
13 | }
14 |
15 | class _SearchScreenState extends State {
16 | TextEditingController _searchController = TextEditingController();
17 | Future _users;
18 |
19 | _buildUserTile(User user) {
20 | return ListTile(
21 | leading: CircleAvatar(
22 | radius: 20.0,
23 | backgroundImage: user.profileImageUrl.isEmpty
24 | ? AssetImage('assets/images/user_placeholder.jpg')
25 | : CachedNetworkImageProvider(user.profileImageUrl),
26 | ),
27 | title: Text(user.name),
28 | onTap: () => Navigator.push(
29 | context,
30 | MaterialPageRoute(
31 | builder: (_) => ProfileScreen(
32 | currentUserId: Provider.of(context).currentUserId,
33 | userId: user.id,
34 | ),
35 | ),
36 | ),
37 | );
38 | }
39 |
40 | _clearSearch() {
41 | WidgetsBinding.instance
42 | .addPostFrameCallback((_) => _searchController.clear());
43 | setState(() {
44 | _users = null;
45 | });
46 | }
47 |
48 | @override
49 | Widget build(BuildContext context) {
50 | return Scaffold(
51 | appBar: AppBar(
52 | backgroundColor: Colors.white,
53 | title: TextField(
54 | controller: _searchController,
55 | decoration: InputDecoration(
56 | contentPadding: EdgeInsets.symmetric(vertical: 15.0),
57 | border: InputBorder.none,
58 | hintText: 'Search',
59 | prefixIcon: Icon(
60 | Icons.search,
61 | size: 30.0,
62 | ),
63 | suffixIcon: IconButton(
64 | icon: Icon(
65 | Icons.clear,
66 | ),
67 | onPressed: _clearSearch,
68 | ),
69 | filled: true,
70 | ),
71 | onSubmitted: (input) {
72 | if (input.isNotEmpty) {
73 | setState(() {
74 | _users = DatabaseService.searchUsers(input);
75 | });
76 | }
77 | },
78 | ),
79 | ),
80 | body: _users == null
81 | ? Center(
82 | child: Text('Search for a user'),
83 | )
84 | : FutureBuilder(
85 | future: _users,
86 | builder: (context, snapshot) {
87 | if (!snapshot.hasData) {
88 | return Center(
89 | child: CircularProgressIndicator(),
90 | );
91 | }
92 | if (snapshot.data.documents.length == 0) {
93 | return Center(
94 | child: Text('No users found! Please try again.'),
95 | );
96 | }
97 | return ListView.builder(
98 | itemCount: snapshot.data.documents.length,
99 | itemBuilder: (BuildContext context, int index) {
100 | User user = User.fromDoc(snapshot.data.documents[index]);
101 | return _buildUserTile(user);
102 | },
103 | );
104 | },
105 | ),
106 | );
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/screens/signup_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_instagram_clone/services/auth_service.dart';
3 |
4 | class SignupScreen extends StatefulWidget {
5 | static final String id = 'signup_screen';
6 |
7 | @override
8 | _SignupScreenState createState() => _SignupScreenState();
9 | }
10 |
11 | class _SignupScreenState extends State {
12 | final _formKey = GlobalKey();
13 | String _name, _email, _password;
14 |
15 | _submit() {
16 | if (_formKey.currentState.validate()) {
17 | _formKey.currentState.save();
18 | // Logging in the user w/ Firebase
19 | AuthService.signUpUser(context, _name, _email, _password);
20 | }
21 | }
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return Scaffold(
26 | body: SingleChildScrollView(
27 | child: Container(
28 | height: MediaQuery.of(context).size.height,
29 | child: Column(
30 | mainAxisAlignment: MainAxisAlignment.center,
31 | crossAxisAlignment: CrossAxisAlignment.center,
32 | children: [
33 | Text(
34 | 'Instagram',
35 | style: TextStyle(
36 | fontFamily: 'Billabong',
37 | fontSize: 50.0,
38 | ),
39 | ),
40 | Form(
41 | key: _formKey,
42 | child: Column(
43 | mainAxisSize: MainAxisSize.min,
44 | children: [
45 | Padding(
46 | padding: EdgeInsets.symmetric(
47 | horizontal: 30.0,
48 | vertical: 10.0,
49 | ),
50 | child: TextFormField(
51 | decoration: InputDecoration(labelText: 'Name'),
52 | validator: (input) => input.trim().isEmpty
53 | ? 'Please enter a valid name'
54 | : null,
55 | onSaved: (input) => _name = input,
56 | ),
57 | ),
58 | Padding(
59 | padding: EdgeInsets.symmetric(
60 | horizontal: 30.0,
61 | vertical: 10.0,
62 | ),
63 | child: TextFormField(
64 | decoration: InputDecoration(labelText: 'Email'),
65 | validator: (input) => !input.contains('@')
66 | ? 'Please enter a valid email'
67 | : null,
68 | onSaved: (input) => _email = input,
69 | ),
70 | ),
71 | Padding(
72 | padding: EdgeInsets.symmetric(
73 | horizontal: 30.0,
74 | vertical: 10.0,
75 | ),
76 | child: TextFormField(
77 | decoration: InputDecoration(labelText: 'Password'),
78 | validator: (input) => input.length < 6
79 | ? 'Must be at least 6 characters'
80 | : null,
81 | onSaved: (input) => _password = input,
82 | obscureText: true,
83 | ),
84 | ),
85 | SizedBox(height: 20.0),
86 | Container(
87 | width: 250.0,
88 | child: FlatButton(
89 | onPressed: _submit,
90 | color: Colors.blue,
91 | padding: EdgeInsets.all(10.0),
92 | child: Text(
93 | 'Sign Up',
94 | style: TextStyle(
95 | color: Colors.white,
96 | fontSize: 18.0,
97 | ),
98 | ),
99 | ),
100 | ),
101 | SizedBox(height: 20.0),
102 | Container(
103 | width: 250.0,
104 | child: FlatButton(
105 | onPressed: () => Navigator.pop(context),
106 | color: Colors.blue,
107 | padding: EdgeInsets.all(10.0),
108 | child: Text(
109 | 'Back to Login',
110 | style: TextStyle(
111 | color: Colors.white,
112 | fontSize: 18.0,
113 | ),
114 | ),
115 | ),
116 | ),
117 | ],
118 | ),
119 | ),
120 | ],
121 | ),
122 | ),
123 | ),
124 | );
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/lib/services/auth_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:firebase_auth/firebase_auth.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_instagram_clone/models/user_data.dart';
5 | import 'package:provider/provider.dart';
6 |
7 | class AuthService {
8 | static final _auth = FirebaseAuth.instance;
9 | static final _firestore = Firestore.instance;
10 |
11 | static void signUpUser(
12 | BuildContext context, String name, String email, String password) async {
13 | try {
14 | AuthResult authResult = await _auth.createUserWithEmailAndPassword(
15 | email: email,
16 | password: password,
17 | );
18 | FirebaseUser signedInUser = authResult.user;
19 | if (signedInUser != null) {
20 | _firestore.collection('/users').document(signedInUser.uid).setData({
21 | 'name': name,
22 | 'email': email,
23 | 'profileImageUrl': '',
24 | });
25 | Provider.of(context).currentUserId = signedInUser.uid;
26 | Navigator.pop(context);
27 | }
28 | } catch (e) {
29 | print(e);
30 | }
31 | }
32 |
33 | static void logout() {
34 | _auth.signOut();
35 | }
36 |
37 | static void login(String email, String password) async {
38 | try {
39 | await _auth.signInWithEmailAndPassword(email: email, password: password);
40 | } catch (e) {
41 | print(e);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/services/database_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter_instagram_clone/models/activity_model.dart';
3 | import 'package:flutter_instagram_clone/models/post_model.dart';
4 | import 'package:flutter_instagram_clone/models/user_model.dart';
5 | import 'package:flutter_instagram_clone/utilities/constants.dart';
6 |
7 | class DatabaseService {
8 | static void updateUser(User user) {
9 | usersRef.document(user.id).updateData({
10 | 'name': user.name,
11 | 'profileImageUrl': user.profileImageUrl,
12 | 'bio': user.bio,
13 | });
14 | }
15 |
16 | static Future searchUsers(String name) {
17 | Future users =
18 | usersRef.where('name', isGreaterThanOrEqualTo: name).getDocuments();
19 | return users;
20 | }
21 |
22 | static void createPost(Post post) {
23 | postsRef.document(post.authorId).collection('userPosts').add({
24 | 'imageUrl': post.imageUrl,
25 | 'caption': post.caption,
26 | 'likeCount': post.likeCount,
27 | 'authorId': post.authorId,
28 | 'timestamp': post.timestamp,
29 | });
30 | }
31 |
32 | static void followUser({String currentUserId, String userId}) {
33 | // Add user to current user's following collection
34 | followingRef
35 | .document(currentUserId)
36 | .collection('userFollowing')
37 | .document(userId)
38 | .setData({});
39 | // Add current user to user's followers collection
40 | followersRef
41 | .document(userId)
42 | .collection('userFollowers')
43 | .document(currentUserId)
44 | .setData({});
45 | }
46 |
47 | static void unfollowUser({String currentUserId, String userId}) {
48 | // Remove user from current user's following collection
49 | followingRef
50 | .document(currentUserId)
51 | .collection('userFollowing')
52 | .document(userId)
53 | .get()
54 | .then((doc) {
55 | if (doc.exists) {
56 | doc.reference.delete();
57 | }
58 | });
59 | // Remove current user from user's followers collection
60 | followersRef
61 | .document(userId)
62 | .collection('userFollowers')
63 | .document(currentUserId)
64 | .get()
65 | .then((doc) {
66 | if (doc.exists) {
67 | doc.reference.delete();
68 | }
69 | });
70 | }
71 |
72 | static Future isFollowingUser(
73 | {String currentUserId, String userId}) async {
74 | DocumentSnapshot followingDoc = await followersRef
75 | .document(userId)
76 | .collection('userFollowers')
77 | .document(currentUserId)
78 | .get();
79 | return followingDoc.exists;
80 | }
81 |
82 | static Future numFollowing(String userId) async {
83 | QuerySnapshot followingSnapshot = await followingRef
84 | .document(userId)
85 | .collection('userFollowing')
86 | .getDocuments();
87 | return followingSnapshot.documents.length;
88 | }
89 |
90 | static Future numFollowers(String userId) async {
91 | QuerySnapshot followersSnapshot = await followersRef
92 | .document(userId)
93 | .collection('userFollowers')
94 | .getDocuments();
95 | return followersSnapshot.documents.length;
96 | }
97 |
98 | static Stream> getFeedPosts(String userId) {
99 | return feedsRef
100 | .document(userId)
101 | .collection('userFeed')
102 | .orderBy('timestamp', descending: true)
103 | .snapshots()
104 | .map((snapshot) =>
105 | snapshot.documents.map((doc) => Post.fromDoc(doc)).toList());
106 | }
107 |
108 | static Future> getUserPosts(String userId) async {
109 | QuerySnapshot userPostsSnapshot = await postsRef
110 | .document(userId)
111 | .collection('userPosts')
112 | .orderBy('timestamp', descending: true)
113 | .getDocuments();
114 | List posts =
115 | userPostsSnapshot.documents.map((doc) => Post.fromDoc(doc)).toList();
116 | return posts;
117 | }
118 |
119 | static Future getUserWithId(String userId) async {
120 | DocumentSnapshot userDocSnapshot = await usersRef.document(userId).get();
121 | if (userDocSnapshot.exists) {
122 | return User.fromDoc(userDocSnapshot);
123 | }
124 | return User();
125 | }
126 |
127 | static void likePost({String currentUserId, Post post}) {
128 | DocumentReference postRef = postsRef
129 | .document(post.authorId)
130 | .collection('userPosts')
131 | .document(post.id);
132 | postRef.get().then((doc) {
133 | int likeCount = doc.data['likeCount'];
134 | postRef.updateData({'likeCount': likeCount + 1});
135 | likesRef
136 | .document(post.id)
137 | .collection('postLikes')
138 | .document(currentUserId)
139 | .setData({});
140 | addActivityItem(currentUserId: currentUserId, post: post, comment: null);
141 | });
142 | }
143 |
144 | static void unlikePost({String currentUserId, Post post}) {
145 | DocumentReference postRef = postsRef
146 | .document(post.authorId)
147 | .collection('userPosts')
148 | .document(post.id);
149 | postRef.get().then((doc) {
150 | int likeCount = doc.data['likeCount'];
151 | postRef.updateData({'likeCount': likeCount - 1});
152 | likesRef
153 | .document(post.id)
154 | .collection('postLikes')
155 | .document(currentUserId)
156 | .get()
157 | .then((doc) {
158 | if (doc.exists) {
159 | doc.reference.delete();
160 | }
161 | });
162 | });
163 | }
164 |
165 | static Future didLikePost({String currentUserId, Post post}) async {
166 | DocumentSnapshot userDoc = await likesRef
167 | .document(post.id)
168 | .collection('postLikes')
169 | .document(currentUserId)
170 | .get();
171 | return userDoc.exists;
172 | }
173 |
174 | static void commentOnPost({String currentUserId, Post post, String comment}) {
175 | commentsRef.document(post.id).collection('postComments').add({
176 | 'content': comment,
177 | 'authorId': currentUserId,
178 | 'timestamp': Timestamp.fromDate(DateTime.now()),
179 | });
180 | addActivityItem(currentUserId: currentUserId, post: post, comment: comment);
181 | }
182 |
183 | static void addActivityItem(
184 | {String currentUserId, Post post, String comment}) {
185 | if (currentUserId != post.authorId) {
186 | activitiesRef.document(post.authorId).collection('userActivities').add({
187 | 'fromUserId': currentUserId,
188 | 'postId': post.id,
189 | 'postImageUrl': post.imageUrl,
190 | 'comment': comment,
191 | 'timestamp': Timestamp.fromDate(DateTime.now()),
192 | });
193 | }
194 | }
195 |
196 | static Future> getActivities(String userId) async {
197 | QuerySnapshot userActivitiesSnapshot = await activitiesRef
198 | .document(userId)
199 | .collection('userActivities')
200 | .orderBy('timestamp', descending: true)
201 | .getDocuments();
202 | List activity = userActivitiesSnapshot.documents
203 | .map((doc) => Activity.fromDoc(doc))
204 | .toList();
205 | return activity;
206 | }
207 |
208 | static Future getUserPost(String userId, String postId) async {
209 | DocumentSnapshot postDocSnapshot = await postsRef
210 | .document(userId)
211 | .collection('userPosts')
212 | .document(postId)
213 | .get();
214 | return Post.fromDoc(postDocSnapshot);
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/lib/services/storage_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:firebase_storage/firebase_storage.dart';
4 | import 'package:flutter_image_compress/flutter_image_compress.dart';
5 | import 'package:flutter_instagram_clone/utilities/constants.dart';
6 | import 'package:path_provider/path_provider.dart';
7 | import 'package:uuid/uuid.dart';
8 |
9 | class StorageService {
10 | static Future uploadUserProfileImage(
11 | String url, File imageFile) async {
12 | String photoId = Uuid().v4();
13 | File image = await compressImage(photoId, imageFile);
14 |
15 | if (url.isNotEmpty) {
16 | // Updating user profile image
17 | RegExp exp = RegExp(r'userProfile_(.*).jpg');
18 | photoId = exp.firstMatch(url)[1];
19 | }
20 |
21 | StorageUploadTask uploadTask = storageRef
22 | .child('images/users/userProfile_$photoId.jpg')
23 | .putFile(image);
24 | StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
25 | String downloadUrl = await storageSnap.ref.getDownloadURL();
26 | return downloadUrl;
27 | }
28 |
29 | static Future compressImage(String photoId, File image) async {
30 | final tempDir = await getTemporaryDirectory();
31 | final path = tempDir.path;
32 | File compressedImageFile = await FlutterImageCompress.compressAndGetFile(
33 | image.absolute.path,
34 | '$path/img_$photoId.jpg',
35 | quality: 70,
36 | );
37 | return compressedImageFile;
38 | }
39 |
40 | static Future uploadPost(File imageFile) async {
41 | String photoId = Uuid().v4();
42 | File image = await compressImage(photoId, imageFile);
43 | StorageUploadTask uploadTask = storageRef
44 | .child('images/posts/post_$photoId.jpg')
45 | .putFile(image);
46 | StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
47 | String downloadUrl = await storageSnap.ref.getDownloadURL();
48 | return downloadUrl;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/lib/utilities/constants.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:firebase_storage/firebase_storage.dart';
3 |
4 | final _firestore = Firestore.instance;
5 | final storageRef = FirebaseStorage.instance.ref();
6 | final usersRef = _firestore.collection('users');
7 | final postsRef = _firestore.collection('posts');
8 | final followersRef = _firestore.collection('followers');
9 | final followingRef = _firestore.collection('following');
10 | final feedsRef = _firestore.collection('feeds');
11 | final likesRef = _firestore.collection('likes');
12 | final commentsRef = _firestore.collection('comments');
13 | final activitiesRef = _firestore.collection('activities');
--------------------------------------------------------------------------------
/lib/widgets/post_view.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:animator/animator.dart';
4 | import 'package:cached_network_image/cached_network_image.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_instagram_clone/models/post_model.dart';
7 | import 'package:flutter_instagram_clone/models/user_model.dart';
8 | import 'package:flutter_instagram_clone/screens/comments_screen.dart';
9 | import 'package:flutter_instagram_clone/screens/profile_screen.dart';
10 | import 'package:flutter_instagram_clone/services/database_service.dart';
11 |
12 | class PostView extends StatefulWidget {
13 | final String currentUserId;
14 | final Post post;
15 | final User author;
16 |
17 | PostView({this.currentUserId, this.post, this.author});
18 |
19 | @override
20 | _PostViewState createState() => _PostViewState();
21 | }
22 |
23 | class _PostViewState extends State {
24 | int _likeCount = 0;
25 | bool _isLiked = false;
26 | bool _heartAnim = false;
27 |
28 | @override
29 | void initState() {
30 | super.initState();
31 | _likeCount = widget.post.likeCount;
32 | _initPostLiked();
33 | }
34 |
35 | @override
36 | void didUpdateWidget(PostView oldWidget) {
37 | super.didUpdateWidget(oldWidget);
38 | if (oldWidget.post.likeCount != widget.post.likeCount) {
39 | _likeCount = widget.post.likeCount;
40 | }
41 | }
42 |
43 | _initPostLiked() async {
44 | bool isLiked = await DatabaseService.didLikePost(
45 | currentUserId: widget.currentUserId,
46 | post: widget.post,
47 | );
48 | if (mounted) {
49 | setState(() {
50 | _isLiked = isLiked;
51 | });
52 | }
53 | }
54 |
55 | _likePost() {
56 | if (_isLiked) {
57 | // Unlike Post
58 | DatabaseService.unlikePost(
59 | currentUserId: widget.currentUserId, post: widget.post);
60 | setState(() {
61 | _isLiked = false;
62 | _likeCount = _likeCount - 1;
63 | });
64 | } else {
65 | // Like Post
66 | DatabaseService.likePost(
67 | currentUserId: widget.currentUserId, post: widget.post);
68 | setState(() {
69 | _heartAnim = true;
70 | _isLiked = true;
71 | _likeCount = _likeCount + 1;
72 | });
73 | Timer(Duration(milliseconds: 350), () {
74 | setState(() {
75 | _heartAnim = false;
76 | });
77 | });
78 | }
79 | }
80 |
81 | @override
82 | Widget build(BuildContext context) {
83 | return Column(
84 | children: [
85 | GestureDetector(
86 | onTap: () => Navigator.push(
87 | context,
88 | MaterialPageRoute(
89 | builder: (_) => ProfileScreen(
90 | currentUserId: widget.currentUserId,
91 | userId: widget.post.authorId,
92 | ),
93 | ),
94 | ),
95 | child: Container(
96 | padding: EdgeInsets.symmetric(
97 | horizontal: 16.0,
98 | vertical: 10.0,
99 | ),
100 | child: Row(
101 | children: [
102 | CircleAvatar(
103 | radius: 25.0,
104 | backgroundColor: Colors.grey,
105 | backgroundImage: widget.author.profileImageUrl.isEmpty
106 | ? AssetImage('assets/images/user_placeholder.jpg')
107 | : CachedNetworkImageProvider(
108 | widget.author.profileImageUrl),
109 | ),
110 | SizedBox(width: 8.0),
111 | Text(
112 | widget.author.name,
113 | style: TextStyle(
114 | fontSize: 18.0,
115 | fontWeight: FontWeight.w600,
116 | ),
117 | )
118 | ],
119 | ),
120 | ),
121 | ),
122 | GestureDetector(
123 | onDoubleTap: _likePost,
124 | child: Stack(
125 | alignment: Alignment.center,
126 | children: [
127 | Container(
128 | height: MediaQuery.of(context).size.width,
129 | decoration: BoxDecoration(
130 | image: DecorationImage(
131 | image: CachedNetworkImageProvider(widget.post.imageUrl),
132 | fit: BoxFit.cover,
133 | ),
134 | ),
135 | ),
136 | _heartAnim
137 | ? Animator(
138 | duration: Duration(milliseconds: 300),
139 | tween: Tween(begin: 0.5, end: 1.4),
140 | curve: Curves.elasticOut,
141 | builder: (anim) => Transform.scale(
142 | scale: anim.value,
143 | child: Icon(
144 | Icons.favorite,
145 | size: 100.0,
146 | color: Colors.red[400],
147 | ),
148 | ),
149 | )
150 | : SizedBox.shrink(),
151 | ],
152 | ),
153 | ),
154 | Padding(
155 | padding: EdgeInsets.symmetric(horizontal: 8.0),
156 | child: Column(
157 | crossAxisAlignment: CrossAxisAlignment.start,
158 | children: [
159 | Row(
160 | children: [
161 | IconButton(
162 | icon: _isLiked
163 | ? Icon(
164 | Icons.favorite,
165 | color: Colors.red,
166 | )
167 | : Icon(Icons.favorite_border),
168 | iconSize: 30.0,
169 | onPressed: _likePost,
170 | ),
171 | IconButton(
172 | icon: Icon(Icons.comment),
173 | iconSize: 30.0,
174 | onPressed: () => Navigator.push(
175 | context,
176 | MaterialPageRoute(
177 | builder: (_) => CommentsScreen(
178 | post: widget.post,
179 | likeCount: _likeCount,
180 | ),
181 | ),
182 | ),
183 | ),
184 | ],
185 | ),
186 | Padding(
187 | padding: EdgeInsets.symmetric(horizontal: 12.0),
188 | child: Text(
189 | '${_likeCount.toString()} likes',
190 | style: TextStyle(
191 | fontSize: 16.0,
192 | fontWeight: FontWeight.bold,
193 | ),
194 | ),
195 | ),
196 | SizedBox(height: 4.0),
197 | Row(
198 | children: [
199 | Container(
200 | margin: EdgeInsets.only(
201 | left: 12.0,
202 | right: 6.0,
203 | ),
204 | child: Text(
205 | widget.author.name,
206 | style: TextStyle(
207 | fontSize: 16.0,
208 | fontWeight: FontWeight.bold,
209 | ),
210 | ),
211 | ),
212 | Expanded(
213 | child: Text(
214 | widget.post.caption,
215 | style: TextStyle(
216 | fontSize: 16.0,
217 | ),
218 | overflow: TextOverflow.ellipsis,
219 | ),
220 | ),
221 | ],
222 | ),
223 | SizedBox(height: 12.0),
224 | ],
225 | ),
226 | ),
227 | ],
228 | );
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | animator:
5 | dependency: "direct main"
6 | description:
7 | name: animator
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "1.0.0+5"
11 | archive:
12 | dependency: transitive
13 | description:
14 | name: archive
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.0.13"
18 | args:
19 | dependency: transitive
20 | description:
21 | name: args
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.6.0"
25 | async:
26 | dependency: transitive
27 | description:
28 | name: async
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "2.4.1"
32 | boolean_selector:
33 | dependency: transitive
34 | description:
35 | name: boolean_selector
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.0.0"
39 | cached_network_image:
40 | dependency: "direct main"
41 | description:
42 | name: cached_network_image
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "2.2.0+1"
46 | charcode:
47 | dependency: transitive
48 | description:
49 | name: charcode
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.1.3"
53 | clock:
54 | dependency: transitive
55 | description:
56 | name: clock
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.0.1"
60 | cloud_firestore:
61 | dependency: "direct main"
62 | description:
63 | name: cloud_firestore
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "0.12.11"
67 | collection:
68 | dependency: transitive
69 | description:
70 | name: collection
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "1.14.12"
74 | convert:
75 | dependency: transitive
76 | description:
77 | name: convert
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "2.1.1"
81 | crypto:
82 | dependency: transitive
83 | description:
84 | name: crypto
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "2.1.4"
88 | cupertino_icons:
89 | dependency: "direct main"
90 | description:
91 | name: cupertino_icons
92 | url: "https://pub.dartlang.org"
93 | source: hosted
94 | version: "0.1.3"
95 | file:
96 | dependency: transitive
97 | description:
98 | name: file
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "5.1.0"
102 | firebase:
103 | dependency: transitive
104 | description:
105 | name: firebase
106 | url: "https://pub.dartlang.org"
107 | source: hosted
108 | version: "7.3.0"
109 | firebase_analytics:
110 | dependency: "direct main"
111 | description:
112 | name: firebase_analytics
113 | url: "https://pub.dartlang.org"
114 | source: hosted
115 | version: "5.0.11"
116 | firebase_auth:
117 | dependency: "direct main"
118 | description:
119 | name: firebase_auth
120 | url: "https://pub.dartlang.org"
121 | source: hosted
122 | version: "0.14.0+9"
123 | firebase_core:
124 | dependency: "direct main"
125 | description:
126 | name: firebase_core
127 | url: "https://pub.dartlang.org"
128 | source: hosted
129 | version: "0.4.4+3"
130 | firebase_core_platform_interface:
131 | dependency: transitive
132 | description:
133 | name: firebase_core_platform_interface
134 | url: "https://pub.dartlang.org"
135 | source: hosted
136 | version: "1.0.4"
137 | firebase_core_web:
138 | dependency: transitive
139 | description:
140 | name: firebase_core_web
141 | url: "https://pub.dartlang.org"
142 | source: hosted
143 | version: "0.1.1+2"
144 | firebase_storage:
145 | dependency: "direct main"
146 | description:
147 | name: firebase_storage
148 | url: "https://pub.dartlang.org"
149 | source: hosted
150 | version: "3.1.5"
151 | flutter:
152 | dependency: "direct main"
153 | description: flutter
154 | source: sdk
155 | version: "0.0.0"
156 | flutter_cache_manager:
157 | dependency: transitive
158 | description:
159 | name: flutter_cache_manager
160 | url: "https://pub.dartlang.org"
161 | source: hosted
162 | version: "1.2.2"
163 | flutter_image_compress:
164 | dependency: "direct main"
165 | description:
166 | name: flutter_image_compress
167 | url: "https://pub.dartlang.org"
168 | source: hosted
169 | version: "0.6.7"
170 | flutter_plugin_android_lifecycle:
171 | dependency: transitive
172 | description:
173 | name: flutter_plugin_android_lifecycle
174 | url: "https://pub.dartlang.org"
175 | source: hosted
176 | version: "1.0.7"
177 | flutter_test:
178 | dependency: "direct dev"
179 | description: flutter
180 | source: sdk
181 | version: "0.0.0"
182 | flutter_web_plugins:
183 | dependency: transitive
184 | description: flutter
185 | source: sdk
186 | version: "0.0.0"
187 | http:
188 | dependency: transitive
189 | description:
190 | name: http
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "0.12.1"
194 | http_parser:
195 | dependency: transitive
196 | description:
197 | name: http_parser
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "3.1.4"
201 | image:
202 | dependency: transitive
203 | description:
204 | name: image
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "2.1.12"
208 | image_cropper:
209 | dependency: "direct main"
210 | description:
211 | name: image_cropper
212 | url: "https://pub.dartlang.org"
213 | source: hosted
214 | version: "1.2.2"
215 | image_picker:
216 | dependency: "direct main"
217 | description:
218 | name: image_picker
219 | url: "https://pub.dartlang.org"
220 | source: hosted
221 | version: "0.6.6+1"
222 | image_picker_platform_interface:
223 | dependency: transitive
224 | description:
225 | name: image_picker_platform_interface
226 | url: "https://pub.dartlang.org"
227 | source: hosted
228 | version: "1.0.0"
229 | intl:
230 | dependency: "direct main"
231 | description:
232 | name: intl
233 | url: "https://pub.dartlang.org"
234 | source: hosted
235 | version: "0.16.1"
236 | js:
237 | dependency: transitive
238 | description:
239 | name: js
240 | url: "https://pub.dartlang.org"
241 | source: hosted
242 | version: "0.6.1+1"
243 | matcher:
244 | dependency: transitive
245 | description:
246 | name: matcher
247 | url: "https://pub.dartlang.org"
248 | source: hosted
249 | version: "0.12.6"
250 | meta:
251 | dependency: transitive
252 | description:
253 | name: meta
254 | url: "https://pub.dartlang.org"
255 | source: hosted
256 | version: "1.1.8"
257 | path:
258 | dependency: transitive
259 | description:
260 | name: path
261 | url: "https://pub.dartlang.org"
262 | source: hosted
263 | version: "1.6.4"
264 | path_provider:
265 | dependency: "direct main"
266 | description:
267 | name: path_provider
268 | url: "https://pub.dartlang.org"
269 | source: hosted
270 | version: "1.6.8"
271 | path_provider_macos:
272 | dependency: transitive
273 | description:
274 | name: path_provider_macos
275 | url: "https://pub.dartlang.org"
276 | source: hosted
277 | version: "0.0.4+2"
278 | path_provider_platform_interface:
279 | dependency: transitive
280 | description:
281 | name: path_provider_platform_interface
282 | url: "https://pub.dartlang.org"
283 | source: hosted
284 | version: "1.0.2"
285 | pedantic:
286 | dependency: transitive
287 | description:
288 | name: pedantic
289 | url: "https://pub.dartlang.org"
290 | source: hosted
291 | version: "1.9.0"
292 | petitparser:
293 | dependency: transitive
294 | description:
295 | name: petitparser
296 | url: "https://pub.dartlang.org"
297 | source: hosted
298 | version: "2.4.0"
299 | platform:
300 | dependency: transitive
301 | description:
302 | name: platform
303 | url: "https://pub.dartlang.org"
304 | source: hosted
305 | version: "2.2.1"
306 | plugin_platform_interface:
307 | dependency: transitive
308 | description:
309 | name: plugin_platform_interface
310 | url: "https://pub.dartlang.org"
311 | source: hosted
312 | version: "1.0.2"
313 | provider:
314 | dependency: "direct main"
315 | description:
316 | name: provider
317 | url: "https://pub.dartlang.org"
318 | source: hosted
319 | version: "3.2.0"
320 | quiver:
321 | dependency: transitive
322 | description:
323 | name: quiver
324 | url: "https://pub.dartlang.org"
325 | source: hosted
326 | version: "2.1.3"
327 | rxdart:
328 | dependency: transitive
329 | description:
330 | name: rxdart
331 | url: "https://pub.dartlang.org"
332 | source: hosted
333 | version: "0.24.0"
334 | sky_engine:
335 | dependency: transitive
336 | description: flutter
337 | source: sdk
338 | version: "0.0.99"
339 | source_span:
340 | dependency: transitive
341 | description:
342 | name: source_span
343 | url: "https://pub.dartlang.org"
344 | source: hosted
345 | version: "1.7.0"
346 | sqflite:
347 | dependency: transitive
348 | description:
349 | name: sqflite
350 | url: "https://pub.dartlang.org"
351 | source: hosted
352 | version: "1.3.0+1"
353 | sqflite_common:
354 | dependency: transitive
355 | description:
356 | name: sqflite_common
357 | url: "https://pub.dartlang.org"
358 | source: hosted
359 | version: "1.0.1"
360 | stack_trace:
361 | dependency: transitive
362 | description:
363 | name: stack_trace
364 | url: "https://pub.dartlang.org"
365 | source: hosted
366 | version: "1.9.3"
367 | states_rebuilder:
368 | dependency: transitive
369 | description:
370 | name: states_rebuilder
371 | url: "https://pub.dartlang.org"
372 | source: hosted
373 | version: "1.15.0"
374 | stream_channel:
375 | dependency: transitive
376 | description:
377 | name: stream_channel
378 | url: "https://pub.dartlang.org"
379 | source: hosted
380 | version: "2.0.0"
381 | string_scanner:
382 | dependency: transitive
383 | description:
384 | name: string_scanner
385 | url: "https://pub.dartlang.org"
386 | source: hosted
387 | version: "1.0.5"
388 | synchronized:
389 | dependency: transitive
390 | description:
391 | name: synchronized
392 | url: "https://pub.dartlang.org"
393 | source: hosted
394 | version: "2.2.0"
395 | term_glyph:
396 | dependency: transitive
397 | description:
398 | name: term_glyph
399 | url: "https://pub.dartlang.org"
400 | source: hosted
401 | version: "1.1.0"
402 | test_api:
403 | dependency: transitive
404 | description:
405 | name: test_api
406 | url: "https://pub.dartlang.org"
407 | source: hosted
408 | version: "0.2.15"
409 | typed_data:
410 | dependency: transitive
411 | description:
412 | name: typed_data
413 | url: "https://pub.dartlang.org"
414 | source: hosted
415 | version: "1.1.6"
416 | uuid:
417 | dependency: "direct main"
418 | description:
419 | name: uuid
420 | url: "https://pub.dartlang.org"
421 | source: hosted
422 | version: "2.0.4"
423 | vector_math:
424 | dependency: transitive
425 | description:
426 | name: vector_math
427 | url: "https://pub.dartlang.org"
428 | source: hosted
429 | version: "2.0.8"
430 | xml:
431 | dependency: transitive
432 | description:
433 | name: xml
434 | url: "https://pub.dartlang.org"
435 | source: hosted
436 | version: "3.6.1"
437 | sdks:
438 | dart: ">=2.7.0 <3.0.0"
439 | flutter: ">=1.12.13+hotfix.5 <2.0.0"
440 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_instagram_clone
2 | description: A new Flutter project.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | environment:
17 | sdk: ">=2.1.0 <3.0.0"
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 |
23 | # The following adds the Cupertino Icons font to your application.
24 | # Use with the CupertinoIcons class for iOS style icons.
25 | cupertino_icons: ^0.1.2
26 | firebase_core: ^0.4.0+9
27 | firebase_analytics: ^5.0.2
28 | firebase_auth: ^0.14.0+5
29 | cloud_firestore: ^0.12.9+4
30 | image_picker: ^0.6.1+4
31 | cached_network_image: ^2.0.0-rc
32 | firebase_storage: ^3.0.6
33 | flutter_image_compress: ^0.6.3
34 | path_provider: ^1.2.0
35 | uuid: ^2.0.2
36 | provider: ^3.2.0
37 | image_cropper: ^1.1.0
38 | animator: ^1.0.0+3
39 | intl: ^0.16.0
40 |
41 | dev_dependencies:
42 | flutter_test:
43 | sdk: flutter
44 |
45 |
46 | # For information on the generic Dart part of this file, see the
47 | # following page: https://dart.dev/tools/pub/pubspec
48 |
49 | # The following section is specific to Flutter.
50 | flutter:
51 |
52 | # The following line ensures that the Material Icons font is
53 | # included with your application, so that you can use the icons in
54 | # the material Icons class.
55 | uses-material-design: true
56 |
57 | # To add assets to your application, add an assets section, like this:
58 | assets:
59 | - assets/images/
60 | # - images/a_dot_ham.jpeg
61 |
62 | # An image asset can refer to one or more resolution-specific "variants", see
63 | # https://flutter.dev/assets-and-images/#resolution-aware.
64 |
65 | # For details regarding adding assets from package dependencies, see
66 | # https://flutter.dev/assets-and-images/#from-packages
67 |
68 | # To add custom fonts to your application, add a fonts section here,
69 | # in this "flutter" section. Each entry in this list should have a
70 | # "family" key with the font family name, and a "fonts" key with a
71 | # list giving the asset and other descriptors for the font. For
72 | # example:
73 | fonts:
74 | - family: Billabong
75 | fonts:
76 | - asset: assets/fonts/Billabong.ttf
77 | # - asset: fonts/Schyler-Italic.ttf
78 | # style: italic
79 | # - family: Trajan Pro
80 | # fonts:
81 | # - asset: fonts/TrajanPro.ttf
82 | # - asset: fonts/TrajanPro_Bold.ttf
83 | # weight: 700
84 | #
85 | # For details regarding fonts from package dependencies,
86 | # see https://flutter.dev/custom-fonts/#from-packages
87 |
--------------------------------------------------------------------------------
/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_instagram_clone/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 |
--------------------------------------------------------------------------------