├── .github
└── workflows
│ ├── deploy.yml
│ ├── deploy.yml.backup
│ └── main.yml
├── .gitignore
├── LICENSE
├── README.md
├── client
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── client
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── 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-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── images
│ ├── chansung.png
│ ├── gde_logo.png
│ └── paul.png
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── 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
│ ├── rest_client.dart
│ ├── screens
│ │ └── main_screen.dart
│ └── widgets
│ │ └── image_grid.dart
├── pubspec.lock
├── pubspec.yaml
├── test
│ └── widget_test.dart
└── web
│ ├── favicon.png
│ ├── icons
│ ├── Icon-192.png
│ └── Icon-512.png
│ ├── index.html
│ └── manifest.json
├── gke
├── deployment.yaml
├── gke-managed-certificate.yaml
├── ingress.yaml
└── service.yaml
├── notebooks
└── Image_Search_CLIP.ipynb
└── server
├── Dockerfile
├── __init__.py
├── k8s_deploy.sh
├── main.py
├── perform_search.py
├── pixabay_utils.py
├── requirements.txt
└── similarity_utils.py
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy to GKE
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | jobs:
8 | build:
9 | name: Build and Deploy
10 | runs-on: ubuntu-latest
11 | defaults:
12 | run:
13 | working-directory: server
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v2
18 |
19 | # Setup gcloud CLI
20 | - uses: google-github-actions/setup-gcloud
21 | with:
22 | service_account_key: ${{ secrets.GKE_SA_KEY }}
23 | project_id: ${{ secrets.GKE_PROJECT }}
24 |
25 | # Configure docker to use the gcloud command-line tool as a credential helper
26 | - run: |-
27 | gcloud --quiet auth configure-docker
28 |
29 | # Get the GKE credentials so we can deploy to the cluster
30 | - uses: google-github-actions/get-gke-credentials
31 | with:
32 | cluster_name: ${{ env.GKE_CLUSTER }}
33 | location: ${{ env.GKE_ZONE }}
34 | credentials: ${{ secrets.GKE_SA_KEY }}
35 |
36 | # Build the Docker image
37 | - name: Build
38 | run: |-
39 | docker build \
40 | --tag "gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA" \
41 | --build-arg GITHUB_SHA="$GITHUB_SHA" \
42 | --build-arg GITHUB_REF="$GITHUB_REF" \
43 | .
44 |
45 | # Push the Docker image to Google Container Registry
46 | - name: Publish
47 | run: |-
48 | docker push "gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA"
49 |
50 | - name: Deploy
51 | run: |-
52 | kubectl apply -f gke/deployment.yaml
53 | kubectl set image \
54 | deployment/clip-search \
55 | search-service-p4sml="gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA"
56 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml.backup:
--------------------------------------------------------------------------------
1 | # name: Deploy an App Engine app
2 |
3 | # on:
4 | # push:
5 | # branches: [main]
6 | # pull_request:
7 | # workflow_dispatch:
8 |
9 | # jobs:
10 | # commit:
11 | # runs-on: ubuntu-latest
12 | # outputs:
13 | # string: ${{ steps.changestring.outputs.string}}
14 |
15 | # steps:
16 | # - name: Checkout
17 | # uses: actions/checkout@v2
18 |
19 | # - name: Inject environtment variable to app.yml
20 | # run: envsubst < ./server/app.yaml | tee ./server/app.yaml
21 | # env:
22 | # PIXABAY_API_KEY: ${{ secrets.PIXABAY_API_KEY }}
23 |
24 | # - name: Setup Gcloud Service Account
25 | # uses: google-github-actions/setup-gcloud@master
26 | # with:
27 | # version: '290.0.1'
28 | # service_account_key: ${{ secrets.gcp_credentials }}
29 | # export_default_credentials: true
30 |
31 | # - name: Deploy App Engine
32 | # uses: google-github-actions/deploy-appengine@main
33 | # with:
34 | # working_directory: server
35 | # project_id: ${{ secrets.gcp_project }}
36 | # deliverables: app.yaml
37 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Flutter Web Build and Deployment
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | workflow_dispatch:
8 |
9 | jobs:
10 | flutter-build:
11 | runs-on: ubuntu-latest
12 | defaults:
13 | run:
14 | working-directory: client
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-java@v1
18 | with:
19 | java-version: "12.x"
20 | - uses: subosito/flutter-action@v1
21 | with:
22 | channel: "stable"
23 | - run: flutter pub get
24 | - run: flutter pub run build_runner build
25 | - run: flutter build apk
26 |
27 | deploy-to-gh-pages:
28 | runs-on: ubuntu-latest
29 |
30 | if: github.ref == 'refs/heads/main'
31 |
32 | needs:
33 | - flutter-build
34 |
35 | defaults:
36 | run:
37 | working-directory: client
38 | steps:
39 | - uses: actions/checkout@v2
40 | - uses: actions/setup-java@v1
41 | with:
42 | java-version: "12.x"
43 | - uses: subosito/flutter-action@v1
44 | with:
45 | channel: "stable"
46 | - run: flutter pub get
47 | - run: flutter build web --release
48 | - uses: JamesIves/github-pages-deploy-action@4.1.4
49 | with:
50 | branch: gh-pages
51 | folder: ./client/build/web
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | wheels/
22 | pip-wheel-metadata/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 |
53 | # Translations
54 | *.mo
55 | *.pot
56 |
57 | # Django stuff:
58 | *.log
59 | local_settings.py
60 | db.sqlite3
61 | db.sqlite3-journal
62 |
63 | # Flask stuff:
64 | instance/
65 | .webassets-cache
66 |
67 | # Scrapy stuff:
68 | .scrapy
69 |
70 | # Sphinx documentation
71 | docs/_build/
72 |
73 | # PyBuilder
74 | target/
75 |
76 | # Jupyter Notebook
77 | .ipynb_checkpoints
78 |
79 | # IPython
80 | profile_default/
81 | ipython_config.py
82 |
83 | # pyenv
84 | .python-version
85 |
86 | # pipenv
87 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
88 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
89 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
90 | # install all needed dependencies.
91 | #Pipfile.lock
92 |
93 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
94 | __pypackages__/
95 |
96 | # Celery stuff
97 | celerybeat-schedule
98 | celerybeat.pid
99 |
100 | # SageMath parsed files
101 | *.sage.py
102 |
103 | # Environments
104 | .env
105 | .venv
106 | env/
107 | venv/
108 | ENV/
109 | env.bak/
110 | venv.bak/
111 |
112 | # Spyder project settings
113 | .spyderproject
114 | .spyproject
115 |
116 | # Rope project settings
117 | .ropeproject
118 |
119 | # mkdocs documentation
120 | /site
121 |
122 | # mypy
123 | .mypy_cache/
124 | .dmypy.json
125 | dmypy.json
126 |
127 | # Pyre type checker
128 | .pyre/
129 |
130 | # Others
131 | .idea/
132 | .DS_Store
133 | .ipynb_checkpoints/
134 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/deep-diver/image_search_with_natural_language/actions/workflows/main.yml)
2 |
3 | # Image Search with Natural Language Queries
4 |
5 |
8 |
9 |
10 |
11 | This project shows how to build an image search utility using natural language queries. Our aim is to use different and related serverless GCP services to demonstrate this. At the core of our project is [OpenAI's CLIP model](https://openai.com/blog/clip/). It makes use of two encoders - one for images and one for texts. Each encoder is trained to learn representations such that similar images and text embeddings are projected as close as possible.
12 |
13 |
14 |
15 | For details, please refer to this blog post: [Image search with natural language queries](https://cloud.google.com/blog/topics/developers-practitioners/image-search-natural-language-queries).
16 |
17 | Following depicts the architecture design of our application:
18 |
19 |
20 |
21 |
22 |
23 | Here's a live application demo:
24 |
25 |
26 | https://user-images.githubusercontent.com/22957388/124558759-926c6e00-de58-11eb-8d3a-88e919655ca5.mp4
27 |
28 |
29 | You can visit this URL for interacting with the application: http://deep-diver.github.io/image_search_with_natural_language/. You can also check out this [Colab Notebook](https://colab.research.google.com/github/deep-diver/image_search_with_natural_language/blob/main/notebooks/Image_Search_CLIP.ipynb) that accompanies this project.
30 |
31 | ## Participants
32 |
33 | - ML GDE, [Chansung Park](https://twitter.com/algo_diver)
34 | - ML GDE, [Paul Sayak](https://twitter.com/RisingSayak)
35 |
36 | ## Acknowledgements
37 |
38 | [ML-GDE program](https://developers.google.com/programs/experts/) for providing GCP credits that supported this project. Thanks to Karl Weinmeister and Soonson Kwon of Google for reviewing the initial draft of this post.
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/client/.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: b22742018b3edf16c6cadd7b76d9db5e7f9064b5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # client
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/client/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/client/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 30
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | defaultConfig {
36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
37 | applicationId "com.example.client"
38 | minSdkVersion 16
39 | targetSdkVersion 30
40 | versionCode flutterVersionCode.toInteger()
41 | versionName flutterVersionName
42 | }
43 |
44 | buildTypes {
45 | release {
46 | // TODO: Add your own signing config for the release build.
47 | // Signing with the debug keys for now, so `flutter run --release` works.
48 | signingConfig signingConfigs.debug
49 | }
50 | }
51 | }
52 |
53 | flutter {
54 | source '../..'
55 | }
56 |
57 | dependencies {
58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
59 | }
60 |
--------------------------------------------------------------------------------
/client/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
13 |
17 |
21 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/client/android/app/src/main/kotlin/com/example/client/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.client
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/client/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/client/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/client/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-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/client/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/client/images/chansung.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/images/chansung.png
--------------------------------------------------------------------------------
/client/images/gde_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/images/gde_logo.png
--------------------------------------------------------------------------------
/client/images/paul.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/images/paul.png
--------------------------------------------------------------------------------
/client/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/client/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/client/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/client/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/client/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 flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/client/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - url_launcher (0.0.1):
4 | - Flutter
5 |
6 | DEPENDENCIES:
7 | - Flutter (from `Flutter`)
8 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
9 |
10 | EXTERNAL SOURCES:
11 | Flutter:
12 | :path: Flutter
13 | url_launcher:
14 | :path: ".symlinks/plugins/url_launcher/ios"
15 |
16 | SPEC CHECKSUMS:
17 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
18 | url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
19 |
20 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
21 |
22 | COCOAPODS: 1.10.0
23 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | A82728331E0631D20005ADB6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF01A2EA661A0B5C0F885404 /* Pods_Runner.framework */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXCopyFilesBuildPhase section */
20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
21 | isa = PBXCopyFilesBuildPhase;
22 | buildActionMask = 2147483647;
23 | dstPath = "";
24 | dstSubfolderSpec = 10;
25 | files = (
26 | );
27 | name = "Embed Frameworks";
28 | runOnlyForDeploymentPostprocessing = 0;
29 | };
30 | /* End PBXCopyFilesBuildPhase section */
31 |
32 | /* Begin PBXFileReference section */
33 | 093D89FE33C82F76DE1451F7 /* 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 = ""; };
34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
37 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
38 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
39 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
40 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
41 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
42 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
43 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
44 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
45 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
46 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
47 | AC60AB0D92602C115196556D /* 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 = ""; };
48 | ADC7CF4D7FA846116CF1592D /* 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 = ""; };
49 | EF01A2EA661A0B5C0F885404 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
50 | /* End PBXFileReference section */
51 |
52 | /* Begin PBXFrameworksBuildPhase section */
53 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | A82728331E0631D20005ADB6 /* Pods_Runner.framework in Frameworks */,
58 | );
59 | runOnlyForDeploymentPostprocessing = 0;
60 | };
61 | /* End PBXFrameworksBuildPhase section */
62 |
63 | /* Begin PBXGroup section */
64 | 5456B0443F48F663D1D27B1B /* Frameworks */ = {
65 | isa = PBXGroup;
66 | children = (
67 | EF01A2EA661A0B5C0F885404 /* Pods_Runner.framework */,
68 | );
69 | name = Frameworks;
70 | sourceTree = "";
71 | };
72 | 561026F80A9452A1BB6B722E /* Pods */ = {
73 | isa = PBXGroup;
74 | children = (
75 | 093D89FE33C82F76DE1451F7 /* Pods-Runner.debug.xcconfig */,
76 | AC60AB0D92602C115196556D /* Pods-Runner.release.xcconfig */,
77 | ADC7CF4D7FA846116CF1592D /* Pods-Runner.profile.xcconfig */,
78 | );
79 | name = Pods;
80 | path = Pods;
81 | sourceTree = "";
82 | };
83 | 9740EEB11CF90186004384FC /* Flutter */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
87 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
88 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
89 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
90 | );
91 | name = Flutter;
92 | sourceTree = "";
93 | };
94 | 97C146E51CF9000F007C117D = {
95 | isa = PBXGroup;
96 | children = (
97 | 9740EEB11CF90186004384FC /* Flutter */,
98 | 97C146F01CF9000F007C117D /* Runner */,
99 | 97C146EF1CF9000F007C117D /* Products */,
100 | 561026F80A9452A1BB6B722E /* Pods */,
101 | 5456B0443F48F663D1D27B1B /* Frameworks */,
102 | );
103 | sourceTree = "";
104 | };
105 | 97C146EF1CF9000F007C117D /* Products */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 97C146EE1CF9000F007C117D /* Runner.app */,
109 | );
110 | name = Products;
111 | sourceTree = "";
112 | };
113 | 97C146F01CF9000F007C117D /* Runner */ = {
114 | isa = PBXGroup;
115 | children = (
116 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
117 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
118 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
119 | 97C147021CF9000F007C117D /* Info.plist */,
120 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
121 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
122 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
123 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
124 | );
125 | path = Runner;
126 | sourceTree = "";
127 | };
128 | /* End PBXGroup section */
129 |
130 | /* Begin PBXNativeTarget section */
131 | 97C146ED1CF9000F007C117D /* Runner */ = {
132 | isa = PBXNativeTarget;
133 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
134 | buildPhases = (
135 | 5B28D00048731019225BCA81 /* [CP] Check Pods Manifest.lock */,
136 | 9740EEB61CF901F6004384FC /* Run Script */,
137 | 97C146EA1CF9000F007C117D /* Sources */,
138 | 97C146EB1CF9000F007C117D /* Frameworks */,
139 | 97C146EC1CF9000F007C117D /* Resources */,
140 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
142 | F4BB777249949F5E82FCCAEC /* [CP] Embed Pods Frameworks */,
143 | );
144 | buildRules = (
145 | );
146 | dependencies = (
147 | );
148 | name = Runner;
149 | productName = Runner;
150 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
151 | productType = "com.apple.product-type.application";
152 | };
153 | /* End PBXNativeTarget section */
154 |
155 | /* Begin PBXProject section */
156 | 97C146E61CF9000F007C117D /* Project object */ = {
157 | isa = PBXProject;
158 | attributes = {
159 | LastUpgradeCheck = 1020;
160 | ORGANIZATIONNAME = "";
161 | TargetAttributes = {
162 | 97C146ED1CF9000F007C117D = {
163 | CreatedOnToolsVersion = 7.3.1;
164 | LastSwiftMigration = 1100;
165 | };
166 | };
167 | };
168 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
169 | compatibilityVersion = "Xcode 9.3";
170 | developmentRegion = en;
171 | hasScannedForEncodings = 0;
172 | knownRegions = (
173 | en,
174 | Base,
175 | );
176 | mainGroup = 97C146E51CF9000F007C117D;
177 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
178 | projectDirPath = "";
179 | projectRoot = "";
180 | targets = (
181 | 97C146ED1CF9000F007C117D /* Runner */,
182 | );
183 | };
184 | /* End PBXProject section */
185 |
186 | /* Begin PBXResourcesBuildPhase section */
187 | 97C146EC1CF9000F007C117D /* Resources */ = {
188 | isa = PBXResourcesBuildPhase;
189 | buildActionMask = 2147483647;
190 | files = (
191 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
192 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
193 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
195 | );
196 | runOnlyForDeploymentPostprocessing = 0;
197 | };
198 | /* End PBXResourcesBuildPhase section */
199 |
200 | /* Begin PBXShellScriptBuildPhase section */
201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
202 | isa = PBXShellScriptBuildPhase;
203 | buildActionMask = 2147483647;
204 | files = (
205 | );
206 | inputPaths = (
207 | );
208 | name = "Thin Binary";
209 | outputPaths = (
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | shellPath = /bin/sh;
213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
214 | };
215 | 5B28D00048731019225BCA81 /* [CP] Check Pods Manifest.lock */ = {
216 | isa = PBXShellScriptBuildPhase;
217 | buildActionMask = 2147483647;
218 | files = (
219 | );
220 | inputFileListPaths = (
221 | );
222 | inputPaths = (
223 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
224 | "${PODS_ROOT}/Manifest.lock",
225 | );
226 | name = "[CP] Check Pods Manifest.lock";
227 | outputFileListPaths = (
228 | );
229 | outputPaths = (
230 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
231 | );
232 | runOnlyForDeploymentPostprocessing = 0;
233 | shellPath = /bin/sh;
234 | 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";
235 | showEnvVarsInLog = 0;
236 | };
237 | 9740EEB61CF901F6004384FC /* Run Script */ = {
238 | isa = PBXShellScriptBuildPhase;
239 | buildActionMask = 2147483647;
240 | files = (
241 | );
242 | inputPaths = (
243 | );
244 | name = "Run Script";
245 | outputPaths = (
246 | );
247 | runOnlyForDeploymentPostprocessing = 0;
248 | shellPath = /bin/sh;
249 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
250 | };
251 | F4BB777249949F5E82FCCAEC /* [CP] Embed Pods Frameworks */ = {
252 | isa = PBXShellScriptBuildPhase;
253 | buildActionMask = 2147483647;
254 | files = (
255 | );
256 | inputFileListPaths = (
257 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
258 | );
259 | name = "[CP] Embed Pods Frameworks";
260 | outputFileListPaths = (
261 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | shellPath = /bin/sh;
265 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
266 | showEnvVarsInLog = 0;
267 | };
268 | /* End PBXShellScriptBuildPhase section */
269 |
270 | /* Begin PBXSourcesBuildPhase section */
271 | 97C146EA1CF9000F007C117D /* Sources */ = {
272 | isa = PBXSourcesBuildPhase;
273 | buildActionMask = 2147483647;
274 | files = (
275 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
276 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
277 | );
278 | runOnlyForDeploymentPostprocessing = 0;
279 | };
280 | /* End PBXSourcesBuildPhase section */
281 |
282 | /* Begin PBXVariantGroup section */
283 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
284 | isa = PBXVariantGroup;
285 | children = (
286 | 97C146FB1CF9000F007C117D /* Base */,
287 | );
288 | name = Main.storyboard;
289 | sourceTree = "";
290 | };
291 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
292 | isa = PBXVariantGroup;
293 | children = (
294 | 97C147001CF9000F007C117D /* Base */,
295 | );
296 | name = LaunchScreen.storyboard;
297 | sourceTree = "";
298 | };
299 | /* End PBXVariantGroup section */
300 |
301 | /* Begin XCBuildConfiguration section */
302 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
303 | isa = XCBuildConfiguration;
304 | buildSettings = {
305 | ALWAYS_SEARCH_USER_PATHS = NO;
306 | CLANG_ANALYZER_NONNULL = YES;
307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
308 | CLANG_CXX_LIBRARY = "libc++";
309 | CLANG_ENABLE_MODULES = YES;
310 | CLANG_ENABLE_OBJC_ARC = YES;
311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
312 | CLANG_WARN_BOOL_CONVERSION = YES;
313 | CLANG_WARN_COMMA = YES;
314 | CLANG_WARN_CONSTANT_CONVERSION = YES;
315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
317 | CLANG_WARN_EMPTY_BODY = YES;
318 | CLANG_WARN_ENUM_CONVERSION = YES;
319 | CLANG_WARN_INFINITE_RECURSION = YES;
320 | CLANG_WARN_INT_CONVERSION = YES;
321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
326 | CLANG_WARN_STRICT_PROTOTYPES = YES;
327 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
328 | CLANG_WARN_UNREACHABLE_CODE = YES;
329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
331 | COPY_PHASE_STRIP = NO;
332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
333 | ENABLE_NS_ASSERTIONS = NO;
334 | ENABLE_STRICT_OBJC_MSGSEND = YES;
335 | GCC_C_LANGUAGE_STANDARD = gnu99;
336 | GCC_NO_COMMON_BLOCKS = YES;
337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
339 | GCC_WARN_UNDECLARED_SELECTOR = YES;
340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
341 | GCC_WARN_UNUSED_FUNCTION = YES;
342 | GCC_WARN_UNUSED_VARIABLE = YES;
343 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
344 | MTL_ENABLE_DEBUG_INFO = NO;
345 | SDKROOT = iphoneos;
346 | SUPPORTED_PLATFORMS = iphoneos;
347 | TARGETED_DEVICE_FAMILY = "1,2";
348 | VALIDATE_PRODUCT = YES;
349 | };
350 | name = Profile;
351 | };
352 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
353 | isa = XCBuildConfiguration;
354 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
355 | buildSettings = {
356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
357 | CLANG_ENABLE_MODULES = YES;
358 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
359 | ENABLE_BITCODE = NO;
360 | INFOPLIST_FILE = Runner/Info.plist;
361 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
362 | PRODUCT_BUNDLE_IDENTIFIER = com.example.client;
363 | PRODUCT_NAME = "$(TARGET_NAME)";
364 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
365 | SWIFT_VERSION = 5.0;
366 | VERSIONING_SYSTEM = "apple-generic";
367 | };
368 | name = Profile;
369 | };
370 | 97C147031CF9000F007C117D /* Debug */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ALWAYS_SEARCH_USER_PATHS = NO;
374 | CLANG_ANALYZER_NONNULL = YES;
375 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
376 | CLANG_CXX_LIBRARY = "libc++";
377 | CLANG_ENABLE_MODULES = YES;
378 | CLANG_ENABLE_OBJC_ARC = YES;
379 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
380 | CLANG_WARN_BOOL_CONVERSION = YES;
381 | CLANG_WARN_COMMA = YES;
382 | CLANG_WARN_CONSTANT_CONVERSION = YES;
383 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
384 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
385 | CLANG_WARN_EMPTY_BODY = YES;
386 | CLANG_WARN_ENUM_CONVERSION = YES;
387 | CLANG_WARN_INFINITE_RECURSION = YES;
388 | CLANG_WARN_INT_CONVERSION = YES;
389 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
390 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
391 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
392 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
393 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
394 | CLANG_WARN_STRICT_PROTOTYPES = YES;
395 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
396 | CLANG_WARN_UNREACHABLE_CODE = YES;
397 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
398 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
399 | COPY_PHASE_STRIP = NO;
400 | DEBUG_INFORMATION_FORMAT = dwarf;
401 | ENABLE_STRICT_OBJC_MSGSEND = YES;
402 | ENABLE_TESTABILITY = YES;
403 | GCC_C_LANGUAGE_STANDARD = gnu99;
404 | GCC_DYNAMIC_NO_PIC = NO;
405 | GCC_NO_COMMON_BLOCKS = YES;
406 | GCC_OPTIMIZATION_LEVEL = 0;
407 | GCC_PREPROCESSOR_DEFINITIONS = (
408 | "DEBUG=1",
409 | "$(inherited)",
410 | );
411 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
412 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
413 | GCC_WARN_UNDECLARED_SELECTOR = YES;
414 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
415 | GCC_WARN_UNUSED_FUNCTION = YES;
416 | GCC_WARN_UNUSED_VARIABLE = YES;
417 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
418 | MTL_ENABLE_DEBUG_INFO = YES;
419 | ONLY_ACTIVE_ARCH = YES;
420 | SDKROOT = iphoneos;
421 | TARGETED_DEVICE_FAMILY = "1,2";
422 | };
423 | name = Debug;
424 | };
425 | 97C147041CF9000F007C117D /* Release */ = {
426 | isa = XCBuildConfiguration;
427 | buildSettings = {
428 | ALWAYS_SEARCH_USER_PATHS = NO;
429 | CLANG_ANALYZER_NONNULL = YES;
430 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
431 | CLANG_CXX_LIBRARY = "libc++";
432 | CLANG_ENABLE_MODULES = YES;
433 | CLANG_ENABLE_OBJC_ARC = YES;
434 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
435 | CLANG_WARN_BOOL_CONVERSION = YES;
436 | CLANG_WARN_COMMA = YES;
437 | CLANG_WARN_CONSTANT_CONVERSION = YES;
438 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
439 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
440 | CLANG_WARN_EMPTY_BODY = YES;
441 | CLANG_WARN_ENUM_CONVERSION = YES;
442 | CLANG_WARN_INFINITE_RECURSION = YES;
443 | CLANG_WARN_INT_CONVERSION = YES;
444 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
445 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
446 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
447 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
448 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
449 | CLANG_WARN_STRICT_PROTOTYPES = YES;
450 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
451 | CLANG_WARN_UNREACHABLE_CODE = YES;
452 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
453 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
454 | COPY_PHASE_STRIP = NO;
455 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
456 | ENABLE_NS_ASSERTIONS = NO;
457 | ENABLE_STRICT_OBJC_MSGSEND = YES;
458 | GCC_C_LANGUAGE_STANDARD = gnu99;
459 | GCC_NO_COMMON_BLOCKS = YES;
460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
462 | GCC_WARN_UNDECLARED_SELECTOR = YES;
463 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
464 | GCC_WARN_UNUSED_FUNCTION = YES;
465 | GCC_WARN_UNUSED_VARIABLE = YES;
466 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
467 | MTL_ENABLE_DEBUG_INFO = NO;
468 | SDKROOT = iphoneos;
469 | SUPPORTED_PLATFORMS = iphoneos;
470 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
471 | TARGETED_DEVICE_FAMILY = "1,2";
472 | VALIDATE_PRODUCT = YES;
473 | };
474 | name = Release;
475 | };
476 | 97C147061CF9000F007C117D /* Debug */ = {
477 | isa = XCBuildConfiguration;
478 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
479 | buildSettings = {
480 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
481 | CLANG_ENABLE_MODULES = YES;
482 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
483 | ENABLE_BITCODE = NO;
484 | INFOPLIST_FILE = Runner/Info.plist;
485 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
486 | PRODUCT_BUNDLE_IDENTIFIER = com.example.client;
487 | PRODUCT_NAME = "$(TARGET_NAME)";
488 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
489 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
490 | SWIFT_VERSION = 5.0;
491 | VERSIONING_SYSTEM = "apple-generic";
492 | };
493 | name = Debug;
494 | };
495 | 97C147071CF9000F007C117D /* Release */ = {
496 | isa = XCBuildConfiguration;
497 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
498 | buildSettings = {
499 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
500 | CLANG_ENABLE_MODULES = YES;
501 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
502 | ENABLE_BITCODE = NO;
503 | INFOPLIST_FILE = Runner/Info.plist;
504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
505 | PRODUCT_BUNDLE_IDENTIFIER = com.example.client;
506 | PRODUCT_NAME = "$(TARGET_NAME)";
507 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
508 | SWIFT_VERSION = 5.0;
509 | VERSIONING_SYSTEM = "apple-generic";
510 | };
511 | name = Release;
512 | };
513 | /* End XCBuildConfiguration section */
514 |
515 | /* Begin XCConfigurationList section */
516 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
517 | isa = XCConfigurationList;
518 | buildConfigurations = (
519 | 97C147031CF9000F007C117D /* Debug */,
520 | 97C147041CF9000F007C117D /* Release */,
521 | 249021D3217E4FDB00AE95B9 /* Profile */,
522 | );
523 | defaultConfigurationIsVisible = 0;
524 | defaultConfigurationName = Release;
525 | };
526 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
527 | isa = XCConfigurationList;
528 | buildConfigurations = (
529 | 97C147061CF9000F007C117D /* Debug */,
530 | 97C147071CF9000F007C117D /* Release */,
531 | 249021D4217E4FDB00AE95B9 /* Profile */,
532 | );
533 | defaultConfigurationIsVisible = 0;
534 | defaultConfigurationName = Release;
535 | };
536 | /* End XCConfigurationList section */
537 | };
538 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
539 | }
540 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/client/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/client/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: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/client/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.
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/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 | client
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/client/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/client/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:client/rest_client.dart';
2 | import 'package:client/screens/main_screen.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 |
6 | const appName = 'Image Search with Natural Language';
7 |
8 | void main() {
9 | runApp(MultiProvider(providers: [
10 | Provider(
11 | create: (context) => RestClient(),
12 | ),
13 | ], child: MainApp()));
14 | }
15 |
16 | class MainApp extends StatelessWidget {
17 | const MainApp({
18 | Key? key,
19 | }) : super(key: key);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return MaterialApp(
24 | title: appName,
25 | initialRoute: MainScreen.routeName,
26 | debugShowCheckedModeBanner: false,
27 | routes: {
28 | MainScreen.routeName: (context) => MainScreen(),
29 | },
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/lib/rest_client.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'package:http/http.dart' as http;
3 |
4 | class RestClient {
5 | List? parseSemantics(String responseBody) {
6 | final parsed = jsonDecode(responseBody)['top_urls'];
7 | List? urls = parsed != null ? List.from(parsed) : null;
8 | return urls;
9 | }
10 |
11 | Future> fetchSematicPhotos(
12 | http.Client client, String tag, String semanticQuery, int topK) async {
13 | tag = Uri.encodeComponent(tag);
14 | semanticQuery = Uri.encodeComponent(semanticQuery);
15 | final response = await client.get(Uri.parse(
16 | 'https://mlgde.com/search?t=$tag&s_query=$semanticQuery&k=${topK.toString()}'));
17 |
18 | print('$tag + $semanticQuery');
19 |
20 | List? urls = parseSemantics(response.body);
21 | if (urls != null) {
22 | return urls;
23 | }
24 | return [];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/client/lib/screens/main_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:client/rest_client.dart';
2 | import 'package:client/widgets/image_grid.dart';
3 | import 'package:http/http.dart' as http;
4 | import 'package:flutter/material.dart';
5 | // ignore: import_of_legacy_library_into_null_safe
6 | import 'package:flutter_linkify/flutter_linkify.dart';
7 | import 'package:url_launcher/url_launcher.dart';
8 |
9 | const appName = 'Semantic Image Search';
10 |
11 | // ignore: must_be_immutable
12 | class MainScreen extends StatefulWidget {
13 | static const String routeName = "main_screen";
14 |
15 | @override
16 | _MainScreenState createState() => _MainScreenState();
17 | }
18 |
19 | class _MainScreenState extends State {
20 | bool isInitialPhase = true;
21 | bool isFetching = false;
22 | RestClient client = RestClient();
23 | List topUrls = [];
24 | final tagController = TextEditingController();
25 | final queryController = TextEditingController();
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return Scaffold(
30 | appBar: AppBar(
31 | title: const Text(appName),
32 | actions: [
33 | IconButton(
34 | onPressed: () {
35 | showDialog(
36 | context: context,
37 | builder: (_) => SimpleDialog(
38 | children: [
39 | Row(
40 | children: [
41 | Expanded(
42 | child: Text(
43 | 'Image Search by Natural Language Query',
44 | textAlign: TextAlign.center,
45 | style: TextStyle(
46 | fontSize: 20,
47 | fontWeight: FontWeight.bold)))
48 | ],
49 | ),
50 | SizedBox(
51 | height: 20,
52 | ),
53 | Row(children: [
54 | Expanded(
55 | child: Container(
56 | padding:
57 | EdgeInsets.only(left: 10, right: 10),
58 | child: Text(
59 | "This project shows how to build an image search utility using natural language queries. Our aim is to use different and related serverless GCP services to demonstrate this. At the core of our project is OpenAI's CLIP model. It makes use of two encoders - one for images and one for texts. Each encoder is trained to learn representations such that similar images and text embeddings are projected as close as possible.",
60 | textAlign: TextAlign.start,
61 | ))),
62 | ]),
63 | SizedBox(
64 | height: 20,
65 | ),
66 | Row(
67 | children: [
68 | SizedBox(
69 | width: 10,
70 | ),
71 | Linkify(
72 | onOpen: (link) async {
73 | if (await canLaunch(link.url)) {
74 | await launch(link.url);
75 | } else {
76 | throw 'Could not launch $link';
77 | }
78 | },
79 | text: "Project Repo: https://git.io/JnxLk",
80 | style: TextStyle(
81 | color: Colors.blue,
82 | fontWeight: FontWeight.bold),
83 | linkStyle: TextStyle(
84 | color: Colors.amber,
85 | fontWeight: FontWeight.bold),
86 | ),
87 | ],
88 | ),
89 | SizedBox(
90 | height: 20,
91 | ),
92 | Row(
93 | children: [
94 | SizedBox(
95 | width: 10,
96 | ),
97 | CircleAvatar(
98 | backgroundColor: Colors.amber,
99 | radius: 30.0,
100 | child: CircleAvatar(
101 | radius: 28.0,
102 | backgroundImage:
103 | AssetImage('images/chansung.png'),
104 | )),
105 | SizedBox(
106 | width: 10,
107 | ),
108 | CircleAvatar(
109 | backgroundColor: Colors.amber,
110 | radius: 30.0,
111 | child: CircleAvatar(
112 | radius: 28.0,
113 | backgroundImage:
114 | AssetImage('images/paul.png'),
115 | )),
116 | Expanded(
117 | child: Column(children: [
118 | Text(
119 | 'Built by ML GDEs',
120 | textAlign: TextAlign.center,
121 | style: TextStyle(
122 | fontSize: 18,
123 | fontWeight: FontWeight.bold),
124 | ),
125 | SizedBox(
126 | height: 5,
127 | ),
128 | Text('Chansung Park',
129 | style: TextStyle(fontSize: 12)),
130 | Text('Paul Sayak',
131 | style: TextStyle(fontSize: 12)),
132 | ]))
133 | ],
134 | ),
135 | ],
136 | ));
137 | },
138 | icon: Icon(Icons.info))
139 | ],
140 | ),
141 | body: Container(
142 | padding: EdgeInsets.all(20),
143 | child: Column(
144 | children: [
145 | TextFormField(
146 | controller: tagController,
147 | decoration: InputDecoration(
148 | border: OutlineInputBorder(),
149 | labelText: 'Enter keywords to search Pixabay'),
150 | ),
151 | SizedBox(
152 | height: 10,
153 | ),
154 | TextFormField(
155 | controller: queryController,
156 | decoration: InputDecoration(
157 | border: OutlineInputBorder(),
158 | labelText: 'Enter Semantic Search Query'),
159 | // maxLines: 3,
160 | keyboardType: TextInputType.multiline,
161 | ),
162 | SizedBox(
163 | height: 10,
164 | ),
165 | ElevatedButton(
166 | style: ElevatedButton.styleFrom(
167 | padding: EdgeInsets.symmetric(horizontal: 50, vertical: 10),
168 | shape: RoundedRectangleBorder(
169 | borderRadius: BorderRadius.circular(20))),
170 | child: Text(
171 | 'Send Query',
172 | style: TextStyle(fontSize: 24),
173 | ),
174 | onPressed: isFetching
175 | ? null
176 | : () async {
177 | FocusScope.of(context).unfocus();
178 | setState(() {
179 | isInitialPhase = false;
180 | topUrls = [];
181 | isFetching = true;
182 | });
183 |
184 | if (tagController.text != "" ||
185 | queryController.text != "") {
186 | final urls = await client.fetchSematicPhotos(
187 | http.Client(),
188 | tagController.text,
189 | queryController.text,
190 | 10);
191 | setState(() {
192 | topUrls = urls;
193 | isFetching = false;
194 | });
195 | }
196 | }),
197 | SizedBox(
198 | height: 20,
199 | ),
200 | getImageGridWidget(),
201 | ],
202 | ),
203 | ),
204 | );
205 | }
206 |
207 | Widget getImageGridWidget() {
208 | return new FutureBuilder(builder: (context, snapshot) {
209 | if (isInitialPhase) {
210 | return Container();
211 | }
212 |
213 | if (topUrls.isNotEmpty) {
214 | return ImageGridWidget(topUrls: topUrls);
215 | }
216 |
217 | return const Center(child: CircularProgressIndicator());
218 | });
219 | }
220 |
221 | Widget getItemCard(String url) {
222 | return Container(
223 | padding: EdgeInsets.only(left: 5, right: 5), child: Image.network(url));
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/client/lib/widgets/image_grid.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ImageGridWidget extends StatelessWidget {
4 | final List topUrls;
5 |
6 | const ImageGridWidget({Key? key, required this.topUrls}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Expanded(
11 | child: GridView.count(
12 | crossAxisCount: 2,
13 | children: List.generate(
14 | topUrls.length, (index) => getItemCard(context, topUrls[index]))),
15 | );
16 | }
17 |
18 | Widget getItemCard(BuildContext context, String url) {
19 | return GestureDetector(
20 | onTap: () {
21 | FocusScope.of(context).unfocus();
22 | showDialog(
23 | context: context,
24 | builder: (_) => SimpleDialog(
25 | children: [Image.network(url)],
26 | ));
27 | },
28 | child: Container(
29 | padding: EdgeInsets.only(left: 5, right: 5),
30 | child: Image.network(url)));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | _fe_analyzer_shared:
5 | dependency: transitive
6 | description:
7 | name: _fe_analyzer_shared
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "22.0.0"
11 | analyzer:
12 | dependency: transitive
13 | description:
14 | name: analyzer
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.7.1"
18 | archive:
19 | dependency: transitive
20 | description:
21 | name: archive
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "3.1.2"
25 | args:
26 | dependency: transitive
27 | description:
28 | name: args
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "2.1.1"
32 | async:
33 | dependency: transitive
34 | description:
35 | name: async
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.6.1"
39 | boolean_selector:
40 | dependency: transitive
41 | description:
42 | name: boolean_selector
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "2.1.0"
46 | build:
47 | dependency: transitive
48 | description:
49 | name: build
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "2.0.2"
53 | build_config:
54 | dependency: transitive
55 | description:
56 | name: build_config
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.0.0"
60 | build_daemon:
61 | dependency: transitive
62 | description:
63 | name: build_daemon
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "3.0.0"
67 | build_resolvers:
68 | dependency: transitive
69 | description:
70 | name: build_resolvers
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "2.0.3"
74 | build_runner:
75 | dependency: "direct dev"
76 | description:
77 | name: build_runner
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "2.0.4"
81 | build_runner_core:
82 | dependency: transitive
83 | description:
84 | name: build_runner_core
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "7.0.0"
88 | built_collection:
89 | dependency: transitive
90 | description:
91 | name: built_collection
92 | url: "https://pub.dartlang.org"
93 | source: hosted
94 | version: "5.1.0"
95 | built_value:
96 | dependency: transitive
97 | description:
98 | name: built_value
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "8.1.0"
102 | characters:
103 | dependency: transitive
104 | description:
105 | name: characters
106 | url: "https://pub.dartlang.org"
107 | source: hosted
108 | version: "1.1.0"
109 | charcode:
110 | dependency: transitive
111 | description:
112 | name: charcode
113 | url: "https://pub.dartlang.org"
114 | source: hosted
115 | version: "1.2.0"
116 | checked_yaml:
117 | dependency: transitive
118 | description:
119 | name: checked_yaml
120 | url: "https://pub.dartlang.org"
121 | source: hosted
122 | version: "2.0.1"
123 | cli_util:
124 | dependency: transitive
125 | description:
126 | name: cli_util
127 | url: "https://pub.dartlang.org"
128 | source: hosted
129 | version: "0.3.0"
130 | clock:
131 | dependency: transitive
132 | description:
133 | name: clock
134 | url: "https://pub.dartlang.org"
135 | source: hosted
136 | version: "1.1.0"
137 | code_builder:
138 | dependency: transitive
139 | description:
140 | name: code_builder
141 | url: "https://pub.dartlang.org"
142 | source: hosted
143 | version: "4.0.0"
144 | collection:
145 | dependency: transitive
146 | description:
147 | name: collection
148 | url: "https://pub.dartlang.org"
149 | source: hosted
150 | version: "1.15.0"
151 | convert:
152 | dependency: transitive
153 | description:
154 | name: convert
155 | url: "https://pub.dartlang.org"
156 | source: hosted
157 | version: "3.0.0"
158 | crypto:
159 | dependency: transitive
160 | description:
161 | name: crypto
162 | url: "https://pub.dartlang.org"
163 | source: hosted
164 | version: "3.0.1"
165 | cupertino_icons:
166 | dependency: "direct main"
167 | description:
168 | name: cupertino_icons
169 | url: "https://pub.dartlang.org"
170 | source: hosted
171 | version: "1.0.3"
172 | dart_style:
173 | dependency: transitive
174 | description:
175 | name: dart_style
176 | url: "https://pub.dartlang.org"
177 | source: hosted
178 | version: "2.0.1"
179 | fake_async:
180 | dependency: transitive
181 | description:
182 | name: fake_async
183 | url: "https://pub.dartlang.org"
184 | source: hosted
185 | version: "1.2.0"
186 | file:
187 | dependency: transitive
188 | description:
189 | name: file
190 | url: "https://pub.dartlang.org"
191 | source: hosted
192 | version: "6.1.0"
193 | fixnum:
194 | dependency: transitive
195 | description:
196 | name: fixnum
197 | url: "https://pub.dartlang.org"
198 | source: hosted
199 | version: "1.0.0"
200 | flutter:
201 | dependency: "direct main"
202 | description: flutter
203 | source: sdk
204 | version: "0.0.0"
205 | flutter_driver:
206 | dependency: "direct dev"
207 | description: flutter
208 | source: sdk
209 | version: "0.0.0"
210 | flutter_linkify:
211 | dependency: "direct main"
212 | description:
213 | name: flutter_linkify
214 | url: "https://pub.dartlang.org"
215 | source: hosted
216 | version: "5.0.2"
217 | flutter_test:
218 | dependency: "direct dev"
219 | description: flutter
220 | source: sdk
221 | version: "0.0.0"
222 | flutter_web_plugins:
223 | dependency: transitive
224 | description: flutter
225 | source: sdk
226 | version: "0.0.0"
227 | frontend_server_client:
228 | dependency: transitive
229 | description:
230 | name: frontend_server_client
231 | url: "https://pub.dartlang.org"
232 | source: hosted
233 | version: "2.1.0"
234 | fuchsia_remote_debug_protocol:
235 | dependency: transitive
236 | description: flutter
237 | source: sdk
238 | version: "0.0.0"
239 | glob:
240 | dependency: transitive
241 | description:
242 | name: glob
243 | url: "https://pub.dartlang.org"
244 | source: hosted
245 | version: "2.0.1"
246 | graphs:
247 | dependency: transitive
248 | description:
249 | name: graphs
250 | url: "https://pub.dartlang.org"
251 | source: hosted
252 | version: "2.0.0"
253 | http:
254 | dependency: "direct main"
255 | description:
256 | name: http
257 | url: "https://pub.dartlang.org"
258 | source: hosted
259 | version: "0.13.3"
260 | http_multi_server:
261 | dependency: transitive
262 | description:
263 | name: http_multi_server
264 | url: "https://pub.dartlang.org"
265 | source: hosted
266 | version: "3.0.1"
267 | http_parser:
268 | dependency: transitive
269 | description:
270 | name: http_parser
271 | url: "https://pub.dartlang.org"
272 | source: hosted
273 | version: "4.0.0"
274 | io:
275 | dependency: transitive
276 | description:
277 | name: io
278 | url: "https://pub.dartlang.org"
279 | source: hosted
280 | version: "1.0.0"
281 | js:
282 | dependency: transitive
283 | description:
284 | name: js
285 | url: "https://pub.dartlang.org"
286 | source: hosted
287 | version: "0.6.3"
288 | json_annotation:
289 | dependency: transitive
290 | description:
291 | name: json_annotation
292 | url: "https://pub.dartlang.org"
293 | source: hosted
294 | version: "4.0.1"
295 | linkify:
296 | dependency: transitive
297 | description:
298 | name: linkify
299 | url: "https://pub.dartlang.org"
300 | source: hosted
301 | version: "4.0.0"
302 | logging:
303 | dependency: transitive
304 | description:
305 | name: logging
306 | url: "https://pub.dartlang.org"
307 | source: hosted
308 | version: "1.0.1"
309 | matcher:
310 | dependency: transitive
311 | description:
312 | name: matcher
313 | url: "https://pub.dartlang.org"
314 | source: hosted
315 | version: "0.12.10"
316 | meta:
317 | dependency: transitive
318 | description:
319 | name: meta
320 | url: "https://pub.dartlang.org"
321 | source: hosted
322 | version: "1.3.0"
323 | mime:
324 | dependency: transitive
325 | description:
326 | name: mime
327 | url: "https://pub.dartlang.org"
328 | source: hosted
329 | version: "1.0.0"
330 | nested:
331 | dependency: transitive
332 | description:
333 | name: nested
334 | url: "https://pub.dartlang.org"
335 | source: hosted
336 | version: "1.0.0"
337 | package_config:
338 | dependency: transitive
339 | description:
340 | name: package_config
341 | url: "https://pub.dartlang.org"
342 | source: hosted
343 | version: "2.0.0"
344 | path:
345 | dependency: transitive
346 | description:
347 | name: path
348 | url: "https://pub.dartlang.org"
349 | source: hosted
350 | version: "1.8.0"
351 | pedantic:
352 | dependency: transitive
353 | description:
354 | name: pedantic
355 | url: "https://pub.dartlang.org"
356 | source: hosted
357 | version: "1.11.1"
358 | platform:
359 | dependency: transitive
360 | description:
361 | name: platform
362 | url: "https://pub.dartlang.org"
363 | source: hosted
364 | version: "3.0.0"
365 | plugin_platform_interface:
366 | dependency: transitive
367 | description:
368 | name: plugin_platform_interface
369 | url: "https://pub.dartlang.org"
370 | source: hosted
371 | version: "2.0.0"
372 | pool:
373 | dependency: transitive
374 | description:
375 | name: pool
376 | url: "https://pub.dartlang.org"
377 | source: hosted
378 | version: "1.5.0"
379 | process:
380 | dependency: transitive
381 | description:
382 | name: process
383 | url: "https://pub.dartlang.org"
384 | source: hosted
385 | version: "4.2.1"
386 | provider:
387 | dependency: "direct main"
388 | description:
389 | name: provider
390 | url: "https://pub.dartlang.org"
391 | source: hosted
392 | version: "5.0.0"
393 | pub_semver:
394 | dependency: transitive
395 | description:
396 | name: pub_semver
397 | url: "https://pub.dartlang.org"
398 | source: hosted
399 | version: "2.0.0"
400 | pubspec_parse:
401 | dependency: transitive
402 | description:
403 | name: pubspec_parse
404 | url: "https://pub.dartlang.org"
405 | source: hosted
406 | version: "1.0.0"
407 | shelf:
408 | dependency: transitive
409 | description:
410 | name: shelf
411 | url: "https://pub.dartlang.org"
412 | source: hosted
413 | version: "1.1.4"
414 | shelf_web_socket:
415 | dependency: transitive
416 | description:
417 | name: shelf_web_socket
418 | url: "https://pub.dartlang.org"
419 | source: hosted
420 | version: "1.0.1"
421 | sky_engine:
422 | dependency: transitive
423 | description: flutter
424 | source: sdk
425 | version: "0.0.99"
426 | source_span:
427 | dependency: transitive
428 | description:
429 | name: source_span
430 | url: "https://pub.dartlang.org"
431 | source: hosted
432 | version: "1.8.1"
433 | stack_trace:
434 | dependency: transitive
435 | description:
436 | name: stack_trace
437 | url: "https://pub.dartlang.org"
438 | source: hosted
439 | version: "1.10.0"
440 | stream_channel:
441 | dependency: transitive
442 | description:
443 | name: stream_channel
444 | url: "https://pub.dartlang.org"
445 | source: hosted
446 | version: "2.1.0"
447 | stream_transform:
448 | dependency: transitive
449 | description:
450 | name: stream_transform
451 | url: "https://pub.dartlang.org"
452 | source: hosted
453 | version: "2.0.0"
454 | string_scanner:
455 | dependency: transitive
456 | description:
457 | name: string_scanner
458 | url: "https://pub.dartlang.org"
459 | source: hosted
460 | version: "1.1.0"
461 | sync_http:
462 | dependency: transitive
463 | description:
464 | name: sync_http
465 | url: "https://pub.dartlang.org"
466 | source: hosted
467 | version: "0.3.0"
468 | term_glyph:
469 | dependency: transitive
470 | description:
471 | name: term_glyph
472 | url: "https://pub.dartlang.org"
473 | source: hosted
474 | version: "1.2.0"
475 | test_api:
476 | dependency: transitive
477 | description:
478 | name: test_api
479 | url: "https://pub.dartlang.org"
480 | source: hosted
481 | version: "0.3.0"
482 | timing:
483 | dependency: transitive
484 | description:
485 | name: timing
486 | url: "https://pub.dartlang.org"
487 | source: hosted
488 | version: "1.0.0"
489 | typed_data:
490 | dependency: transitive
491 | description:
492 | name: typed_data
493 | url: "https://pub.dartlang.org"
494 | source: hosted
495 | version: "1.3.0"
496 | url_launcher:
497 | dependency: "direct main"
498 | description:
499 | name: url_launcher
500 | url: "https://pub.dartlang.org"
501 | source: hosted
502 | version: "6.0.7"
503 | url_launcher_linux:
504 | dependency: transitive
505 | description:
506 | name: url_launcher_linux
507 | url: "https://pub.dartlang.org"
508 | source: hosted
509 | version: "2.0.0"
510 | url_launcher_macos:
511 | dependency: transitive
512 | description:
513 | name: url_launcher_macos
514 | url: "https://pub.dartlang.org"
515 | source: hosted
516 | version: "2.0.0"
517 | url_launcher_platform_interface:
518 | dependency: transitive
519 | description:
520 | name: url_launcher_platform_interface
521 | url: "https://pub.dartlang.org"
522 | source: hosted
523 | version: "2.0.3"
524 | url_launcher_web:
525 | dependency: transitive
526 | description:
527 | name: url_launcher_web
528 | url: "https://pub.dartlang.org"
529 | source: hosted
530 | version: "2.0.1"
531 | url_launcher_windows:
532 | dependency: transitive
533 | description:
534 | name: url_launcher_windows
535 | url: "https://pub.dartlang.org"
536 | source: hosted
537 | version: "2.0.0"
538 | vector_math:
539 | dependency: transitive
540 | description:
541 | name: vector_math
542 | url: "https://pub.dartlang.org"
543 | source: hosted
544 | version: "2.1.0"
545 | vm_service:
546 | dependency: transitive
547 | description:
548 | name: vm_service
549 | url: "https://pub.dartlang.org"
550 | source: hosted
551 | version: "6.2.0"
552 | watcher:
553 | dependency: transitive
554 | description:
555 | name: watcher
556 | url: "https://pub.dartlang.org"
557 | source: hosted
558 | version: "1.0.0"
559 | web_socket_channel:
560 | dependency: transitive
561 | description:
562 | name: web_socket_channel
563 | url: "https://pub.dartlang.org"
564 | source: hosted
565 | version: "2.1.0"
566 | webdriver:
567 | dependency: transitive
568 | description:
569 | name: webdriver
570 | url: "https://pub.dartlang.org"
571 | source: hosted
572 | version: "3.0.0"
573 | yaml:
574 | dependency: transitive
575 | description:
576 | name: yaml
577 | url: "https://pub.dartlang.org"
578 | source: hosted
579 | version: "3.1.0"
580 | sdks:
581 | dart: ">=2.12.0 <3.0.0"
582 | flutter: ">=2.0.0"
583 |
--------------------------------------------------------------------------------
/client/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: client
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 | http: ^0.13.3
27 | provider: ^5.0.0
28 | flutter_linkify: ^5.0.2
29 | url_launcher: ^6.0.7
30 |
31 | # The following adds the Cupertino Icons font to your application.
32 | # Use with the CupertinoIcons class for iOS style icons.
33 | cupertino_icons: ^1.0.2
34 |
35 | dev_dependencies:
36 | build_runner: ^2.0.4
37 | flutter_driver:
38 | sdk: flutter
39 | flutter_test:
40 | sdk: flutter
41 |
42 | # For information on the generic Dart part of this file, see the
43 | # following page: https://dart.dev/tools/pub/pubspec
44 |
45 | # The following section is specific to Flutter.
46 | flutter:
47 |
48 | # The following line ensures that the Material Icons font is
49 | # included with your application, so that you can use the icons in
50 | # the material Icons class.
51 | uses-material-design: true
52 |
53 | # To add assets to your application, add an assets section, like this:
54 | assets:
55 | - images/gde_logo.png
56 | - images/paul.png
57 | - images/chansung.png
58 |
59 | # An image asset can refer to one or more resolution-specific "variants", see
60 | # https://flutter.dev/assets-and-images/#resolution-aware.
61 |
62 | # For details regarding adding assets from package dependencies, see
63 | # https://flutter.dev/assets-and-images/#from-packages
64 |
65 | # To add custom fonts to your application, add a fonts section here,
66 | # in this "flutter" section. Each entry in this list should have a
67 | # "family" key with the font family name, and a "fonts" key with a
68 | # list giving the asset and other descriptors for the font. For
69 | # example:
70 | # fonts:
71 | # - family: Schyler
72 | # fonts:
73 | # - asset: fonts/Schyler-Regular.ttf
74 | # - asset: fonts/Schyler-Italic.ttf
75 | # style: italic
76 | # - family: Trajan Pro
77 | # fonts:
78 | # - asset: fonts/TrajanPro.ttf
79 | # - asset: fonts/TrajanPro_Bold.ttf
80 | # weight: 700
81 | #
82 | # For details regarding fonts from package dependencies,
83 | # see https://flutter.dev/custom-fonts/#from-packages
84 |
--------------------------------------------------------------------------------
/client/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:client/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(MainApp());
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 |
--------------------------------------------------------------------------------
/client/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/web/favicon.png
--------------------------------------------------------------------------------
/client/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/client/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/client/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/client/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | client
27 |
28 |
29 |
30 |
33 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/client/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "short_name": "client",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/gke/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: clip-search
6 | name: clip-search
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: clip-search
12 | template:
13 | metadata:
14 | labels:
15 | app: clip-search
16 | spec:
17 | containers:
18 | # image registry URI should be replaced once GitHub Action is set up
19 | - image: gcr.io/titanium-vision-318200/search_service
20 | name: search-service-p4sml
21 | imagePullPolicy: Always
22 | ports:
23 | - containerPort: 8080
24 | protocol: TCP
25 | livenessProbe:
26 | httpGet:
27 | path: /alive
28 | port: 8080
29 | initialDelaySeconds: 90
30 | periodSeconds: 10
31 | readinessProbe:
32 | httpGet:
33 | path: /ready
34 | port: 8080
35 | initialDelaySeconds: 90
36 | periodSeconds: 10
37 | resources:
38 | limits:
39 | cpu: 1500m
40 | memory: 4G
41 | ---
42 | apiVersion: autoscaling/v1
43 | kind: HorizontalPodAutoscaler
44 | metadata:
45 | name: clip-search-hpa
46 |
47 | spec:
48 | scaleTargetRef:
49 | apiVersion: apps/v1
50 | kind: Deployment
51 | name: clip-search
52 | minReplicas: 1
53 | maxReplicas: 3
54 | targetCPUUtilizationPercentage: 80
55 |
--------------------------------------------------------------------------------
/gke/gke-managed-certificate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.gke.io/v1
2 | kind: ManagedCertificate
3 | metadata:
4 | name: gde
5 | spec:
6 | domains:
7 | # should be replaced once GitHub Action is set up
8 | - mlgde.com
--------------------------------------------------------------------------------
/gke/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: gde
5 | annotations:
6 | kubernetes.io/ingress.global-static-ip-name: gde
7 | networking.gke.io/managed-certificates: gde
8 | kubernetes.io/ingress.class: "gce"
9 | spec:
10 | rules:
11 | - http:
12 | paths:
13 | - path: /*
14 | pathType: ImplementationSpecific
15 | backend:
16 | service:
17 | name: gde
18 | port:
19 | number: 80
--------------------------------------------------------------------------------
/gke/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: gde
5 | spec:
6 | selector:
7 | app: clip-search
8 | type: ClusterIP
9 | ports:
10 | - protocol: TCP
11 | port: 80
12 | targetPort: 8080
13 |
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM pytorch/pytorch:latest
2 | WORKDIR /app
3 | COPY requirements.txt requirements.txt
4 | RUN pip install -r requirements.txt
5 | COPY . .
6 | # this information should be replaced once GitHub Action is set up
7 | ENV PIXABAY_API_KEY="22176616-358d1b190a298ff59f96b35a1"
8 | ENV REDIS_IN_USE="true"
9 | ENV REDISHOST="10.195.86.155"
10 | EXPOSE 8080
11 | CMD [ "python3", "main.py" ]
12 |
--------------------------------------------------------------------------------
/server/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deep-diver/image_search_with_natural_language/7620faa796a0df7061eab09ecb0207468a999f60/server/__init__.py
--------------------------------------------------------------------------------
/server/k8s_deploy.sh:
--------------------------------------------------------------------------------
1 | ## Docker build and push ##
2 | # We are inside the `server` directory
3 | docker build -t gcr.io/${PROJECT_ID}/search_service .
4 | docker push gcr.io/${PROJECT_ID}/search_service
5 |
6 | ## Deploy on a GKE cluster ##
7 | # Configure the Docker command-line tool to authenticate to Container Registry
8 | gcloud auth configure-docker
9 |
10 | # Create a Kubernetes cluster
11 | gcloud container clusters create image-search-nlp
12 |
13 | # Create a deployment
14 | kubectl create deployment clip-search --image=gcr.io/${PROJECT_ID}/search_service
15 |
16 | # Number of worker replicas
17 | kubectl scale deployment clip-search --replicas=3
18 |
19 | # HorizontalPodAutoscaler resource
20 | kubectl autoscale deployment clip-search --cpu-percent=80 --min=1 --max=5
21 |
22 | # Expose deployment
23 | kubectl expose deployment clip-search --name=clip-search-service --type=LoadBalancer --port 80 --target-port 8080
24 |
--------------------------------------------------------------------------------
/server/main.py:
--------------------------------------------------------------------------------
1 | from perform_search import Searcher
2 | from flask import Flask, jsonify, request
3 | from flask_cors import CORS
4 | from flask_healthz import healthz
5 | from flask_healthz import HealthError
6 |
7 | app = Flask(__name__)
8 | app.register_blueprint(healthz, url_prefix="/")
9 |
10 | cors = CORS(
11 | app,
12 | resources={
13 | r"/search/*": {"origin": "*"},
14 | r"/test/*": {"origin": "*"},
15 | },
16 | )
17 |
18 | def printok():
19 | print("Everything is fine")
20 |
21 | def liveness():
22 | try:
23 | printok()
24 | except Exception:
25 | raise HealthError("Can't connect to the file")
26 |
27 | def readiness():
28 | try:
29 | printok()
30 | except Exception:
31 | raise HealthError("Can't connect to the file")
32 |
33 | app.config.update(
34 | HEALTHZ = {
35 | "alive": "main.liveness",
36 | "ready": "main.readiness",
37 | }
38 | )
39 |
40 | searcher = Searcher()
41 |
42 | MAX_PIXABAY_SEARCH = 30
43 |
44 | @app.route("/search", methods=["GET"])
45 | def get_images():
46 | tag = request.args.get("t").lower()
47 | query = request.args.get("s_query").lower()
48 | top_k = request.args.get("k")
49 |
50 | (top_urls, top_scores) = searcher.get_similar_images(
51 | tag, query, MAX_PIXABAY_SEARCH, int(top_k)
52 | )
53 |
54 | return jsonify({"top_urls": top_urls, "top_scores": top_scores})
55 |
56 |
57 | @app.route("/test", methods=["GET"])
58 | def test():
59 | return jsonify({"result": "good to go"})
60 |
61 |
62 | if __name__ == "__main__":
63 | app.run(host='0.0.0.0', port=8080, debug=True)
64 |
--------------------------------------------------------------------------------
/server/perform_search.py:
--------------------------------------------------------------------------------
1 | import os
2 | from similarity_utils import SimilarityUtil
3 | from pixabay_utils import fetch_images_tag
4 | from direct_redis import DirectRedis
5 |
6 | redis = os.environ.get("REDIS_IN_USE", "false").lower()
7 | redis_host = os.environ.get("REDISHOST", "localhost")
8 | redis_port = int(os.environ.get("REDISPORT", 6379))
9 | redis_client = DirectRedis(host=redis_host, port=redis_port)
10 |
11 |
12 | class Searcher:
13 | def __init__(self):
14 | self.similarity_model = SimilarityUtil()
15 |
16 | def get_similar_images(self, keyword, semantic_query, pixabay_max, top_k):
17 | """
18 | Finds semantically similar images.
19 | :param keyword: Keyword to search with on Pixabay.
20 | :param semantic_query: Query to find semantically similar images retrieved from Pixabay.
21 | :param pixabay_max: Number of maximum images to retrieve from Pixabay.
22 | :param top_k: Top-k images to return.
23 | :return: Tuple of top_k URLs and the similarity scores of the images present inside the URLs.
24 | """
25 |
26 | if redis == "true":
27 | images_redis_key = keyword + "_images"
28 | urls_redis_key = keyword + "_urls"
29 |
30 | if redis_client.exists(images_redis_key) and redis_client.exists(
31 | urls_redis_key
32 | ):
33 | keyword_images = redis_client.get(images_redis_key)
34 | keyword_image_urls = redis_client.get(urls_redis_key)
35 | else:
36 | (keyword_images, keyword_image_urls) = fetch_images_tag(
37 | keyword, pixabay_max
38 | )
39 | redis_client.set(images_redis_key, keyword_images)
40 | redis_client.set(urls_redis_key, keyword_image_urls)
41 | else:
42 | (keyword_images, keyword_image_urls) = fetch_images_tag(keyword, pixabay_max)
43 |
44 | (top_indices, top_scores) = self.similarity_model.perform_sim_search(
45 | keyword_images, semantic_query, top_k
46 | )
47 |
48 | top_urls = [keyword_image_urls[index] for index in top_indices]
49 |
50 | return (top_urls, top_scores)
51 |
--------------------------------------------------------------------------------
/server/pixabay_utils.py:
--------------------------------------------------------------------------------
1 | """
2 | References:
3 | * https://towardsdatascience.com/beyond-tags-and-entering-the-semantic-search-era-on-images-with-openai-clip-1f7d629a9978
4 | """
5 |
6 | from io import BytesIO
7 | from PIL import Image
8 | import requests
9 | import logging
10 | import time
11 | import os
12 |
13 | ORIGINAL_API = "https://pixabay.com/api/?key="
14 | API_KEY = os.environ["PIXABAY_API_KEY"]
15 | PIXABAY_API = ORIGINAL_API + API_KEY
16 |
17 |
18 | def fetch_images_tag(pixabay_search_keyword, num_images):
19 | """
20 | Fetches images from Pixabay w.r.t a keyword.
21 | :param pixabay_search_keyword: Keyword to perform the search on Pixabay.
22 | :param num_images: Number of images to retrieve.
23 | :return: List of PIL images.
24 | :return: List of image URLs.
25 | """
26 | query = (
27 | PIXABAY_API
28 | + "&q="
29 | + pixabay_search_keyword.lower()
30 | + "&image_type=photo&safesearch=true&per_page="
31 | + str(num_images)
32 | )
33 | logging.info(
34 | f"Making request to Pixabay for {num_images} images to fetch with {pixabay_search_keyword} keyword."
35 | )
36 |
37 | start_time = time.time()
38 | response = requests.get(query)
39 | end_time = time.time() - start_time
40 | logging.info(f"Fetched search results in {end_time:.3f} seconds.")
41 | output = response.json()
42 |
43 | all_images = []
44 | all_image_urls = []
45 | start_time = time.time()
46 | for each in output["hits"]:
47 | imageurl = each["webformatURL"]
48 | response = requests.get(imageurl)
49 | image = Image.open(BytesIO(response.content)).convert("RGB")
50 | all_images.append(image)
51 | all_image_urls.append(imageurl)
52 |
53 | end_time = time.time() - start_time
54 | logging.info(f"Fetched individual results in {end_time:.3f} " f"seconds.")
55 |
56 | return (all_images, all_image_urls)
57 |
--------------------------------------------------------------------------------
/server/requirements.txt:
--------------------------------------------------------------------------------
1 | regex
2 | transformers
3 | ftfy
4 | torch
5 | torchvision
6 | numpy
7 | flask
8 | gunicorn==19.3.0
9 | flask-cors
10 | direct-redis
11 | flask-healthz
--------------------------------------------------------------------------------
/server/similarity_utils.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import time
3 |
4 | from transformers import CLIPProcessor, CLIPModel
5 | import torch
6 |
7 | CLIP_MODEL = "openai/clip-vit-base-patch32"
8 | CLIP_PREPROCESSOR = "openai/clip-vit-base-patch32"
9 |
10 |
11 | class SimilarityUtil:
12 | def __init__(self):
13 | self.model = CLIPModel.from_pretrained(CLIP_MODEL)
14 | self.processor = CLIPProcessor.from_pretrained(CLIP_PREPROCESSOR)
15 | self.device = "cuda" if torch.cuda.is_available() else "cpu"
16 | logging.info("Model and preprocessor loaded.")
17 |
18 | def perform_sim_search(self, images, query_phrase, top_k=3):
19 | """
20 | Performs similarity search between the images and query.
21 | :param images: A list of PIL images initially retrieved with
22 | respect to some entity e.g. Tiger.
23 | :param query_phrase: A list containing a single text query,
24 | e.g. "Tiger drinking water".
25 | :param top_k: Number of top images to return from `images`.
26 | :return: Top-k indices matching the query semantically and
27 | their similarity scores.
28 | """
29 | model = self.model.to(self.device)
30 | # Obtain the text-image similarity scores
31 | with torch.no_grad():
32 | inputs = self.processor(
33 | text=[query_phrase], images=images, return_tensors="pt", padding=True
34 | )
35 | inputs = inputs.to(self.device)
36 | start_time = time.time()
37 | outputs = model(**inputs)
38 |
39 | end_time = time.time() - start_time
40 | logging.info(f"Similarity search completed in {end_time:.3f} seconds.")
41 |
42 | # Image-text similarity scores
43 | logits_per_image = outputs.logits_per_image.cpu()
44 |
45 | (top_indices, top_scores) = self.sort_scores(logits_per_image, top_k)
46 |
47 | return (top_indices, top_scores)
48 |
49 | def sort_scores(self, scores, top_k):
50 | """
51 | Sorts the scores in a descending manner.
52 | :param scores: Scores to sort through.
53 | :param top_k: Number of top scores to return.
54 | :return: Top-k scores and their indices.
55 | """
56 | values, indices = scores.squeeze().topk(top_k)
57 | top_indices, top_scores = [], []
58 |
59 | for score, index in zip(values, indices):
60 | top_indices.append(int(index.numpy()))
61 | score = score.numpy().tolist()
62 | top_scores.append(round(score, 3))
63 |
64 | return (top_indices, top_scores)
65 |
--------------------------------------------------------------------------------