├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── lib ├── caching_directory.dart ├── caching_network_image.dart ├── caching_search_engine.dart ├── cse-results.dart └── main.dart ├── macos ├── .gitignore ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── MainMenu.xib ├── Configs │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig ├── ExampleWindow.swift ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Info.plist ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme └── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── pubspec.lock ├── pubspec.yaml ├── readme └── demo.gif └── windows ├── .gitignore ├── Runner.sln ├── Runner.vcxproj ├── Runner.vcxproj.filters ├── flutter_embedder_example.cpp └── scripts ├── bundle_assets_and_deps.bat └── prepare_dependencies.bat /.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 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 73 | 74 | # never check these in! 75 | cse-key.txt 76 | cse-engine-id.txt 77 | 78 | # other misc 79 | /cse-cache 80 | /image-cache 81 | macos/Flutter/ 82 | image-cache/ 83 | ios/Flutter/ 84 | -------------------------------------------------------------------------------- /.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: 841707365a9be08f2219cbafc52c52d6af5355aa 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.background": "#043609", 4 | "titleBar.activeBackground": "#054C0C", 5 | "titleBar.activeForeground": "#EFFEF0" 6 | } 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Chris Sells. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_image_search 2 | An image search app using Google's Custom Search Engine, caching and debouncing. 3 | 4 | ## Getting Started 5 | This currently only works on desktop for Windows and Mac as the file handling is simple; could be updated to support mobile, too, with a little effort. Not quite sure how to think about making it work on the web... 6 | 7 | For this sample to work, you need a file called cse-engine-id.txt and cse-key.txt in the root folder. You can 8 | get those values by setting up a [Google Custom Search Engine project](https://stackoverflow.com/a/34062436). The contents of the cse-engine-id.txt file should be the cx parameter of an API query and the contents of the cse-key.txt file should be the key parameter. 9 | 10 | With these files in place, you can run the app like this: 11 | 12 | ```shell 13 | $ flutter run -d macos 14 | $ flutter run -d windows 15 | ``` 16 | 17 | And expect results like this: 18 | 19 | 20 | 21 | ## Implementation Details 22 | The debouncing is implemented with a little helper class. 23 | 24 | The caching is implemented with another little helper class and then shared between the CSE helper and the CachingNetworkImage so that both CSE search results (limited to 100 free/day) and the image downloads are cached. 25 | 26 | Selection is implemented with a RawMaterialButton, since it handles the clicking and the outlining. 27 | 28 | ## Room for improvement 29 | Right now, the URL is checked for known image extensions to avoid attempting to show anything that isn't a known image type, but really the file should be downloaded and the MIME type checked. 30 | 31 | Also, the file handling could easily be fixed to support mobile; PRs gratefully accepted! 32 | 33 | Further, there is no cache clearing policy -- it just grows forever! This could certainly be improved. 34 | 35 | Finally, I didn't implementing paging, which the CSE API supports. Instead I just show the first 10 results, which I consider good enough for demo purposes. 36 | 37 | ## Enjoy! -------------------------------------------------------------------------------- /lib/caching_directory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:path/path.dart' as path; 4 | 5 | class CachingDirectory { 6 | final Directory cacheDir; 7 | final String cacheExt; 8 | CachingDirectory(String dir, {this.cacheExt}) : cacheDir = Directory(dir); 9 | 10 | Future getCacheFile(String key) async { 11 | await cacheDir.create(); 12 | var ext = cacheExt; 13 | if (ext == null) { 14 | // attempt to pull extension off the key (if it's a file or URL) 15 | var pathPart = key.split('?')[0]; // file path or url path w/o query string portion 16 | ext = path.extension(pathPart); 17 | } 18 | 19 | var file = File(path.join(cacheDir.path, '${key.hashCode}$ext')); 20 | if (await file.exists()) { 21 | debugPrint('"$key" found in ${file.path}'); 22 | } else { 23 | debugPrint('"$key" NOT FOUND in ${file.path}'); 24 | } 25 | return file; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/caching_network_image.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_image_search/caching_directory.dart'; 4 | import 'package:http/http.dart' as http; 5 | 6 | class CachingNetworkImage extends StatefulWidget { 7 | final String url; 8 | CachingNetworkImage(this.url); 9 | 10 | @override 11 | _CachingNetworkImageState createState() => _CachingNetworkImageState(); 12 | } 13 | 14 | class _CachingNetworkImageState extends State { 15 | final _imageCache = CachingDirectory('image-cache'); 16 | Image _image; 17 | 18 | @override 19 | void initState() { 20 | super.initState(); 21 | load(); 22 | } 23 | 24 | void load() async { 25 | Uint8List bytes; 26 | 27 | // if the file doesn't exist in the cache, write it 28 | var file = await _imageCache.getCacheFile(widget.url); 29 | if (await file.exists()) { 30 | bytes = await file.readAsBytes(); 31 | } else { 32 | var resp = await http.get(widget.url); 33 | if (resp.statusCode != 200) throw Exception('get error: statusCode= ${resp.statusCode}'); 34 | 35 | bytes = resp.bodyBytes; 36 | await file.writeAsBytes(bytes, flush: true); 37 | } 38 | 39 | // check to see if we've been disposed before the image is returned (it happens...) 40 | if (mounted) setState(() => _image = Image.memory(bytes)); 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) => _image == null ? Center(child: CircularProgressIndicator()) : _image; 45 | } 46 | -------------------------------------------------------------------------------- /lib/caching_search_engine.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:http/http.dart' as http; 3 | import 'cse-results.dart'; 4 | import 'caching_directory.dart'; 5 | 6 | class CachingSearchEngine { 7 | final String cseKey; 8 | final String cseEngineID; 9 | final _resultsCache = CachingDirectory('cse-cache', cacheExt: '.json'); 10 | 11 | CachingSearchEngine({@required this.cseEngineID, @required this.cseKey}); 12 | 13 | Future imageSearch(String q) async { 14 | String json; 15 | 16 | // if the file doesn't exist in the cache, write it 17 | var file = await _resultsCache.getCacheFile(q); 18 | if (await file.exists()) { 19 | json = await file.readAsString(); 20 | } else { 21 | var params = { 22 | 'cx': cseEngineID, 23 | 'key': cseKey, 24 | 'searchType': 'image', 25 | 'q': q, 26 | }; 27 | 28 | var query = Uri(queryParameters: params).query; 29 | var url = 'https://www.googleapis.com/customsearch/v1?$query'; 30 | var resp = await http.get(url); 31 | if (resp.statusCode != 200) throw Exception('get error: statusCode= ${resp.statusCode}'); 32 | 33 | json = resp.body; 34 | await file.writeAsString(json); 35 | } 36 | 37 | return Results.fromRawJson(json); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cse-results.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class Results { 4 | String kind; 5 | Url url; 6 | Queries queries; 7 | Context context; 8 | SearchInformation searchInformation; 9 | List items; 10 | 11 | Results({ 12 | this.kind, 13 | this.url, 14 | this.queries, 15 | this.context, 16 | this.searchInformation, 17 | this.items, 18 | }); 19 | 20 | factory Results.fromRawJson(String str) => Results.fromJson(json.decode(str)); 21 | 22 | String toRawJson() => json.encode(toJson()); 23 | 24 | factory Results.fromJson(Map json) => new Results( 25 | kind: json["kind"] == null ? null : json["kind"], 26 | url: json["url"] == null ? null : Url.fromJson(json["url"]), 27 | queries: json["queries"] == null ? null : Queries.fromJson(json["queries"]), 28 | context: json["context"] == null ? null : Context.fromJson(json["context"]), 29 | searchInformation: json["searchInformation"] == null 30 | ? null 31 | : SearchInformation.fromJson(json["searchInformation"]), 32 | items: json["items"] == null 33 | ? null 34 | : new List.from(json["items"].map((x) => Item.fromJson(x))), 35 | ); 36 | 37 | Map toJson() => { 38 | "kind": kind == null ? null : kind, 39 | "url": url == null ? null : url.toJson(), 40 | "queries": queries == null ? null : queries.toJson(), 41 | "context": context == null ? null : context.toJson(), 42 | "searchInformation": searchInformation == null ? null : searchInformation.toJson(), 43 | "items": items == null ? null : new List.from(items.map((x) => x.toJson())), 44 | }; 45 | } 46 | 47 | class Context { 48 | String title; 49 | 50 | Context({ 51 | this.title, 52 | }); 53 | 54 | factory Context.fromRawJson(String str) => Context.fromJson(json.decode(str)); 55 | 56 | String toRawJson() => json.encode(toJson()); 57 | 58 | factory Context.fromJson(Map json) => new Context( 59 | title: json["title"] == null ? null : json["title"], 60 | ); 61 | 62 | Map toJson() => { 63 | "title": title == null ? null : title, 64 | }; 65 | } 66 | 67 | class Item { 68 | Kind kind; 69 | String title; 70 | String htmlTitle; 71 | String link; 72 | String displayLink; 73 | String snippet; 74 | String htmlSnippet; 75 | Mime mime; 76 | Image image; 77 | 78 | Item({ 79 | this.kind, 80 | this.title, 81 | this.htmlTitle, 82 | this.link, 83 | this.displayLink, 84 | this.snippet, 85 | this.htmlSnippet, 86 | this.mime, 87 | this.image, 88 | }); 89 | 90 | factory Item.fromRawJson(String str) => Item.fromJson(json.decode(str)); 91 | 92 | String toRawJson() => json.encode(toJson()); 93 | 94 | factory Item.fromJson(Map json) => new Item( 95 | kind: json["kind"] == null ? null : kindValues.map[json["kind"]], 96 | title: json["title"] == null ? null : json["title"], 97 | htmlTitle: json["htmlTitle"] == null ? null : json["htmlTitle"], 98 | link: json["link"] == null ? null : json["link"], 99 | displayLink: json["displayLink"] == null ? null : json["displayLink"], 100 | snippet: json["snippet"] == null ? null : json["snippet"], 101 | htmlSnippet: json["htmlSnippet"] == null ? null : json["htmlSnippet"], 102 | mime: json["mime"] == null ? null : mimeValues.map[json["mime"]], 103 | image: json["image"] == null ? null : Image.fromJson(json["image"]), 104 | ); 105 | 106 | Map toJson() => { 107 | "kind": kind == null ? null : kindValues.reverse[kind], 108 | "title": title == null ? null : title, 109 | "htmlTitle": htmlTitle == null ? null : htmlTitle, 110 | "link": link == null ? null : link, 111 | "displayLink": displayLink == null ? null : displayLink, 112 | "snippet": snippet == null ? null : snippet, 113 | "htmlSnippet": htmlSnippet == null ? null : htmlSnippet, 114 | "mime": mime == null ? null : mimeValues.reverse[mime], 115 | "image": image == null ? null : image.toJson(), 116 | }; 117 | } 118 | 119 | class Image { 120 | String contextLink; 121 | int height; 122 | int width; 123 | int byteSize; 124 | String thumbnailLink; 125 | int thumbnailHeight; 126 | int thumbnailWidth; 127 | 128 | Image({ 129 | this.contextLink, 130 | this.height, 131 | this.width, 132 | this.byteSize, 133 | this.thumbnailLink, 134 | this.thumbnailHeight, 135 | this.thumbnailWidth, 136 | }); 137 | 138 | factory Image.fromRawJson(String str) => Image.fromJson(json.decode(str)); 139 | 140 | String toRawJson() => json.encode(toJson()); 141 | 142 | factory Image.fromJson(Map json) => new Image( 143 | contextLink: json["contextLink"] == null ? null : json["contextLink"], 144 | height: json["height"] == null ? null : json["height"], 145 | width: json["width"] == null ? null : json["width"], 146 | byteSize: json["byteSize"] == null ? null : json["byteSize"], 147 | thumbnailLink: json["thumbnailLink"] == null ? null : json["thumbnailLink"], 148 | thumbnailHeight: json["thumbnailHeight"] == null ? null : json["thumbnailHeight"], 149 | thumbnailWidth: json["thumbnailWidth"] == null ? null : json["thumbnailWidth"], 150 | ); 151 | 152 | Map toJson() => { 153 | "contextLink": contextLink == null ? null : contextLink, 154 | "height": height == null ? null : height, 155 | "width": width == null ? null : width, 156 | "byteSize": byteSize == null ? null : byteSize, 157 | "thumbnailLink": thumbnailLink == null ? null : thumbnailLink, 158 | "thumbnailHeight": thumbnailHeight == null ? null : thumbnailHeight, 159 | "thumbnailWidth": thumbnailWidth == null ? null : thumbnailWidth, 160 | }; 161 | } 162 | 163 | enum Kind { CUSTOMSEARCH_RESULT } 164 | 165 | final kindValues = new EnumValues({"customsearch#result": Kind.CUSTOMSEARCH_RESULT}); 166 | 167 | enum Mime { IMAGE_JPEG, IMAGE, IMAGE_PNG } 168 | 169 | final mimeValues = new EnumValues( 170 | {"image/": Mime.IMAGE, "image/jpeg": Mime.IMAGE_JPEG, "image/png": Mime.IMAGE_PNG}); 171 | 172 | class Queries { 173 | List request; 174 | List nextPage; 175 | 176 | Queries({ 177 | this.request, 178 | this.nextPage, 179 | }); 180 | 181 | factory Queries.fromRawJson(String str) => Queries.fromJson(json.decode(str)); 182 | 183 | String toRawJson() => json.encode(toJson()); 184 | 185 | factory Queries.fromJson(Map json) => new Queries( 186 | request: json["request"] == null 187 | ? null 188 | : new List.from(json["request"].map((x) => NextPage.fromJson(x))), 189 | nextPage: json["nextPage"] == null 190 | ? null 191 | : new List.from(json["nextPage"].map((x) => NextPage.fromJson(x))), 192 | ); 193 | 194 | Map toJson() => { 195 | "request": request == null ? null : new List.from(request.map((x) => x.toJson())), 196 | "nextPage": 197 | nextPage == null ? null : new List.from(nextPage.map((x) => x.toJson())), 198 | }; 199 | } 200 | 201 | class NextPage { 202 | String title; 203 | String totalResults; 204 | String searchTerms; 205 | int count; 206 | int startIndex; 207 | String inputEncoding; 208 | String outputEncoding; 209 | String safe; 210 | String cx; 211 | String searchType; 212 | 213 | NextPage({ 214 | this.title, 215 | this.totalResults, 216 | this.searchTerms, 217 | this.count, 218 | this.startIndex, 219 | this.inputEncoding, 220 | this.outputEncoding, 221 | this.safe, 222 | this.cx, 223 | this.searchType, 224 | }); 225 | 226 | factory NextPage.fromRawJson(String str) => NextPage.fromJson(json.decode(str)); 227 | 228 | String toRawJson() => json.encode(toJson()); 229 | 230 | factory NextPage.fromJson(Map json) => new NextPage( 231 | title: json["title"] == null ? null : json["title"], 232 | totalResults: json["totalResults"] == null ? null : json["totalResults"], 233 | searchTerms: json["searchTerms"] == null ? null : json["searchTerms"], 234 | count: json["count"] == null ? null : json["count"], 235 | startIndex: json["startIndex"] == null ? null : json["startIndex"], 236 | inputEncoding: json["inputEncoding"] == null ? null : json["inputEncoding"], 237 | outputEncoding: json["outputEncoding"] == null ? null : json["outputEncoding"], 238 | safe: json["safe"] == null ? null : json["safe"], 239 | cx: json["cx"] == null ? null : json["cx"], 240 | searchType: json["searchType"] == null ? null : json["searchType"], 241 | ); 242 | 243 | Map toJson() => { 244 | "title": title == null ? null : title, 245 | "totalResults": totalResults == null ? null : totalResults, 246 | "searchTerms": searchTerms == null ? null : searchTerms, 247 | "count": count == null ? null : count, 248 | "startIndex": startIndex == null ? null : startIndex, 249 | "inputEncoding": inputEncoding == null ? null : inputEncoding, 250 | "outputEncoding": outputEncoding == null ? null : outputEncoding, 251 | "safe": safe == null ? null : safe, 252 | "cx": cx == null ? null : cx, 253 | "searchType": searchType == null ? null : searchType, 254 | }; 255 | } 256 | 257 | class SearchInformation { 258 | double searchTime; 259 | String formattedSearchTime; 260 | String totalResults; 261 | String formattedTotalResults; 262 | 263 | SearchInformation({ 264 | this.searchTime, 265 | this.formattedSearchTime, 266 | this.totalResults, 267 | this.formattedTotalResults, 268 | }); 269 | 270 | factory SearchInformation.fromRawJson(String str) => SearchInformation.fromJson(json.decode(str)); 271 | 272 | String toRawJson() => json.encode(toJson()); 273 | 274 | factory SearchInformation.fromJson(Map json) => new SearchInformation( 275 | searchTime: json["searchTime"] == null ? null : json["searchTime"].toDouble(), 276 | formattedSearchTime: 277 | json["formattedSearchTime"] == null ? null : json["formattedSearchTime"], 278 | totalResults: json["totalResults"] == null ? null : json["totalResults"], 279 | formattedTotalResults: 280 | json["formattedTotalResults"] == null ? null : json["formattedTotalResults"], 281 | ); 282 | 283 | Map toJson() => { 284 | "searchTime": searchTime == null ? null : searchTime, 285 | "formattedSearchTime": formattedSearchTime == null ? null : formattedSearchTime, 286 | "totalResults": totalResults == null ? null : totalResults, 287 | "formattedTotalResults": formattedTotalResults == null ? null : formattedTotalResults, 288 | }; 289 | } 290 | 291 | class Url { 292 | String type; 293 | String template; 294 | 295 | Url({ 296 | this.type, 297 | this.template, 298 | }); 299 | 300 | factory Url.fromRawJson(String str) => Url.fromJson(json.decode(str)); 301 | 302 | String toRawJson() => json.encode(toJson()); 303 | 304 | factory Url.fromJson(Map json) => new Url( 305 | type: json["type"] == null ? null : json["type"], 306 | template: json["template"] == null ? null : json["template"], 307 | ); 308 | 309 | Map toJson() => { 310 | "type": type == null ? null : type, 311 | "template": template == null ? null : template, 312 | }; 313 | } 314 | 315 | class EnumValues { 316 | Map map; 317 | Map reverseMap; 318 | 319 | EnumValues(this.map); 320 | 321 | Map get reverse { 322 | if (reverseMap == null) { 323 | reverseMap = map.map((k, v) => new MapEntry(v, k)); 324 | } 325 | return reverseMap; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride, kIsWeb; 5 | import 'package:path/path.dart' as path; 6 | import 'caching_network_image.dart'; 7 | import 'caching_search_engine.dart'; 8 | import 'cse-results.dart' as cse; 9 | 10 | void _desktopInitHack() { 11 | if (kIsWeb) return; 12 | 13 | if (Platform.isMacOS) { 14 | debugDefaultTargetPlatformOverride = TargetPlatform.iOS; 15 | } else if (Platform.isLinux || Platform.isWindows) { 16 | debugDefaultTargetPlatformOverride = TargetPlatform.android; 17 | } else if (Platform.isFuchsia) { 18 | debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; 19 | } 20 | } 21 | 22 | void main() { 23 | _desktopInitHack(); 24 | runApp(MyApp()); 25 | } 26 | 27 | class MyApp extends StatelessWidget { 28 | final title = 'Flutter Image Search'; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return MaterialApp( 33 | title: title, 34 | theme: ThemeData(primarySwatch: Colors.blue), 35 | home: Scaffold( 36 | appBar: AppBar(title: Text(title)), 37 | body: ImageSearch(), 38 | ), 39 | ); 40 | } 41 | } 42 | 43 | class ImageSearch extends StatefulWidget { 44 | @override 45 | _ImageSearchState createState() => _ImageSearchState(); 46 | } 47 | 48 | class _ImageSearchState extends State { 49 | final CachingSearchEngine _engine; 50 | final _debouncer = Debouncer(); 51 | var _items = List(); 52 | var _selectedLink = ''; 53 | 54 | _ImageSearchState() 55 | : _engine = CachingSearchEngine( 56 | cseEngineID: File("cse-engine-id.txt").readAsStringSync(), 57 | cseKey: File("cse-key.txt").readAsStringSync(), 58 | ); 59 | 60 | static final _imageExts = ['.jpg', '.jpeg', '.bmp', '.png']; 61 | static bool _isImageExt(String ext) => _imageExts.contains(ext.toLowerCase()); 62 | 63 | Future search(String q) async { 64 | debugPrint('search: q= "$q"'); 65 | 66 | var res = await _engine.imageSearch(q); 67 | 68 | var items = List(); 69 | for (var item in res.items ?? List()) { 70 | // only show results that look like they're images 71 | // TODO: actually download and check the MIME type 72 | var pathPart = item.link.split('?')[0]; // file path or url path w/o query string portion 73 | var ext = path.extension(pathPart); 74 | if (_isImageExt(ext)) items.add(item); 75 | } 76 | 77 | setState(() => _items = items); 78 | } 79 | 80 | @override 81 | Widget build(BuildContext context) => Padding( 82 | padding: const EdgeInsets.all(8.0), 83 | child: Column( 84 | children: [ 85 | TextField( 86 | decoration: InputDecoration(labelText: 'Search'), 87 | onChanged: onSearchTextChanged, 88 | ), 89 | Expanded( 90 | child: _debouncer.isRunning 91 | ? Center(child: CircularProgressIndicator()) 92 | : Scrollbar( 93 | child: GridView.count( 94 | crossAxisCount: 3, 95 | children: [ 96 | for (var item in _items) 97 | RawMaterialButton( 98 | child: Padding( 99 | padding: const EdgeInsets.all(8.0), 100 | child: CachingNetworkImage(item.link), 101 | ), 102 | fillColor: item.link == _selectedLink ? Colors.blue : Colors.white, 103 | onPressed: () => setState(() => _selectedLink = item.link), 104 | ), 105 | ], 106 | ), 107 | ), 108 | ), 109 | Text(_selectedLink), 110 | ], 111 | ), 112 | ); 113 | 114 | void onSearchTextChanged(String value) { 115 | debugPrint('onSearchTextChanged: value= "$value"'); 116 | 117 | if (value.length < 5) 118 | _debouncer.stop(); 119 | else 120 | _debouncer.run(() => search(value)); 121 | 122 | setState(() { 123 | _items.clear(); 124 | _selectedLink = ''; 125 | }); 126 | } 127 | } 128 | 129 | class Debouncer { 130 | final int milliseconds; 131 | VoidCallback action; 132 | Timer _timer; 133 | 134 | Debouncer({this.milliseconds = 500}); 135 | 136 | void run(VoidCallback action) { 137 | stop(); 138 | _timer = Timer(Duration(milliseconds: milliseconds), () { 139 | stop(); 140 | action(); 141 | }); 142 | } 143 | 144 | void stop() { 145 | if (_timer == null) return; 146 | _timer.cancel(); 147 | _timer = null; 148 | } 149 | 150 | bool get isRunning => _timer != null; 151 | } 152 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | 3 | # Generated by flutter tooling as needed. 4 | Flutter/ephemeral/ 5 | # Created by CocoaPods for plugins. 6 | Pods/ 7 | -------------------------------------------------------------------------------- /macos/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Cocoa 16 | 17 | @NSApplicationMain 18 | class AppDelegate: NSObject, NSApplicationDelegate { 19 | @IBOutlet weak var window: NSWindow! 20 | 21 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 22 | return true 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /macos/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /macos/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /macos/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/ExampleWindow.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Cocoa 16 | import FlutterMacOS 17 | 18 | class ExampleWindow: NSWindow { 19 | override func awakeFromNib() { 20 | let flutterViewController = FLEViewController.init() 21 | let windowFrame = self.frame 22 | self.contentViewController = flutterViewController 23 | self.setFrame(windowFrame, display: true) 24 | 25 | RegisterGeneratedPlugins(registry: flutterViewController) 26 | 27 | super.awakeFromNib() 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | import Foundation 5 | import FlutterMacOS 6 | 7 | 8 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 9 | } 10 | -------------------------------------------------------------------------------- /macos/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 Google LLC. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 33CC111A2044C6BA0003C045 /* Build Flutter Bundle */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Build Flutter Bundle" */; 13 | buildPhases = ( 14 | 33CC111E2044C6BF0003C045 /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = "Build Flutter Bundle"; 19 | productName = FLX; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 25 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 26 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 27 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 28 | 33CC11132044BFA00003C045 /* ExampleWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* ExampleWindow.swift */; }; 29 | 33CC112F204626C80003C045 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC112C20461AD40003C045 /* flutter_assets */; }; 30 | 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; 31 | 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXContainerItemProxy section */ 35 | 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 33CC111A2044C6BA0003C045; 40 | remoteInfo = FLX; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXCopyFilesBuildPhase section */ 45 | 33CC110E2044A8840003C045 /* Bundle Framework */ = { 46 | isa = PBXCopyFilesBuildPhase; 47 | buildActionMask = 2147483647; 48 | dstPath = ""; 49 | dstSubfolderSpec = 10; 50 | files = ( 51 | 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */, 52 | ); 53 | name = "Bundle Framework"; 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXCopyFilesBuildPhase section */ 57 | 58 | /* Begin PBXFileReference section */ 59 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Warnings.xcconfig; path = Configs/Warnings.xcconfig; sourceTree = ""; }; 60 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 61 | 33CC10ED2044A3C60003C045 /* Flutter Desktop Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Flutter Desktop Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 63 | 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 64 | 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 65 | 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | 33CC11122044BFA00003C045 /* ExampleWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleWindow.swift; sourceTree = ""; }; 67 | 33CC112C20461AD40003C045 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../build/flutter_assets; sourceTree = ""; }; 68 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; 69 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; 70 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; 71 | 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; }; 72 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configs/Release.xcconfig; sourceTree = ""; }; 73 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configs/Debug.xcconfig; sourceTree = ""; }; 74 | /* End PBXFileReference section */ 75 | 76 | /* Begin PBXFrameworksBuildPhase section */ 77 | 33CC10EA2044A3C60003C045 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXFrameworksBuildPhase section */ 86 | 87 | /* Begin PBXGroup section */ 88 | 33BA886A226E78AF003329D5 /* Configs */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 92 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 93 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, 94 | ); 95 | name = Configs; 96 | sourceTree = ""; 97 | }; 98 | 33CC10E42044A3C60003C045 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */, 102 | 33CC11122044BFA00003C045 /* ExampleWindow.swift */, 103 | 33CC11242044D66E0003C045 /* Resources */, 104 | 33BA886A226E78AF003329D5 /* Configs */, 105 | 33CEB47122A05771004F2AC0 /* Flutter */, 106 | 33CC10EE2044A3C60003C045 /* Products */, 107 | ); 108 | sourceTree = ""; 109 | }; 110 | 33CC10EE2044A3C60003C045 /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 33CC10ED2044A3C60003C045 /* Flutter Desktop Example.app */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | 33CC11242044D66E0003C045 /* Resources */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 33CC10F22044A3C60003C045 /* Assets.xcassets */, 122 | 33CC10F42044A3C60003C045 /* MainMenu.xib */, 123 | 33CC10F72044A3C60003C045 /* Info.plist */, 124 | 33CC112C20461AD40003C045 /* flutter_assets */, 125 | ); 126 | name = Resources; 127 | sourceTree = ""; 128 | }; 129 | 33CEB47122A05771004F2AC0 /* Flutter */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 133 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 134 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, 135 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, 136 | 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */, 137 | ); 138 | path = Flutter; 139 | sourceTree = ""; 140 | }; 141 | /* End PBXGroup section */ 142 | 143 | /* Begin PBXNativeTarget section */ 144 | 33CC10EC2044A3C60003C045 /* Runner */ = { 145 | isa = PBXNativeTarget; 146 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; 147 | buildPhases = ( 148 | 33CC10E92044A3C60003C045 /* Sources */, 149 | 33CC10EA2044A3C60003C045 /* Frameworks */, 150 | 33CC10EB2044A3C60003C045 /* Resources */, 151 | 33CC110E2044A8840003C045 /* Bundle Framework */, 152 | 3399D490228B24CF009A79C7 /* ShellScript */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | 33CC11202044C79F0003C045 /* PBXTargetDependency */, 158 | ); 159 | name = Runner; 160 | productName = "Flutter Desktop Example"; 161 | productReference = 33CC10ED2044A3C60003C045 /* Flutter Desktop Example.app */; 162 | productType = "com.apple.product-type.application"; 163 | }; 164 | /* End PBXNativeTarget section */ 165 | 166 | /* Begin PBXProject section */ 167 | 33CC10E52044A3C60003C045 /* Project object */ = { 168 | isa = PBXProject; 169 | attributes = { 170 | LastSwiftUpdateCheck = 0920; 171 | LastUpgradeCheck = 0930; 172 | ORGANIZATIONNAME = "Google LLC"; 173 | TargetAttributes = { 174 | 33CC10EC2044A3C60003C045 = { 175 | CreatedOnToolsVersion = 9.2; 176 | LastSwiftMigration = 0920; 177 | ProvisioningStyle = Manual; 178 | }; 179 | 33CC111A2044C6BA0003C045 = { 180 | CreatedOnToolsVersion = 9.2; 181 | ProvisioningStyle = Automatic; 182 | }; 183 | }; 184 | }; 185 | buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; 186 | compatibilityVersion = "Xcode 8.0"; 187 | developmentRegion = en; 188 | hasScannedForEncodings = 0; 189 | knownRegions = ( 190 | en, 191 | Base, 192 | ); 193 | mainGroup = 33CC10E42044A3C60003C045; 194 | productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; 195 | projectDirPath = ""; 196 | projectRoot = ""; 197 | targets = ( 198 | 33CC10EC2044A3C60003C045 /* Runner */, 199 | 33CC111A2044C6BA0003C045 /* Build Flutter Bundle */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 33CC10EB2044A3C60003C045 /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 210 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, 211 | 33CC112F204626C80003C045 /* flutter_assets in Resources */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXResourcesBuildPhase section */ 216 | 217 | /* Begin PBXShellScriptBuildPhase section */ 218 | 3399D490228B24CF009A79C7 /* ShellScript */ = { 219 | isa = PBXShellScriptBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | inputFileListPaths = ( 224 | ); 225 | inputPaths = ( 226 | ); 227 | outputFileListPaths = ( 228 | ); 229 | outputPaths = ( 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | shellPath = /bin/sh; 233 | shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n"; 234 | }; 235 | 33CC111E2044C6BF0003C045 /* ShellScript */ = { 236 | isa = PBXShellScriptBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | ); 240 | inputPaths = ( 241 | ); 242 | outputPaths = ( 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | shellPath = /bin/sh; 246 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_build_flutter_assets.sh\n"; 247 | }; 248 | /* End PBXShellScriptBuildPhase section */ 249 | 250 | /* Begin PBXSourcesBuildPhase section */ 251 | 33CC10E92044A3C60003C045 /* Sources */ = { 252 | isa = PBXSourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | 33CC11132044BFA00003C045 /* ExampleWindow.swift in Sources */, 256 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, 257 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin PBXTargetDependency section */ 264 | 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { 265 | isa = PBXTargetDependency; 266 | target = 33CC111A2044C6BA0003C045 /* Build Flutter Bundle */; 267 | targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin PBXVariantGroup section */ 272 | 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { 273 | isa = PBXVariantGroup; 274 | children = ( 275 | 33CC10F52044A3C60003C045 /* Base */, 276 | ); 277 | name = MainMenu.xib; 278 | sourceTree = ""; 279 | }; 280 | /* End PBXVariantGroup section */ 281 | 282 | /* Begin XCBuildConfiguration section */ 283 | 33CC10F92044A3C60003C045 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ANALYZER_NONNULL = YES; 289 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 295 | CLANG_WARN_BOOL_CONVERSION = YES; 296 | CLANG_WARN_CONSTANT_CONVERSION = YES; 297 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 299 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 300 | CLANG_WARN_EMPTY_BODY = YES; 301 | CLANG_WARN_ENUM_CONVERSION = YES; 302 | CLANG_WARN_INFINITE_RECURSION = YES; 303 | CLANG_WARN_INT_CONVERSION = YES; 304 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 307 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 308 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 309 | CODE_SIGN_IDENTITY = ""; 310 | COPY_PHASE_STRIP = NO; 311 | DEBUG_INFORMATION_FORMAT = dwarf; 312 | ENABLE_STRICT_OBJC_MSGSEND = YES; 313 | ENABLE_TESTABILITY = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu11; 315 | GCC_DYNAMIC_NO_PIC = NO; 316 | GCC_NO_COMMON_BLOCKS = YES; 317 | GCC_OPTIMIZATION_LEVEL = 0; 318 | GCC_PREPROCESSOR_DEFINITIONS = ( 319 | "DEBUG=1", 320 | "$(inherited)", 321 | ); 322 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 323 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 324 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 325 | GCC_WARN_UNUSED_FUNCTION = YES; 326 | GCC_WARN_UNUSED_VARIABLE = YES; 327 | MACOSX_DEPLOYMENT_TARGET = 10.13; 328 | MTL_ENABLE_DEBUG_INFO = YES; 329 | ONLY_ACTIVE_ARCH = YES; 330 | SDKROOT = macosx; 331 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 332 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 333 | }; 334 | name = Debug; 335 | }; 336 | 33CC10FA2044A3C60003C045 /* Release */ = { 337 | isa = XCBuildConfiguration; 338 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 339 | buildSettings = { 340 | ALWAYS_SEARCH_USER_PATHS = NO; 341 | CLANG_ANALYZER_NONNULL = YES; 342 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 343 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 344 | CLANG_CXX_LIBRARY = "libc++"; 345 | CLANG_ENABLE_MODULES = YES; 346 | CLANG_ENABLE_OBJC_ARC = YES; 347 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 348 | CLANG_WARN_BOOL_CONVERSION = YES; 349 | CLANG_WARN_CONSTANT_CONVERSION = YES; 350 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 351 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 352 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 353 | CLANG_WARN_EMPTY_BODY = YES; 354 | CLANG_WARN_ENUM_CONVERSION = YES; 355 | CLANG_WARN_INFINITE_RECURSION = YES; 356 | CLANG_WARN_INT_CONVERSION = YES; 357 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 358 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 359 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 360 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 361 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 362 | CODE_SIGN_IDENTITY = ""; 363 | COPY_PHASE_STRIP = NO; 364 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 365 | ENABLE_NS_ASSERTIONS = NO; 366 | ENABLE_STRICT_OBJC_MSGSEND = YES; 367 | GCC_C_LANGUAGE_STANDARD = gnu11; 368 | GCC_NO_COMMON_BLOCKS = YES; 369 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 370 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | MACOSX_DEPLOYMENT_TARGET = 10.13; 375 | MTL_ENABLE_DEBUG_INFO = NO; 376 | SDKROOT = macosx; 377 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 378 | }; 379 | name = Release; 380 | }; 381 | 33CC10FC2044A3C60003C045 /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | CLANG_ENABLE_MODULES = YES; 386 | CODE_SIGN_STYLE = Manual; 387 | COMBINE_HIDPI_IMAGES = YES; 388 | DEVELOPMENT_TEAM = ""; 389 | FRAMEWORK_SEARCH_PATHS = ( 390 | $PROJECT_DIR/Flutter/ephemeral, 391 | "$(inherited)", 392 | ); 393 | INFOPLIST_FILE = Info.plist; 394 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 395 | PRODUCT_BUNDLE_IDENTIFIER = com.google.FlutterEmbedderMacExample.FlutterDesktopExample; 396 | PRODUCT_NAME = "Flutter Desktop Example"; 397 | PROVISIONING_PROFILE_SPECIFIER = ""; 398 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 399 | SWIFT_VERSION = 4.0; 400 | }; 401 | name = Debug; 402 | }; 403 | 33CC10FD2044A3C60003C045 /* Release */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 407 | CLANG_ENABLE_MODULES = YES; 408 | CODE_SIGN_STYLE = Manual; 409 | COMBINE_HIDPI_IMAGES = YES; 410 | DEVELOPMENT_TEAM = ""; 411 | FRAMEWORK_SEARCH_PATHS = ( 412 | $PROJECT_DIR/Flutter/ephemeral, 413 | "$(inherited)", 414 | ); 415 | INFOPLIST_FILE = Info.plist; 416 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 417 | PRODUCT_BUNDLE_IDENTIFIER = com.google.FlutterEmbedderMacExample.FlutterDesktopExample; 418 | PRODUCT_NAME = "Flutter Desktop Example"; 419 | PROVISIONING_PROFILE_SPECIFIER = ""; 420 | SWIFT_VERSION = 4.0; 421 | }; 422 | name = Release; 423 | }; 424 | 33CC111C2044C6BA0003C045 /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | buildSettings = { 427 | CODE_SIGN_STYLE = Automatic; 428 | PRODUCT_NAME = "$(TARGET_NAME)"; 429 | }; 430 | name = Debug; 431 | }; 432 | 33CC111D2044C6BA0003C045 /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | CODE_SIGN_STYLE = Automatic; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | }; 438 | name = Release; 439 | }; 440 | /* End XCBuildConfiguration section */ 441 | 442 | /* Begin XCConfigurationList section */ 443 | 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { 444 | isa = XCConfigurationList; 445 | buildConfigurations = ( 446 | 33CC10F92044A3C60003C045 /* Debug */, 447 | 33CC10FA2044A3C60003C045 /* Release */, 448 | ); 449 | defaultConfigurationIsVisible = 0; 450 | defaultConfigurationName = Release; 451 | }; 452 | 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { 453 | isa = XCConfigurationList; 454 | buildConfigurations = ( 455 | 33CC10FC2044A3C60003C045 /* Debug */, 456 | 33CC10FD2044A3C60003C045 /* Release */, 457 | ); 458 | defaultConfigurationIsVisible = 0; 459 | defaultConfigurationName = Release; 460 | }; 461 | 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Build Flutter Bundle" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | 33CC111C2044C6BA0003C045 /* Debug */, 465 | 33CC111D2044C6BA0003C045 /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | /* End XCConfigurationList section */ 471 | }; 472 | rootObject = 33CC10E52044A3C60003C045 /* Project object */; 473 | } 474 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.3.0" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.5" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | cupertino_icons: 33 | dependency: "direct main" 34 | description: 35 | name: cupertino_icons 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.1.2" 39 | flutter: 40 | dependency: "direct main" 41 | description: flutter 42 | source: sdk 43 | version: "0.0.0" 44 | flutter_test: 45 | dependency: "direct dev" 46 | description: flutter 47 | source: sdk 48 | version: "0.0.0" 49 | http: 50 | dependency: "direct main" 51 | description: 52 | name: http 53 | url: "https://pub.dartlang.org" 54 | source: hosted 55 | version: "0.12.0+2" 56 | http_parser: 57 | dependency: transitive 58 | description: 59 | name: http_parser 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "3.1.3" 63 | matcher: 64 | dependency: transitive 65 | description: 66 | name: matcher 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "0.12.5" 70 | meta: 71 | dependency: transitive 72 | description: 73 | name: meta 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "1.1.7" 77 | path: 78 | dependency: "direct main" 79 | description: 80 | name: path 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.6.4" 84 | pedantic: 85 | dependency: transitive 86 | description: 87 | name: pedantic 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.8.0+1" 91 | quiver: 92 | dependency: transitive 93 | description: 94 | name: quiver 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "2.0.5" 98 | sky_engine: 99 | dependency: transitive 100 | description: flutter 101 | source: sdk 102 | version: "0.0.99" 103 | source_span: 104 | dependency: transitive 105 | description: 106 | name: source_span 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.5.5" 110 | stack_trace: 111 | dependency: transitive 112 | description: 113 | name: stack_trace 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.9.3" 117 | stream_channel: 118 | dependency: transitive 119 | description: 120 | name: stream_channel 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "2.0.0" 124 | string_scanner: 125 | dependency: transitive 126 | description: 127 | name: string_scanner 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.0.5" 131 | term_glyph: 132 | dependency: transitive 133 | description: 134 | name: term_glyph 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.1.0" 138 | test_api: 139 | dependency: transitive 140 | description: 141 | name: test_api 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "0.2.5" 145 | typed_data: 146 | dependency: transitive 147 | description: 148 | name: typed_data 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.1.6" 152 | vector_math: 153 | dependency: transitive 154 | description: 155 | name: vector_math 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "2.0.8" 159 | sdks: 160 | dart: ">=2.2.2 <3.0.0" 161 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_image_search 2 | description: A new Flutter project. 3 | version: 1.0.0+1 4 | 5 | environment: 6 | sdk: ">=2.2.2 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | cupertino_icons: ^0.1.2 12 | http: 13 | path: 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | 19 | flutter: 20 | uses-material-design: true 21 | -------------------------------------------------------------------------------- /readme/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/flutter_image_search/6c92d762459ad7d14bc39f24af2731e02f410a12/readme/demo.gif -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # Local additions, not from the link above 7 | flutter/ 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_i.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *.log 83 | *.vspscc 84 | *.vssscc 85 | .builds 86 | *.pidb 87 | *.svclog 88 | *.scc 89 | 90 | # Chutzpah Test files 91 | _Chutzpah* 92 | 93 | # Visual C++ cache files 94 | ipch/ 95 | *.aps 96 | *.ncb 97 | *.opendb 98 | *.opensdf 99 | *.sdf 100 | *.cachefile 101 | *.VC.db 102 | *.VC.VC.opendb 103 | 104 | # Visual Studio profiler 105 | *.psess 106 | *.vsp 107 | *.vspx 108 | *.sap 109 | 110 | # Visual Studio Trace Files 111 | *.e2e 112 | 113 | # TFS 2012 Local Workspace 114 | $tf/ 115 | 116 | # Guidance Automation Toolkit 117 | *.gpState 118 | 119 | # ReSharper is a .NET coding add-in 120 | _ReSharper*/ 121 | *.[Rr]e[Ss]harper 122 | *.DotSettings.user 123 | 124 | # JustCode is a .NET coding add-in 125 | .JustCode 126 | 127 | # TeamCity is a build add-in 128 | _TeamCity* 129 | 130 | # DotCover is a Code Coverage Tool 131 | *.dotCover 132 | 133 | # AxoCover is a Code Coverage Tool 134 | .axoCover/* 135 | !.axoCover/settings.json 136 | 137 | # Visual Studio code coverage results 138 | *.coverage 139 | *.coveragexml 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | nCrunchTemp_* 145 | 146 | # MightyMoose 147 | *.mm.* 148 | AutoTest.Net/ 149 | 150 | # Web workbench (sass) 151 | .sass-cache/ 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # Note: Comment the next line if you want to checkin your web deploy settings, 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/[Pp]ackages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/[Pp]ackages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/[Pp]ackages/repositories.config 190 | # NuGet v3's project.json files produces more ignorable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Windows Store app package directories and files 203 | AppPackages/ 204 | BundleArtifacts/ 205 | Package.StoreAssociation.xml 206 | _pkginfo.txt 207 | *.appx 208 | 209 | # Visual Studio cache files 210 | # files ending in .cache can be ignored 211 | *.[Cc]ache 212 | # but keep track of directories ending in .cache 213 | !*.[Cc]ache/ 214 | 215 | # Others 216 | ClientBin/ 217 | ~$* 218 | *~ 219 | *.dbmdl 220 | *.dbproj.schemaview 221 | *.jfm 222 | *.pfx 223 | *.publishsettings 224 | orleans.codegen.cs 225 | 226 | # Including strong name files can present a security risk 227 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 228 | #*.snk 229 | 230 | # Since there are multiple workflows, uncomment next line to ignore bower_components 231 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 232 | #bower_components/ 233 | 234 | # RIA/Silverlight projects 235 | Generated_Code/ 236 | 237 | # Backup & report files from converting an old project file 238 | # to a newer Visual Studio version. Backup files are not needed, 239 | # because we have git ;-) 240 | _UpgradeReport_Files/ 241 | Backup*/ 242 | UpgradeLog*.XML 243 | UpgradeLog*.htm 244 | ServiceFabricBackup/ 245 | *.rptproj.bak 246 | 247 | # SQL Server files 248 | *.mdf 249 | *.ldf 250 | *.ndf 251 | 252 | # Business Intelligence projects 253 | *.rdl.data 254 | *.bim.layout 255 | *.bim_*.settings 256 | *.rptproj.rsuser 257 | 258 | # Microsoft Fakes 259 | FakesAssemblies/ 260 | 261 | # GhostDoc plugin setting file 262 | *.GhostDoc.xml 263 | 264 | # Node.js Tools for Visual Studio 265 | .ntvs_analysis.dat 266 | node_modules/ 267 | 268 | # Visual Studio 6 build log 269 | *.plg 270 | 271 | # Visual Studio 6 workspace options file 272 | *.opt 273 | 274 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 275 | *.vbw 276 | 277 | # Visual Studio LightSwitch build output 278 | **/*.HTMLClient/GeneratedArtifacts 279 | **/*.DesktopClient/GeneratedArtifacts 280 | **/*.DesktopClient/ModelManifest.xml 281 | **/*.Server/GeneratedArtifacts 282 | **/*.Server/ModelManifest.xml 283 | _Pvt_Extensions 284 | 285 | # Paket dependency manager 286 | .paket/paket.exe 287 | paket-files/ 288 | 289 | # FAKE - F# Make 290 | .fake/ 291 | 292 | # JetBrains Rider 293 | .idea/ 294 | *.sln.iml 295 | 296 | # CodeRush 297 | .cr/ 298 | 299 | # Python Tools for Visual Studio (PTVS) 300 | __pycache__/ 301 | *.pyc 302 | 303 | # Cake - Uncomment if you are using it 304 | # tools/** 305 | # !tools/packages.config 306 | 307 | # Tabs Studio 308 | *.tss 309 | 310 | # Telerik's JustMock configuration file 311 | *.jmconfig 312 | 313 | # BizTalk build output 314 | *.btp.cs 315 | *.btm.cs 316 | *.odx.cs 317 | *.xsd.cs 318 | 319 | # OpenCover UI analysis results 320 | OpenCover/ 321 | 322 | # Azure Stream Analytics local run output 323 | ASALocalRun/ 324 | 325 | # MSBuild Binary and Structured Log 326 | *.binlog 327 | 328 | # NVidia Nsight GPU debugger configuration file 329 | *.nvuser 330 | 331 | # MFractors (Xamarin productivity tool) working folder 332 | .mfractor/ 333 | -------------------------------------------------------------------------------- /windows/Runner.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.645 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Runner", "Runner.vcxproj", "{5A827760-CF8B-408A-99A3-B6C0AD2271E7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug|x64.ActiveCfg = Debug|x64 15 | {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug|x64.Build.0 = Debug|x64 16 | {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release|x64.ActiveCfg = Release|x64 17 | {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B8A69CB0-A974-4774-9EBD-1E5EECACD186} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /windows/Runner.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15.0 15 | {5A827760-CF8B-408A-99A3-B6C0AD2271E7} 16 | GLFWExample 17 | 10.0.17763.0 18 | 19 | 20 | 21 | Application 22 | true 23 | v141 24 | v142 25 | MultiByte 26 | 27 | 28 | Application 29 | false 30 | v141 31 | v142 32 | true 33 | MultiByte 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | $(ProjectDir)..\build\windows\$(Platform)\$(Configuration)\$(ProjectName)\ 51 | $(ProjectDir)..\build\windows\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ 52 | $(ProjectDir)flutter\;$(ProjectDir)flutter\cpp_client_wrapper\include\;$(IncludePath) 53 | $(ProjectDir)flutter;$(LibraryPath) 54 | Flutter Desktop Example 55 | 56 | 57 | $(ProjectDir)..\build\windows\$(Platform)\$(Configuration)\$(ProjectName)\ 58 | $(ProjectDir)..\build\windows\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ 59 | $(ProjectDir)flutter\;$(ProjectDir)flutter\cpp_client_wrapper\include\;$(IncludePath) 60 | $(ProjectDir)flutter;$(LibraryPath) 61 | Flutter Desktop Example 62 | 63 | 64 | 65 | Level3 66 | Disabled 67 | true 68 | true 69 | 70 | 71 | _MBCS;%(PreprocessorDefinitions) 72 | 73 | 74 | flutter_windows.dll.lib;opengl32.lib;%(AdditionalDependencies) 75 | 76 | 77 | "$(ProjectDir)scripts\prepare_dependencies" debug 78 | Sync and build dependencies 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | "$(ProjectDir)scripts\bundle_assets_and_deps" "$(ProjectDir)flutter\" "$(OutputPath)" "$(TargetFileName)" 96 | 97 | 98 | Bundling dependencies 99 | Dummy_Run_Always 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | MaxSpeed 108 | true 109 | true 110 | true 111 | true 112 | 113 | 114 | _MBCS;%(PreprocessorDefinitions) 115 | 116 | 117 | true 118 | true 119 | flutter_windows.dll.lib;opengl32.lib;%(AdditionalDependencies) 120 | 121 | 122 | "$(ProjectDir)scripts\prepare_dependencies" release 123 | Sync and build dependencies 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | "$(ProjectDir)scripts\bundle_assets_and_deps" "$(ProjectDir)flutter\" "$(OutputPath)" "$(TargetFileName)" 141 | 142 | 143 | Bundling dependencies 144 | Dummy_Run_Always 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | $(SolutionDir) 160 | 161 | 162 | -------------------------------------------------------------------------------- /windows/Runner.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {2761a4b5-57b2-4d50-a677-d20ddc17a7f1} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files\Client Wrapper 26 | 27 | 28 | Source Files\Client Wrapper 29 | 30 | 31 | Source Files\Client Wrapper 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /windows/flutter_embedder_example.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "flutter/flutter_window_controller.h" 20 | 21 | // Include windows.h last, to minimize potential conflicts. The CreateWindow 22 | // macro needs to be undefined because it prevents calling 23 | // FlutterWindowController's method. 24 | #include 25 | #undef CreateWindow 26 | 27 | namespace { 28 | 29 | // Returns the path of the directory containing this executable, or an empty 30 | // string if the directory cannot be found. 31 | std::string GetExecutableDirectory() { 32 | char buffer[MAX_PATH]; 33 | if (GetModuleFileName(nullptr, buffer, MAX_PATH) == 0) { 34 | std::cerr << "Couldn't locate executable" << std::endl; 35 | return ""; 36 | } 37 | std::string executable_path(buffer); 38 | size_t last_separator_position = executable_path.find_last_of('\\'); 39 | if (last_separator_position == std::string::npos) { 40 | std::cerr << "Unabled to find parent directory of " << executable_path 41 | << std::endl; 42 | return ""; 43 | } 44 | return executable_path.substr(0, last_separator_position); 45 | } 46 | 47 | } // namespace 48 | 49 | int main(int argc, char **argv) { 50 | // Resources are located relative to the executable. 51 | std::string base_directory = GetExecutableDirectory(); 52 | if (base_directory.empty()) { 53 | base_directory = "."; 54 | } 55 | std::string data_directory = base_directory + "\\data"; 56 | std::string assets_path = data_directory + "\\flutter_assets"; 57 | std::string icu_data_path = data_directory + "\\icudtl.dat"; 58 | 59 | // Arguments for the Flutter Engine. 60 | std::vector arguments; 61 | 62 | flutter::FlutterWindowController flutter_controller(icu_data_path); 63 | 64 | // Start the engine. 65 | if (!flutter_controller.CreateWindow(800, 600, "Flutter Desktop Example", 66 | assets_path, arguments)) { 67 | return EXIT_FAILURE; 68 | } 69 | 70 | // Run until the window is closed. 71 | flutter_controller.RunEventLoop(); 72 | return EXIT_SUCCESS; 73 | } 74 | -------------------------------------------------------------------------------- /windows/scripts/bundle_assets_and_deps.bat: -------------------------------------------------------------------------------- 1 | :: Copyright 2018 Google LLC 2 | :: 3 | :: Licensed under the Apache License, Version 2.0 (the "License"); 4 | :: you may not use this file except in compliance with the License. 5 | :: You may obtain a copy of the License at 6 | :: 7 | :: http://www.apache.org/licenses/LICENSE-2.0 8 | :: 9 | :: Unless required by applicable law or agreed to in writing, software 10 | :: distributed under the License is distributed on an "AS IS" BASIS, 11 | :: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | :: See the License for the specific language governing permissions and 13 | :: limitations under the License. 14 | @echo off 15 | 16 | set FLUTTER_CACHE_DIR=%~1 17 | set BUNDLE_DIR=%~2 18 | set EXE_NAME=%~3 19 | 20 | set DATA_DIR=%BUNDLE_DIR%data 21 | 22 | if not exist "%DATA_DIR%" call mkdir "%DATA_DIR%" 23 | if %errorlevel% neq 0 exit /b %errorlevel% 24 | 25 | :: Write the executable name to the location expected by the Flutter tool. 26 | echo %EXE_NAME%>"%FLUTTER_CACHE_DIR%exe_filename" 27 | 28 | :: Copy the Flutter assets to the data directory. 29 | set FLUTTER_APP_DIR=%~dp0..\.. 30 | set ASSET_DIR_NAME=flutter_assets 31 | set TARGET_ASSET_DIR=%DATA_DIR%\%ASSET_DIR_NAME% 32 | if exist "%TARGET_ASSET_DIR%" call rmdir /s /q "%TARGET_ASSET_DIR%" 33 | if %errorlevel% neq 0 exit /b %errorlevel% 34 | call xcopy /s /e /i /q "%FLUTTER_APP_DIR%\build\%ASSET_DIR_NAME%" "%TARGET_ASSET_DIR%" 35 | if %errorlevel% neq 0 exit /b %errorlevel% 36 | 37 | :: Copy the icudtl.dat file from the Flutter tree to the data directory. 38 | call xcopy /y /d /q "%FLUTTER_CACHE_DIR%icudtl.dat" "%DATA_DIR%" 39 | if %errorlevel% neq 0 exit /b %errorlevel% 40 | 41 | :: Copy the Flutter DLL to the target location. 42 | call xcopy /y /d /q "%FLUTTER_CACHE_DIR%flutter_windows.dll" "%BUNDLE_DIR%" 43 | if %errorlevel% neq 0 exit /b %errorlevel% 44 | -------------------------------------------------------------------------------- /windows/scripts/prepare_dependencies.bat: -------------------------------------------------------------------------------- 1 | :: Copyright 2018 Google LLC 2 | :: 3 | :: Licensed under the Apache License, Version 2.0 (the "License"); 4 | :: you may not use this file except in compliance with the License. 5 | :: You may obtain a copy of the License at 6 | :: 7 | :: http://www.apache.org/licenses/LICENSE-2.0 8 | :: 9 | :: Unless required by applicable law or agreed to in writing, software 10 | :: distributed under the License is distributed on an "AS IS" BASIS, 11 | :: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | :: See the License for the specific language governing permissions and 13 | :: limitations under the License. 14 | @echo off 15 | 16 | set BUILD_MODE=%~1 17 | "%FLUTTER_ROOT%\packages\flutter_tools\bin\tool_backend" windows-x64 %BUILD_MODE% 18 | --------------------------------------------------------------------------------