├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ └── AndroidManifest.xml
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── optimized
│ │ │ │ │ └── cached
│ │ │ │ │ └── image
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── .last_build_id
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── 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
│ ├── plugin_example
│ │ ├── basic_content.dart
│ │ ├── grid_content.dart
│ │ └── list_content.dart
│ └── template
│ │ ├── globals.dart
│ │ └── info_page.dart
├── linux
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ │ └── CMakeLists.txt
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── macos
│ ├── .gitignore
│ ├── Flutter
│ │ ├── Flutter-Debug.xcconfig
│ │ ├── Flutter-Release.xcconfig
│ │ └── GeneratedPluginRegistrant.swift
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── app_icon_1024.png
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_256.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_512.png
│ │ │ └── app_icon_64.png
│ │ ├── Base.lproj
│ │ └── MainMenu.xib
│ │ ├── Configs
│ │ ├── AppInfo.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
├── pubspec.lock
├── pubspec.yaml
├── web
│ ├── favicon.png
│ ├── icons
│ │ ├── Icon-192.png
│ │ └── Icon-512.png
│ ├── index.html
│ └── manifest.json
└── windows
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ └── CMakeLists.txt
│ └── runner
│ ├── CMakeLists.txt
│ ├── Runner.rc
│ ├── flutter_window.cpp
│ ├── flutter_window.h
│ ├── main.cpp
│ ├── resource.h
│ ├── resources
│ └── app_icon.ico
│ ├── run_loop.cpp
│ ├── run_loop.h
│ ├── runner.exe.manifest
│ ├── utils.cpp
│ ├── utils.h
│ ├── win32_window.cpp
│ └── win32_window.h
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
└── optimized_cached_image.podspec
├── lib
├── optimized_cached_image.dart
└── src
│ ├── cache
│ ├── default_image_cache_manager.dart
│ └── image_cache_manager.dart
│ ├── debug_tools.dart
│ ├── image_provider
│ ├── _image_provider_io.dart
│ ├── _image_provider_web.dart
│ ├── _load_async_web.dart
│ ├── multi_image_stream_completer.dart
│ └── optimized_cached_image_provider.dart
│ ├── oci_widget.dart
│ └── transformer
│ ├── image_transformer.dart
│ └── scale_info.dart
├── optimized_cached_image.iml
├── pubspec.lock
├── pubspec.yaml
├── screenshots
└── streamed_vs_nonstreamed.jpg
└── scripts
└── checks.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 | .idea/
9 | doc/
10 | /.flutter-plugins
11 | /.flutter-plugins-dependencies
12 | /example/linux/flutter/generated_plugin_registrant.cc
13 | /example/windows/flutter/generated_plugin_registrant.cc
14 | /example/linux/flutter/generated_plugin_registrant.h
15 | /example/windows/flutter/generated_plugin_registrant.h
16 | /example/linux/flutter/generated_plugins.cmake
17 | /example/windows/flutter/generated_plugins.cmake
18 | /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
19 |
--------------------------------------------------------------------------------
/.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: 0b8abb4724aa590dd0f429683339b1e045a1594d
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 3.0.1
2 |
3 | * Avoid unnecessary null checks
4 |
5 | ## 3.0.0
6 |
7 | * Null safety and interim gif support.
8 |
9 | ## 2.0.2-alpha
10 |
11 | * Limited gif support. Gifs are compressed into webp and rendered as of now.
12 |
13 | ## 2.0.1
14 |
15 | * Update to null safety package dependencies. OCI still needs to migrate code to respect null
16 | safety. Additionally fix issue with hero widgets
17 |
18 | ## 2.0.0
19 |
20 | * Update flutter cache manager dependency and other pub dependencies
21 |
22 | ## 2.0.0-dev.2
23 |
24 | * Fix issues with file package conflicting with dart io.
25 |
26 | ## 2.0.0-dev.1
27 |
28 | * Update with flutter cache manager dependency and `CachedNetworkImage`. Introduces breaking api
29 | changes.
30 |
31 | ## 1.0.0
32 |
33 | * Release to Pub
34 |
35 | ## 1.0.0-rc1
36 |
37 | * Removed included octo_image library.
38 |
39 | ## 1.0.0-beta
40 |
41 | * Prevent unnecessary downloads from happening by caching the image from the original url and
42 | resizing it for different sizes.
43 |
44 | ## 0.1.15
45 |
46 | * Syncing changes with Cached Network Image
47 |
48 | ## 0.1.14
49 |
50 | * Fix open socket issue in compression library and allow debug mode.
51 |
52 | ## 0.1.13
53 |
54 | * Fixed an issue around compression error handling
55 |
56 | ## 0.1.12
57 |
58 | * Fix issue with stream closure, causing some stream to remain open.
59 |
60 | ## 0.1.11
61 |
62 | * Revert to original file when compression fails.
63 |
64 | ## 0.1.10
65 |
66 | * Migrate to latest apis in cache manager dependency. Now the stream fetching is done by default
67 | instead of via flag.
68 |
69 | ## 0.1.9
70 |
71 | * Fix transparency/image format issues.
72 |
73 | ## 0.1.8
74 |
75 | * Fix dependency version breaking change in flutter cache library.
76 |
77 | ## 0.1.7
78 |
79 | * Add style fixes
80 |
81 | ## 0.1.6
82 |
83 | * Add experimental support for streamed downloading via `useHttpStream` flag which further reduces
84 | the memory footprint.
85 |
86 | ## 0.1.5
87 |
88 | * Minor lint issues and formatting patched.
89 |
90 | ## 0.1.4
91 |
92 | * Fixed issue faced while specifying custom width and height.
93 |
94 | ## 0.1.3
95 |
96 | * Readme updated.
97 |
98 | ## 0.1.2
99 |
100 | * Minor lint issues and formatting patched.
101 |
102 | ## 0.1.1
103 |
104 | * Updated examples.
105 |
106 | ## 0.1.0
107 |
108 | * Minimalist/Core functionality of the library and health fixes.
109 |
110 | ## 0.0.1
111 |
112 | * Minimalist/Core functionality of the library.
113 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2020 Anvith Bhat
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Optimized Cached Image
2 |
3 | [](https://pub.dartlang.org/packages/optimized_cached_image)
4 |
5 | ### ❌❌ Important Update ❌❌ ###
6 | This library is no longer being maintained. When I started this library, it was meant to add memory performant extensions which could not be directly added into the parent library. Since then a lot has changed, [the parent library](https://github.com/Baseflow/flutter_cached_network_image) has incorporated similar changes, perhaps with the exception of a trivial `LayoutBuilder` that auto detects image sizes. I feel like this is a good time to deprecate this library in favour of the [parent](https://github.com/Baseflow/flutter_cached_network_image).
7 |
8 | ## Description
9 | A flutter library for loading images from network, resizing and caching them for memory sensitivity.
10 | This resizes and stores the images in cache based on parent container constraints and hence
11 | loads images of lower size into memory. This is heavily inspired by [cached_network_image](https://pub.dev/packages/cached_network_image) library.
12 |
13 | This library exposes two classes for loading images
14 | - `OptimizedCacheImage` which is a 1:1 mapping of `CachedNetworkImage`.
15 | - `OptimizedCacheImageProvider` which is a mapping of `CachedNetworkImageProvider`.
16 |
17 | ## How to use
18 | The OptimizedCacheImage can be used directly or through the ImageProvider.
19 | Both the OptimizedCacheImage as OptimizedCacheImageProvider have minimal support for web. It currently doesn't include caching.
20 |
21 | With a placeholder:
22 | ```dart
23 | OptimizedCacheImage(
24 | imageUrl: "http://via.placeholder.com/350x150",
25 | placeholder: (context, url) => CircularProgressIndicator(),
26 | errorWidget: (context, url, error) => Icon(Icons.error),
27 | ),
28 | ```
29 |
30 | Or with a progress indicator:
31 | ```dart
32 | OptimizedCacheImage(
33 | imageUrl: "http://via.placeholder.com/350x150",
34 | progressIndicatorBuilder: (context, url, downloadProgress) =>
35 | CircularProgressIndicator(value: downloadProgress.progress),
36 | errorWidget: (context, url, error) => Icon(Icons.error),
37 | ),
38 | ```
39 |
40 |
41 | ````dart
42 | Image(image: OptimizedCacheImageProvider(url))
43 | ````
44 |
45 | When you want to have both the placeholder functionality and want to get the imageprovider to use in another widget you can provide an imageBuilder:
46 | ```dart
47 | OptimizedCacheImage(
48 | imageUrl: "http://via.placeholder.com/200x150",
49 | imageBuilder: (context, imageProvider) => Container(
50 | decoration: BoxDecoration(
51 | image: DecorationImage(
52 | image: imageProvider,
53 | fit: BoxFit.cover,
54 | colorFilter:
55 | ColorFilter.mode(Colors.red, BlendMode.colorBurn)),
56 | ),
57 | ),
58 | placeholder: (context, url) => CircularProgressIndicator(),
59 | errorWidget: (context, url, error) => Icon(Icons.error),
60 | ),
61 | ```
62 |
63 | ## Handling Gifs
64 | OCI uses [Flutter Image Compress](https://pub.dev/packages/flutter_image_compress) as the compression library, while being memory efficient this library doesn't provide out of box support for gifs, however it does allow compressing to webp. Hence all gifs are compressed to webp format beginning `2.0.2-alpha`.
65 |
66 | ## How it works
67 | The optimized cached network images stores and retrieves files using the [flutter_cache_manager](https://pub.dev/packages/flutter_cache_manager).
68 |
69 | ## FAQ
70 | ### My app crashes when the image loading failed. (I know, this is not really a question.)
71 | Does it really crash though? The debugger might pause, as the Dart VM doesn't recognize it as a caught exception; the console might print errors; even your crash reporting tool might report it (I know, that really sucks). However, does it really crash? Probably everything is just running fine. If you really get an app crashes you are fine to report an issue, but do that with a small example so we can reproduce that crash.
72 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'anvith.in.optimized_cached_image'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.6.21'
6 | repositories {
7 | google()
8 | jcenter()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:4.1.3'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdkVersion 29
29 |
30 | sourceSets {
31 | main.java.srcDirs += 'src/main/kotlin'
32 | }
33 | defaultConfig {
34 | minSdkVersion 16
35 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
36 | }
37 | lintOptions {
38 | disable 'InvalidPackage'
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
44 | }
45 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'optimized_cached_image'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/example/.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 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 | ios/.symlinks
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Exceptions to above rules.
37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
38 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # optimized_cached_image_example
2 |
3 | Demonstrates how to use the optimized_cached_image plugin.
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 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | 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 31
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "optimized.cached.image"
42 | minSdkVersion 16
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
47 | }
48 |
49 | buildTypes {
50 | release {
51 | // TODO: Add your own signing config for the release build.
52 | // Signing with the debug keys for now, so `flutter run --release` works.
53 | signingConfig signingConfigs.debug
54 | }
55 | }
56 | }
57 |
58 | flutter {
59 | source '../..'
60 | }
61 |
62 | dependencies {
63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 | testImplementation 'junit:junit:4.12'
65 | androidTestImplementation 'androidx.test:runner:1.1.1'
66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
67 | }
68 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/optimized/cached/image/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package optimized.cached.image.example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity : FlutterActivity()
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.21'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.3'
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 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/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/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 | .symlinks/
--------------------------------------------------------------------------------
/example/ios/Flutter/.last_build_id:
--------------------------------------------------------------------------------
1 | 7f50a9631ab41028892fbb0801436462
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | 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 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_image_compress (0.0.1):
4 | - Flutter
5 | - Mantle
6 | - SDWebImage
7 | - SDWebImageWebPCoder
8 | - FMDB (2.7.5):
9 | - FMDB/standard (= 2.7.5)
10 | - FMDB/standard (2.7.5)
11 | - libwebp (1.1.0):
12 | - libwebp/demux (= 1.1.0)
13 | - libwebp/mux (= 1.1.0)
14 | - libwebp/webp (= 1.1.0)
15 | - libwebp/demux (1.1.0):
16 | - libwebp/webp
17 | - libwebp/mux (1.1.0):
18 | - libwebp/demux
19 | - libwebp/webp (1.1.0)
20 | - Mantle (2.1.1):
21 | - Mantle/extobjc (= 2.1.1)
22 | - Mantle/extobjc (2.1.1)
23 | - path_provider_ios (0.0.1):
24 | - Flutter
25 | - SDWebImage (5.5.2):
26 | - SDWebImage/Core (= 5.5.2)
27 | - SDWebImage/Core (5.5.2)
28 | - SDWebImageWebPCoder (0.5.0):
29 | - libwebp (~> 1.0)
30 | - SDWebImage/Core (~> 5.5)
31 | - sqflite (0.0.2):
32 | - Flutter
33 | - FMDB (>= 2.7.5)
34 | - url_launcher_ios (0.0.1):
35 | - Flutter
36 |
37 | DEPENDENCIES:
38 | - Flutter (from `Flutter`)
39 | - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`)
40 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
41 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
42 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
43 |
44 | SPEC REPOS:
45 | trunk:
46 | - FMDB
47 | - libwebp
48 | - Mantle
49 | - SDWebImage
50 | - SDWebImageWebPCoder
51 |
52 | EXTERNAL SOURCES:
53 | Flutter:
54 | :path: Flutter
55 | flutter_image_compress:
56 | :path: ".symlinks/plugins/flutter_image_compress/ios"
57 | path_provider_ios:
58 | :path: ".symlinks/plugins/path_provider_ios/ios"
59 | sqflite:
60 | :path: ".symlinks/plugins/sqflite/ios"
61 | url_launcher_ios:
62 | :path: ".symlinks/plugins/url_launcher_ios/ios"
63 |
64 | SPEC CHECKSUMS:
65 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
66 | flutter_image_compress: fd2b476345226e1a10ea352fa306af95704642c1
67 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
68 | libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
69 | Mantle: 35238ae6f2e2b2d474fa7b67fee82a59fea71915
70 | path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
71 | SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca
72 | SDWebImageWebPCoder: e7ae855f058e3dcae99696920b6a5d134e9dcddf
73 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
74 | url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
75 |
76 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
77 |
78 | COCOAPODS: 1.11.3
79 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
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 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | OCI Example
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 | CADisableMinimumFrameDurationOnPhone
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'template/globals.dart';
4 |
5 | void main() {
6 | runApp(HumblerookiePluginExample());
7 | }
8 |
9 | /// A Flutter application demonstrating the functionality of this plugin
10 | class HumblerookiePluginExample extends StatelessWidget {
11 | /// [MaterialColor] to be used in the app [ThemeData]
12 | final MaterialColor themeMaterialColor =
13 | createMaterialColor(const Color.fromRGBO(48, 49, 60, 1));
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return MaterialApp(
18 | title: 'Humblerookie $pluginName',
19 | theme: ThemeData(
20 | buttonTheme: ButtonThemeData(
21 | buttonColor: themeMaterialColor.shade500,
22 | disabledColor: themeMaterialColor.withRed(200),
23 | splashColor: themeMaterialColor.shade50,
24 | textTheme: ButtonTextTheme.primary,
25 | ),
26 | hintColor: themeMaterialColor.shade500,
27 | textTheme: TextTheme(
28 | bodyLarge: TextStyle(
29 | color: Colors.white,
30 | fontSize: 16,
31 | height: 1.3,
32 | ),
33 | bodyMedium: TextStyle(
34 | color: Colors.white,
35 | fontSize: 18,
36 | height: 1.2,
37 | ),
38 | labelLarge: TextStyle(color: Colors.white),
39 | displayLarge: TextStyle(
40 | color: Colors.white,
41 | fontSize: 18,
42 | ),
43 | ),
44 | visualDensity: VisualDensity.adaptivePlatformDensity,
45 | inputDecorationTheme: const InputDecorationTheme(
46 | fillColor: Color.fromRGBO(37, 37, 37, 1),
47 | filled: true,
48 | ),
49 | bottomAppBarTheme:
50 | BottomAppBarTheme(color: const Color.fromRGBO(57, 58, 71, 1)),
51 | colorScheme: ColorScheme.fromSwatch(
52 | primarySwatch:
53 | createMaterialColor(const Color.fromRGBO(48, 49, 60, 1)))
54 | .copyWith(background: const Color.fromRGBO(48, 49, 60, 0.8))
55 | .copyWith(secondary: Colors.white60),
56 | ),
57 | home: AppHome(title: 'Baseflow $pluginName example app'),
58 | );
59 | }
60 |
61 | /// Creates a [MaterialColor] based on the supplied [Color]
62 | static MaterialColor createMaterialColor(Color color) {
63 | List strengths = [.05];
64 | Map swatch = {};
65 | final r = color.red, g = color.green, b = color.blue;
66 |
67 | for (var i = 1; i < 10; i++) {
68 | strengths.add(0.1 * i);
69 | }
70 | for (var strength in strengths) {
71 | final ds = 0.5 - strength;
72 | swatch[(strength * 1000).round()] = Color.fromRGBO(
73 | r + ((ds < 0 ? r : (255 - r)) * ds).round(),
74 | g + ((ds < 0 ? g : (255 - g)) * ds).round(),
75 | b + ((ds < 0 ? b : (255 - b)) * ds).round(),
76 | 1,
77 | );
78 | }
79 | return MaterialColor(color.value, swatch as Map);
80 | }
81 | }
82 |
83 | /// A Flutter example demonstrating how the [pluginName] plugin could be used
84 | class AppHome extends StatefulWidget {
85 | /// Constructs the [AppHome] class
86 | AppHome({Key? key, this.title}) : super(key: key);
87 |
88 | /// The [title] of the application, which is shown in the application's
89 | /// title bar.
90 | final String? title;
91 |
92 | @override
93 | _AppHomeState createState() => _AppHomeState();
94 | }
95 |
96 | class _AppHomeState extends State {
97 | static final PageController _pageController = PageController(initialPage: 0);
98 | int _currentPage = 0;
99 |
100 | @override
101 | Widget build(BuildContext context) {
102 | return Scaffold(
103 | appBar: AppBar(
104 | backgroundColor: Theme.of(context).bottomAppBarTheme.color,
105 | title: Center(
106 | child: Text("OptimizedCachedImage"),
107 | ),
108 | ),
109 | backgroundColor: Theme.of(context).colorScheme.background,
110 | body: PageView(
111 | controller: _pageController,
112 | children: pages,
113 | onPageChanged: (page) {
114 | setState(() {
115 | _currentPage = page;
116 | });
117 | },
118 | ),
119 | bottomNavigationBar: _bottomAppBar(),
120 | );
121 | }
122 |
123 | BottomAppBar _bottomAppBar() {
124 | return BottomAppBar(
125 | elevation: 5,
126 | color: Theme.of(context).bottomAppBarTheme.color,
127 | child: Row(
128 | mainAxisSize: MainAxisSize.max,
129 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
130 | children: List.unmodifiable(() sync* {
131 | for (var i = 0; i < pages.length; i++) {
132 | yield Expanded(
133 | child: IconButton(
134 | iconSize: 30,
135 | icon: Icon(icons.elementAt(i)),
136 | color: _bottomAppBarIconColor(i),
137 | onPressed: () => _animateToPage(i),
138 | ),
139 | );
140 | }
141 | }()),
142 | ),
143 | );
144 | }
145 |
146 | void _animateToPage(int page) {
147 | _pageController.animateToPage(page,
148 | duration: const Duration(milliseconds: 200), curve: Curves.linear);
149 | }
150 |
151 | Color _bottomAppBarIconColor(int page) {
152 | return _currentPage == page
153 | ? Colors.white
154 | : Theme.of(context).colorScheme.secondary;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/example/lib/plugin_example/basic_content.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_blurhash/flutter_blurhash.dart';
3 | import 'package:optimized_cached_image/optimized_cached_image.dart';
4 |
5 | class BasicContent extends StatelessWidget {
6 | @override
7 | Widget build(BuildContext context) {
8 | return SingleChildScrollView(
9 | child: Center(
10 | child: Column(
11 | mainAxisAlignment: MainAxisAlignment.center,
12 | children: [
13 | _blurHashImage(),
14 | _sizedContainer(
15 | const Image(
16 | image: OptimizedCacheImageProvider(
17 | "https://sample-videos.com/gif/3.gif",
18 | ),
19 | ),
20 | ),
21 | _sizedContainer(
22 | OptimizedCacheImage(
23 | progressIndicatorBuilder: (context, url, progress) => Center(
24 | child: CircularProgressIndicator(
25 | value: progress.progress,
26 | ),
27 | ),
28 | imageUrl:
29 | 'https://images.unsplash.com/photo-1532264523420-881a47db012d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9',
30 | ),
31 | ),
32 | _sizedContainer(
33 | OptimizedCacheImage(
34 | placeholder: (context, url) =>
35 | const CircularProgressIndicator(),
36 | imageUrl: 'https://via.placeholder.com/200x150',
37 | ),
38 | ),
39 | _sizedContainer(
40 | OptimizedCacheImage(
41 | imageUrl: 'https://via.placeholder.com/300x150',
42 | imageBuilder: (context, imageProvider) => Container(
43 | decoration: BoxDecoration(
44 | image: DecorationImage(
45 | image: imageProvider,
46 | fit: BoxFit.cover,
47 | colorFilter: const ColorFilter.mode(
48 | Colors.red,
49 | BlendMode.colorBurn,
50 | ),
51 | ),
52 | ),
53 | ),
54 | placeholder: (context, url) =>
55 | const CircularProgressIndicator(),
56 | errorWidget: (context, url, error) => const Icon(Icons.error),
57 | ),
58 | ),
59 | OptimizedCacheImage(
60 | imageUrl: 'https://via.placeholder.com/300x300',
61 | placeholder: (context, url) => const CircleAvatar(
62 | backgroundColor: Colors.amber,
63 | radius: 150,
64 | ),
65 | imageBuilder: (context, image) => CircleAvatar(
66 | backgroundImage: image,
67 | radius: 150,
68 | ),
69 | ),
70 | _sizedContainer(
71 | OptimizedCacheImage(
72 | imageUrl: 'https://notAvalid.uri',
73 | placeholder: (context, url) =>
74 | const CircularProgressIndicator(),
75 | errorWidget: (context, url, error) => const Icon(Icons.error),
76 | ),
77 | ),
78 | _sizedContainer(
79 | OptimizedCacheImage(
80 | imageUrl: 'not a uri at all',
81 | placeholder: (context, url) =>
82 | const CircularProgressIndicator(),
83 | errorWidget: (context, url, error) => const Icon(Icons.error),
84 | ),
85 | ),
86 | _sizedContainer(
87 | OptimizedCacheImage(
88 | maxHeightDiskCache: 10,
89 | imageUrl: 'https://via.placeholder.com/350x200',
90 | placeholder: (context, url) =>
91 | const CircularProgressIndicator(),
92 | errorWidget: (context, url, error) => const Icon(Icons.error),
93 | fadeOutDuration: const Duration(seconds: 1),
94 | fadeInDuration: const Duration(seconds: 3),
95 | ),
96 | ),
97 | ],
98 | ),
99 | ),
100 | );
101 | }
102 |
103 | Widget _blurHashImage() {
104 | return SizedBox(
105 | width: double.infinity,
106 | child: OptimizedCacheImage(
107 | placeholder: (context, url) => const AspectRatio(
108 | aspectRatio: 1.6,
109 | child: BlurHash(hash: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj'),
110 | ),
111 | imageUrl: 'https://blurha.sh/assets/images/img1.jpg',
112 | fit: BoxFit.cover,
113 | ),
114 | );
115 | }
116 |
117 | Widget _sizedContainer(Widget child) {
118 | return SizedBox(
119 | width: 300.0,
120 | height: 150.0,
121 | child: Center(child: child),
122 | );
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/example/lib/plugin_example/grid_content.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:optimized_cached_image/optimized_cached_image.dart';
3 |
4 | /// Demonstrates a [GridView] containing [OptimizedCacheImage]
5 | class GridContent extends StatelessWidget {
6 | @override
7 | Widget build(BuildContext context) {
8 | return GridView.builder(
9 | itemCount: 10,
10 | gridDelegate:
11 | const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
12 | itemBuilder: (BuildContext context, int index) => OptimizedCacheImage(
13 | imageUrl: 'https://loremflickr.com/100/100/music?lock=$index',
14 | placeholder: _loader,
15 | errorWidget: _error,
16 | ),
17 | );
18 | }
19 |
20 | Widget _loader(BuildContext context, String url) {
21 | return const Center(
22 | child: CircularProgressIndicator(),
23 | );
24 | }
25 |
26 | Widget _error(BuildContext context, String url, dynamic error) {
27 | print(error);
28 | return const Center(child: Icon(Icons.error));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/example/lib/plugin_example/list_content.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:optimized_cached_image/optimized_cached_image.dart';
3 |
4 | /// Demonstrates a [ListView] containing [OptimizedCacheImage]
5 | class ListContent extends StatelessWidget {
6 | @override
7 | Widget build(BuildContext context) {
8 | return ListView.builder(
9 | itemBuilder: (BuildContext context, int index) => Card(
10 | child: Column(
11 | children: [
12 | OptimizedCacheImage(
13 | imageUrl: 'https://loremflickr.com/320/240/music?lock=$index',
14 | placeholder: (BuildContext context, String url) => Container(
15 | width: 320,
16 | height: 240,
17 | color: Colors.purple,
18 | ),
19 | ),
20 | ],
21 | ),
22 | ),
23 | itemCount: 10,
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/example/lib/template/globals.dart:
--------------------------------------------------------------------------------
1 | import 'dart:core';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import '../plugin_example/basic_content.dart';
6 | import '../plugin_example/grid_content.dart';
7 | import '../plugin_example/list_content.dart';
8 | import 'info_page.dart';
9 |
10 | /// The name of the plugin, which will be displayed throughout the example App.
11 | const String pluginName = 'OptimizedCachedImage';
12 |
13 | /// Returns Github URL, which is shown in the [InfoPage].
14 | const String githubURL =
15 | 'https://github.com/humblerookie/optimized_cached_image';
16 |
17 | /// Returns Baseflow URL, which is shown in the [InfoPage].
18 | const String baseflowURL = 'https://anvith.dev';
19 |
20 | /// Returns pub.dev URL, which is shown in the [InfoPage].
21 | const String pubDevURL = 'https://pub.dev/packages/optimized_cached_image';
22 |
23 | /// [EdgeInsets] to define horizontal padding throughout the application.
24 | const EdgeInsets defaultHorizontalPadding =
25 | EdgeInsets.symmetric(horizontal: 24);
26 |
27 | /// [EdgeInsets] to define vertical padding throughout the application.
28 | const EdgeInsets defaultVerticalPadding = EdgeInsets.symmetric(vertical: 24);
29 |
30 | /// Returns a [List] with [IconData] to show in the [AppHome] [AppBar].
31 | final List icons = [
32 | Icons.image,
33 | Icons.list,
34 | Icons.grid_on,
35 | Icons.info_outline,
36 | ];
37 |
38 | /// Returns a [List] with [Widget]s to construct pages in the [AppBar].
39 | final List pages = [
40 | BasicContent(),
41 | ListContent(),
42 | GridContent(),
43 | InfoPage(),
44 | ];
45 |
--------------------------------------------------------------------------------
/example/lib/template/info_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:url_launcher/url_launcher.dart';
3 |
4 | import 'globals.dart';
5 |
6 | /// [StatelessWidget] displaying information about Baseflow
7 | class InfoPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return SizedBox.expand(
11 | child: Align(
12 | alignment: Alignment.bottomCenter,
13 | child: SingleChildScrollView(
14 | child: Padding(
15 | padding: defaultHorizontalPadding + defaultVerticalPadding,
16 | child: Column(
17 | mainAxisSize: MainAxisSize.max,
18 | mainAxisAlignment: MainAxisAlignment.end,
19 | children: [
20 | Align(
21 | alignment: Alignment.centerLeft,
22 | child: Text("OptimizedCachedImage by humblerookie"),
23 | ),
24 | const Padding(
25 | padding: EdgeInsets.symmetric(vertical: 24),
26 | ),
27 | Text(
28 | 'This app showcases the possibilities of the $pluginName '
29 | 'plugin. '
30 | 'This plugin is available as open source project on Github. '
31 | '\n\n'
32 | 'Need help with integrating functionalities within your own '
33 | 'apps? Contact us at anvithv4@gmail.com',
34 | style: Theme.of(context).textTheme.bodyLarge,
35 | ),
36 | const Padding(
37 | padding: EdgeInsets.symmetric(vertical: 8),
38 | ),
39 | _launcherRaisedButton(
40 | 'Find us on Github',
41 | githubURL,
42 | context,
43 | ),
44 | _launcherRaisedButton(
45 | 'Find us on pub.dev',
46 | pubDevURL,
47 | context,
48 | ),
49 | _launcherRaisedButton(
50 | 'Visit anvith.dev',
51 | baseflowURL,
52 | context,
53 | ),
54 | const Padding(
55 | padding: EdgeInsets.only(bottom: 30),
56 | ),
57 | ],
58 | ),
59 | ),
60 | ),
61 | ),
62 | );
63 | }
64 |
65 | Widget _launcherRaisedButton(String text, String url, BuildContext context) {
66 | return Container(
67 | width: MediaQuery.of(context).size.width,
68 | height: 50,
69 | margin: const EdgeInsets.only(top: 24.0),
70 | alignment: Alignment.center,
71 | child: SizedBox.expand(
72 | child: ElevatedButton(
73 | child: Text(text),
74 | onPressed: () => _launchURL(url),
75 | ),
76 | ),
77 | );
78 | }
79 |
80 | Future _launchURL(String url) async {
81 | final uri = Uri.tryParse(url);
82 | if (uri != null) {
83 | await launchUrl(uri);
84 | } else {
85 | throw 'Could not launch $url';
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/example/linux/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral
2 |
--------------------------------------------------------------------------------
/example/linux/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(runner LANGUAGES CXX)
3 |
4 | set(BINARY_NAME "example")
5 | set(APPLICATION_ID "com.example.example")
6 |
7 | cmake_policy(SET CMP0063 NEW)
8 |
9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
10 |
11 | # Configure build options.
12 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
13 | set(CMAKE_BUILD_TYPE "Debug" CACHE
14 | STRING "Flutter build mode" FORCE)
15 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
16 | "Debug" "Profile" "Release")
17 | endif()
18 |
19 | # Compilation settings that should be applied to most targets.
20 | function(APPLY_STANDARD_SETTINGS TARGET)
21 | target_compile_features(${TARGET} PUBLIC cxx_std_14)
22 | target_compile_options(${TARGET} PRIVATE -Wall -Werror)
23 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
24 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
25 | endfunction()
26 |
27 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
28 |
29 | # Flutter library and tool build rules.
30 | add_subdirectory(${FLUTTER_MANAGED_DIR})
31 |
32 | # System-level dependencies.
33 | find_package(PkgConfig REQUIRED)
34 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
35 |
36 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
37 |
38 | # Application build
39 | add_executable(${BINARY_NAME}
40 | "main.cc"
41 | "my_application.cc"
42 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
43 | )
44 | apply_standard_settings(${BINARY_NAME})
45 | target_link_libraries(${BINARY_NAME} PRIVATE flutter)
46 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
47 | add_dependencies(${BINARY_NAME} flutter_assemble)
48 | # Only the install-generated bundle's copy of the executable will launch
49 | # correctly, since the resources must in the right relative locations. To avoid
50 | # people trying to run the unbundled copy, put it in a subdirectory instead of
51 | # the default top-level location.
52 | set_target_properties(${BINARY_NAME}
53 | PROPERTIES
54 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
55 | )
56 |
57 | # Generated plugin build rules, which manage building the plugins and adding
58 | # them to the application.
59 | include(flutter/generated_plugins.cmake)
60 |
61 |
62 | # === Installation ===
63 | # By default, "installing" just makes a relocatable bundle in the build
64 | # directory.
65 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
66 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
67 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
68 | endif()
69 |
70 | # Start with a clean build bundle directory every time.
71 | install(CODE "
72 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
73 | " COMPONENT Runtime)
74 |
75 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
76 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
77 |
78 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
79 | COMPONENT Runtime)
80 |
81 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
82 | COMPONENT Runtime)
83 |
84 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
85 | COMPONENT Runtime)
86 |
87 | if(PLUGIN_BUNDLED_LIBRARIES)
88 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
89 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
90 | COMPONENT Runtime)
91 | endif()
92 |
93 | # Fully re-copy the assets directory on each build to avoid having stale files
94 | # from a previous install.
95 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
96 | install(CODE "
97 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
98 | " COMPONENT Runtime)
99 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
100 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
101 |
102 | # Install the AOT library on non-Debug builds only.
103 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
104 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
105 | COMPONENT Runtime)
106 | endif()
107 |
--------------------------------------------------------------------------------
/example/linux/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 |
3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
4 |
5 | # Configuration provided via flutter tool.
6 | include(${EPHEMERAL_DIR}/generated_config.cmake)
7 |
8 | # TODO: Move the rest of this into files in ephemeral. See
9 | # https://github.com/flutter/flutter/issues/57146.
10 |
11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...),
12 | # which isn't available in 3.10.
13 | function(list_prepend LIST_NAME PREFIX)
14 | set(NEW_LIST "")
15 | foreach(element ${${LIST_NAME}})
16 | list(APPEND NEW_LIST "${PREFIX}${element}")
17 | endforeach(element)
18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
19 | endfunction()
20 |
21 | # === Flutter Library ===
22 | # System-level dependencies.
23 | find_package(PkgConfig REQUIRED)
24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
27 | pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
28 |
29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
30 |
31 | # Published to parent scope for install step.
32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
36 |
37 | list(APPEND FLUTTER_LIBRARY_HEADERS
38 | "fl_basic_message_channel.h"
39 | "fl_binary_codec.h"
40 | "fl_binary_messenger.h"
41 | "fl_dart_project.h"
42 | "fl_engine.h"
43 | "fl_json_message_codec.h"
44 | "fl_json_method_codec.h"
45 | "fl_message_codec.h"
46 | "fl_method_call.h"
47 | "fl_method_channel.h"
48 | "fl_method_codec.h"
49 | "fl_method_response.h"
50 | "fl_plugin_registrar.h"
51 | "fl_plugin_registry.h"
52 | "fl_standard_message_codec.h"
53 | "fl_standard_method_codec.h"
54 | "fl_string_codec.h"
55 | "fl_value.h"
56 | "fl_view.h"
57 | "flutter_linux.h"
58 | )
59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
60 | add_library(flutter INTERFACE)
61 | target_include_directories(flutter INTERFACE
62 | "${EPHEMERAL_DIR}"
63 | )
64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
65 | target_link_libraries(flutter INTERFACE
66 | PkgConfig::GTK
67 | PkgConfig::GLIB
68 | PkgConfig::GIO
69 | PkgConfig::BLKID
70 | )
71 | add_dependencies(flutter flutter_assemble)
72 |
73 | # === Flutter tool backend ===
74 | # _phony_ is a non-existent file to force this command to run every time,
75 | # since currently there's no way to get a full input/output list from the
76 | # flutter tool.
77 | add_custom_command(
78 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
79 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_
80 | COMMAND ${CMAKE_COMMAND} -E env
81 | ${FLUTTER_TOOL_ENVIRONMENT}
82 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
83 | linux-x64 ${CMAKE_BUILD_TYPE}
84 | )
85 | add_custom_target(flutter_assemble DEPENDS
86 | "${FLUTTER_LIBRARY}"
87 | ${FLUTTER_LIBRARY_HEADERS}
88 | )
89 |
--------------------------------------------------------------------------------
/example/linux/main.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | int main(int argc, char** argv) {
4 | // Only X11 is currently supported.
5 | // Wayland support is being developed: https://github.com/flutter/flutter/issues/57932.
6 | gdk_set_allowed_backends("x11");
7 |
8 | g_autoptr(MyApplication) app = my_application_new();
9 | return g_application_run(G_APPLICATION(app), argc, argv);
10 | }
11 |
--------------------------------------------------------------------------------
/example/linux/my_application.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | struct _MyApplication {
8 | GtkApplication parent_instance;
9 | };
10 |
11 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
12 |
13 | // Implements GApplication::activate.
14 | static void my_application_activate(GApplication* application) {
15 | GtkWindow* window =
16 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
17 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
18 | gtk_widget_show(GTK_WIDGET(header_bar));
19 | gtk_header_bar_set_title(header_bar, "example");
20 | gtk_header_bar_set_show_close_button(header_bar, TRUE);
21 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
22 | gtk_window_set_default_size(window, 1280, 720);
23 | gtk_widget_show(GTK_WIDGET(window));
24 |
25 | g_autoptr(FlDartProject) project = fl_dart_project_new();
26 |
27 | FlView* view = fl_view_new(project);
28 | gtk_widget_show(GTK_WIDGET(view));
29 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
30 |
31 | fl_register_plugins(FL_PLUGIN_REGISTRY(view));
32 |
33 | gtk_widget_grab_focus(GTK_WIDGET(view));
34 | }
35 |
36 | static void my_application_class_init(MyApplicationClass* klass) {
37 | G_APPLICATION_CLASS(klass)->activate = my_application_activate;
38 | }
39 |
40 | static void my_application_init(MyApplication* self) {}
41 |
42 | MyApplication* my_application_new() {
43 | return MY_APPLICATION(g_object_new(my_application_get_type(),
44 | "application-id", APPLICATION_ID,
45 | nullptr));
46 | }
47 |
--------------------------------------------------------------------------------
/example/linux/my_application.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_MY_APPLICATION_H_
2 | #define FLUTTER_MY_APPLICATION_H_
3 |
4 | #include
5 |
6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
7 | GtkApplication)
8 |
9 | /**
10 | * my_application_new:
11 | *
12 | * Creates a new Flutter-based application.
13 | *
14 | * Returns: a new #MyApplication.
15 | */
16 | MyApplication* my_application_new();
17 |
18 | #endif // FLUTTER_MY_APPLICATION_H_
19 |
--------------------------------------------------------------------------------
/example/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/xcuserdata/
7 |
--------------------------------------------------------------------------------
/example/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import path_provider_macos
9 | import sqflite
10 | import url_launcher_macos
11 |
12 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
13 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
14 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
15 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
16 | }
17 |
--------------------------------------------------------------------------------
/example/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.11'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def parse_KV_file(file, separator='=')
13 | file_abs_path = File.expand_path(file)
14 | if !File.exists? file_abs_path
15 | return [];
16 | end
17 | pods_ary = []
18 | skip_line_start_symbols = ["#", "/"]
19 | File.foreach(file_abs_path) { |line|
20 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
21 | plugin = line.split(pattern=separator)
22 | if plugin.length == 2
23 | podname = plugin[0].strip()
24 | path = plugin[1].strip()
25 | podpath = File.expand_path("#{path}", file_abs_path)
26 | pods_ary.push({:name => podname, :path => podpath});
27 | else
28 | puts "Invalid plugin specification: #{line}"
29 | end
30 | }
31 | return pods_ary
32 | end
33 |
34 | def pubspec_supports_macos(file)
35 | file_abs_path = File.expand_path(file)
36 | if !File.exists? file_abs_path
37 | return false;
38 | end
39 | File.foreach(file_abs_path) { |line|
40 | return true if line =~ /^\s*macos:/
41 | }
42 | return false
43 | end
44 |
45 | target 'Runner' do
46 | use_frameworks!
47 | use_modular_headers!
48 |
49 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
50 | # referring to absolute paths on developers' machines.
51 | ephemeral_dir = File.join('Flutter', 'ephemeral')
52 | symlink_dir = File.join(ephemeral_dir, '.symlinks')
53 | symlink_plugins_dir = File.join(symlink_dir, 'plugins')
54 | system("rm -rf #{symlink_dir}")
55 | system("mkdir -p #{symlink_plugins_dir}")
56 |
57 | # Flutter Pods
58 | generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig'))
59 | if generated_xcconfig.empty?
60 | puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
61 | end
62 | generated_xcconfig.map { |p|
63 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
64 | symlink = File.join(symlink_dir, 'flutter')
65 | File.symlink(File.dirname(p[:path]), symlink)
66 | pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path]))
67 | end
68 | }
69 |
70 | # Plugin Pods
71 | plugin_pods = parse_KV_file('../.flutter-plugins')
72 | plugin_pods.map { |p|
73 | symlink = File.join(symlink_plugins_dir, p[:name])
74 | File.symlink(p[:path], symlink)
75 | if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml'))
76 | pod p[:name], :path => File.join(symlink, 'macos')
77 | end
78 | }
79 | end
80 |
81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
82 | install! 'cocoapods', :disable_input_output_paths => true
83 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "app_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "app_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "app_icon_32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "app_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "app_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "app_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "app_icon_256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "app_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "app_icon_512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "app_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = example
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2020 com.example. All rights reserved.
15 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/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 |
--------------------------------------------------------------------------------
/example/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.network.server
10 |
11 | com.apple.security.network.client
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/macos/Runner/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 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | $(PRODUCT_COPYRIGHT)
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController.init()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | sha256: eb33140ede1b4039f4ad631f7bf3cfa58e24514e8bf87184bc32f17541af87fc
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "3.3.0"
12 | async:
13 | dependency: transitive
14 | description:
15 | name: async
16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.11.0"
20 | boolean_selector:
21 | dependency: transitive
22 | description:
23 | name: boolean_selector
24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "2.1.1"
28 | characters:
29 | dependency: transitive
30 | description:
31 | name: characters
32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.3.0"
36 | clock:
37 | dependency: transitive
38 | description:
39 | name: clock
40 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.1.1"
44 | collection:
45 | dependency: transitive
46 | description:
47 | name: collection
48 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.17.1"
52 | cross_file:
53 | dependency: transitive
54 | description:
55 | name: cross_file
56 | sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "0.3.3+4"
60 | crypto:
61 | dependency: transitive
62 | description:
63 | name: crypto
64 | sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
65 | url: "https://pub.dev"
66 | source: hosted
67 | version: "3.0.2"
68 | cupertino_icons:
69 | dependency: "direct main"
70 | description:
71 | name: cupertino_icons
72 | sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5"
73 | url: "https://pub.dev"
74 | source: hosted
75 | version: "1.0.4"
76 | fake_async:
77 | dependency: transitive
78 | description:
79 | name: fake_async
80 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
81 | url: "https://pub.dev"
82 | source: hosted
83 | version: "1.3.1"
84 | ffi:
85 | dependency: transitive
86 | description:
87 | name: ffi
88 | sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18"
89 | url: "https://pub.dev"
90 | source: hosted
91 | version: "1.2.1"
92 | file:
93 | dependency: "direct main"
94 | description:
95 | name: file
96 | sha256: b69516f2c26a5bcac4eee2e32512e1a5205ab312b3536c1c1227b2b942b5f9ad
97 | url: "https://pub.dev"
98 | source: hosted
99 | version: "6.1.2"
100 | flutter:
101 | dependency: "direct main"
102 | description: flutter
103 | source: sdk
104 | version: "0.0.0"
105 | flutter_blurhash:
106 | dependency: transitive
107 | description:
108 | name: flutter_blurhash
109 | sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6"
110 | url: "https://pub.dev"
111 | source: hosted
112 | version: "0.7.0"
113 | flutter_cache_manager:
114 | dependency: transitive
115 | description:
116 | name: flutter_cache_manager
117 | sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
118 | url: "https://pub.dev"
119 | source: hosted
120 | version: "3.3.0"
121 | flutter_image_compress:
122 | dependency: transitive
123 | description:
124 | name: flutter_image_compress
125 | sha256: "2babae2c69ee4849c3d3ae0f1f10c14a6b8132390e4583c5d3b1f02e8167a022"
126 | url: "https://pub.dev"
127 | source: hosted
128 | version: "2.0.3"
129 | flutter_image_compress_common:
130 | dependency: transitive
131 | description:
132 | name: flutter_image_compress_common
133 | sha256: "88aae2219cd4507992643f19aee7882fe8ff82375ffa8cb4c876e3bfe3e7c3b6"
134 | url: "https://pub.dev"
135 | source: hosted
136 | version: "1.0.1"
137 | flutter_image_compress_platform_interface:
138 | dependency: transitive
139 | description:
140 | name: flutter_image_compress_platform_interface
141 | sha256: dae0a3eb1fb7f38cf327cc2005dc287bc891becdd83c519277de4415fd9e065d
142 | url: "https://pub.dev"
143 | source: hosted
144 | version: "1.0.1"
145 | flutter_image_compress_web:
146 | dependency: transitive
147 | description:
148 | name: flutter_image_compress_web
149 | sha256: "677f02509bdf5fd71afb560a22f0444e82c9ee887d223cd006cb44fd145fcb17"
150 | url: "https://pub.dev"
151 | source: hosted
152 | version: "0.1.3"
153 | flutter_test:
154 | dependency: "direct dev"
155 | description: flutter
156 | source: sdk
157 | version: "0.0.0"
158 | flutter_web_plugins:
159 | dependency: transitive
160 | description: flutter
161 | source: sdk
162 | version: "0.0.0"
163 | http:
164 | dependency: transitive
165 | description:
166 | name: http
167 | sha256: "2ed163531e071c2c6b7c659635112f24cb64ecbebf6af46b550d536c0b1aa112"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "0.13.4"
171 | http_parser:
172 | dependency: transitive
173 | description:
174 | name: http_parser
175 | sha256: db3060f22889f3d9d55f6a217565486737037eec3609f7f3eca4d0c67ee0d8a0
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "4.0.1"
179 | image:
180 | dependency: transitive
181 | description:
182 | name: image
183 | sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "4.0.17"
187 | js:
188 | dependency: transitive
189 | description:
190 | name: js
191 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "0.6.7"
195 | matcher:
196 | dependency: transitive
197 | description:
198 | name: matcher
199 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
200 | url: "https://pub.dev"
201 | source: hosted
202 | version: "0.12.15"
203 | material_color_utilities:
204 | dependency: transitive
205 | description:
206 | name: material_color_utilities
207 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
208 | url: "https://pub.dev"
209 | source: hosted
210 | version: "0.2.0"
211 | meta:
212 | dependency: transitive
213 | description:
214 | name: meta
215 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
216 | url: "https://pub.dev"
217 | source: hosted
218 | version: "1.9.1"
219 | octo_image:
220 | dependency: transitive
221 | description:
222 | name: octo_image
223 | sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143"
224 | url: "https://pub.dev"
225 | source: hosted
226 | version: "1.0.2"
227 | optimized_cached_image:
228 | dependency: "direct main"
229 | description:
230 | path: ".."
231 | relative: true
232 | source: path
233 | version: "3.0.1"
234 | path:
235 | dependency: transitive
236 | description:
237 | name: path
238 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
239 | url: "https://pub.dev"
240 | source: hosted
241 | version: "1.8.3"
242 | path_provider:
243 | dependency: transitive
244 | description:
245 | name: path_provider
246 | sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
247 | url: "https://pub.dev"
248 | source: hosted
249 | version: "2.0.15"
250 | path_provider_android:
251 | dependency: transitive
252 | description:
253 | name: path_provider_android
254 | sha256: dfaa152e93c3a6fec632482928c770b2156dfb873582e99fbd6ac3b3de651d4c
255 | url: "https://pub.dev"
256 | source: hosted
257 | version: "2.0.14"
258 | path_provider_foundation:
259 | dependency: transitive
260 | description:
261 | name: path_provider_foundation
262 | sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3"
263 | url: "https://pub.dev"
264 | source: hosted
265 | version: "2.2.3"
266 | path_provider_linux:
267 | dependency: transitive
268 | description:
269 | name: path_provider_linux
270 | sha256: "367b9311fe9ce1421215bcc37dce9bde57b6640c7b790cee1974c2b0a691e074"
271 | url: "https://pub.dev"
272 | source: hosted
273 | version: "2.1.6"
274 | path_provider_platform_interface:
275 | dependency: transitive
276 | description:
277 | name: path_provider_platform_interface
278 | sha256: "27dc7a224fcd07444cb5e0e60423ccacea3e13cf00fc5282ac2c918132da931d"
279 | url: "https://pub.dev"
280 | source: hosted
281 | version: "2.0.4"
282 | path_provider_windows:
283 | dependency: transitive
284 | description:
285 | name: path_provider_windows
286 | sha256: "62dbb1bc45f1e7ba1094c9dd8ea46bdcffc254db7354b4988cb9326c9d2efcdd"
287 | url: "https://pub.dev"
288 | source: hosted
289 | version: "2.0.6"
290 | pedantic:
291 | dependency: transitive
292 | description:
293 | name: pedantic
294 | sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
295 | url: "https://pub.dev"
296 | source: hosted
297 | version: "1.11.1"
298 | petitparser:
299 | dependency: transitive
300 | description:
301 | name: petitparser
302 | sha256: "2ebb289dc4764ec397f5cd3ca9881c6d17196130a7d646ed022a0dd9c2e25a71"
303 | url: "https://pub.dev"
304 | source: hosted
305 | version: "5.0.0"
306 | platform:
307 | dependency: transitive
308 | description:
309 | name: platform
310 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
311 | url: "https://pub.dev"
312 | source: hosted
313 | version: "3.1.0"
314 | plugin_platform_interface:
315 | dependency: transitive
316 | description:
317 | name: plugin_platform_interface
318 | sha256: "075f927ebbab4262ace8d0b283929ac5410c0ac4e7fc123c76429564facfb757"
319 | url: "https://pub.dev"
320 | source: hosted
321 | version: "2.1.2"
322 | process:
323 | dependency: transitive
324 | description:
325 | name: process
326 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
327 | url: "https://pub.dev"
328 | source: hosted
329 | version: "4.2.4"
330 | rxdart:
331 | dependency: transitive
332 | description:
333 | name: rxdart
334 | sha256: bc2d2b17b87fab32e2dca53ca3066d3147de6f96c74d76cfe1a379a24239c46d
335 | url: "https://pub.dev"
336 | source: hosted
337 | version: "0.27.3"
338 | sky_engine:
339 | dependency: transitive
340 | description: flutter
341 | source: sdk
342 | version: "0.0.99"
343 | source_span:
344 | dependency: transitive
345 | description:
346 | name: source_span
347 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
348 | url: "https://pub.dev"
349 | source: hosted
350 | version: "1.9.1"
351 | sprintf:
352 | dependency: transitive
353 | description:
354 | name: sprintf
355 | sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
356 | url: "https://pub.dev"
357 | source: hosted
358 | version: "7.0.0"
359 | sqflite:
360 | dependency: transitive
361 | description:
362 | name: sqflite
363 | sha256: "51c09d414ca74b1cd4a5880d63763ebd2033a4fc6d921708c7c1e98c603735d4"
364 | url: "https://pub.dev"
365 | source: hosted
366 | version: "2.0.2+1"
367 | sqflite_common:
368 | dependency: transitive
369 | description:
370 | name: sqflite_common
371 | sha256: b504fc5b4576a05586a0bb99d9bcc0d37a78d9d5ed68b96c361d5d3a8e538275
372 | url: "https://pub.dev"
373 | source: hosted
374 | version: "2.2.1+1"
375 | stack_trace:
376 | dependency: transitive
377 | description:
378 | name: stack_trace
379 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
380 | url: "https://pub.dev"
381 | source: hosted
382 | version: "1.11.0"
383 | stream_channel:
384 | dependency: transitive
385 | description:
386 | name: stream_channel
387 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
388 | url: "https://pub.dev"
389 | source: hosted
390 | version: "2.1.1"
391 | string_scanner:
392 | dependency: transitive
393 | description:
394 | name: string_scanner
395 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
396 | url: "https://pub.dev"
397 | source: hosted
398 | version: "1.2.0"
399 | synchronized:
400 | dependency: transitive
401 | description:
402 | name: synchronized
403 | sha256: a7f0790927c0806ae0d5eb061c713748fa6070ef0037e391a2d53c3844c09dc2
404 | url: "https://pub.dev"
405 | source: hosted
406 | version: "3.0.0+2"
407 | term_glyph:
408 | dependency: transitive
409 | description:
410 | name: term_glyph
411 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
412 | url: "https://pub.dev"
413 | source: hosted
414 | version: "1.2.1"
415 | test_api:
416 | dependency: transitive
417 | description:
418 | name: test_api
419 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
420 | url: "https://pub.dev"
421 | source: hosted
422 | version: "0.5.1"
423 | typed_data:
424 | dependency: transitive
425 | description:
426 | name: typed_data
427 | sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
428 | url: "https://pub.dev"
429 | source: hosted
430 | version: "1.3.1"
431 | url_launcher:
432 | dependency: "direct main"
433 | description:
434 | name: url_launcher
435 | sha256: "16177b6719e7c904875fdae7366479fb96b304bce0ad043ac5e652269ac09e82"
436 | url: "https://pub.dev"
437 | source: hosted
438 | version: "6.1.2"
439 | url_launcher_android:
440 | dependency: transitive
441 | description:
442 | name: url_launcher_android
443 | sha256: "1ccd353c1bff66b49863527c02759f4d06b92744bd9777c96a00ca6a9e8e1d2f"
444 | url: "https://pub.dev"
445 | source: hosted
446 | version: "6.0.17"
447 | url_launcher_ios:
448 | dependency: transitive
449 | description:
450 | name: url_launcher_ios
451 | sha256: "6ba7dddee26c9fae27c9203c424631109d73c8fa26cfa7bc3e35e751cb87f62e"
452 | url: "https://pub.dev"
453 | source: hosted
454 | version: "6.0.17"
455 | url_launcher_linux:
456 | dependency: transitive
457 | description:
458 | name: url_launcher_linux
459 | sha256: "360fa359ab06bcb4f7c5cd3123a2a9a4d3364d4575d27c4b33468bd4497dd094"
460 | url: "https://pub.dev"
461 | source: hosted
462 | version: "3.0.1"
463 | url_launcher_macos:
464 | dependency: transitive
465 | description:
466 | name: url_launcher_macos
467 | sha256: a9b3ea9043eabfaadfa3fb89de67a11210d85569086d22b3854484beab8b3978
468 | url: "https://pub.dev"
469 | source: hosted
470 | version: "3.0.1"
471 | url_launcher_platform_interface:
472 | dependency: transitive
473 | description:
474 | name: url_launcher_platform_interface
475 | sha256: "1b9c4dab07794498b83b5f938e26b20f68c3b460a3015b0307f9541cb34ef93d"
476 | url: "https://pub.dev"
477 | source: hosted
478 | version: "2.0.5"
479 | url_launcher_web:
480 | dependency: transitive
481 | description:
482 | name: url_launcher_web
483 | sha256: "1c7d34f93353de7f7ad9cb239e8b1e680e759b73845d4970dedc4e0a926e9abe"
484 | url: "https://pub.dev"
485 | source: hosted
486 | version: "2.0.11"
487 | url_launcher_windows:
488 | dependency: transitive
489 | description:
490 | name: url_launcher_windows
491 | sha256: e3c3b16d3104260c10eea3b0e34272aaa57921f83148b0619f74c2eced9b7ef1
492 | url: "https://pub.dev"
493 | source: hosted
494 | version: "3.0.1"
495 | uuid:
496 | dependency: transitive
497 | description:
498 | name: uuid
499 | sha256: "2469694ad079893e3b434a627970c33f2fa5adc46dfe03c9617546969a9a8afc"
500 | url: "https://pub.dev"
501 | source: hosted
502 | version: "3.0.6"
503 | vector_math:
504 | dependency: transitive
505 | description:
506 | name: vector_math
507 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
508 | url: "https://pub.dev"
509 | source: hosted
510 | version: "2.1.4"
511 | win32:
512 | dependency: transitive
513 | description:
514 | name: win32
515 | sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef
516 | url: "https://pub.dev"
517 | source: hosted
518 | version: "2.6.1"
519 | xdg_directories:
520 | dependency: transitive
521 | description:
522 | name: xdg_directories
523 | sha256: "060b6e1c891d956f72b5ac9463466c37cce3fa962a921532fc001e86fe93438e"
524 | url: "https://pub.dev"
525 | source: hosted
526 | version: "0.2.0+1"
527 | xml:
528 | dependency: transitive
529 | description:
530 | name: xml
531 | sha256: ac0e3f4bf00ba2708c33fbabbbe766300e509f8c82dbd4ab6525039813f7e2fb
532 | url: "https://pub.dev"
533 | source: hosted
534 | version: "6.1.0"
535 | sdks:
536 | dart: ">=3.0.0 <4.0.0"
537 | flutter: ">=3.3.0"
538 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: optimized_cached_image_example
2 | description: Demonstrates how to use the optimized_cached_image plugin.
3 | publish_to: 'none'
4 |
5 | environment:
6 | sdk: '>=2.12.0 <3.0.0'
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 |
12 | # The following adds the Cupertino Icons font to your application.
13 | # Use with the CupertinoIcons class for iOS style icons.
14 | cupertino_icons: ^1.0.3
15 | url_launcher: ^6.0.9
16 | optimized_cached_image:
17 | path: ../
18 | file: ^6.1.2
19 |
20 | dev_dependencies:
21 | flutter_test:
22 | sdk: flutter
23 |
24 | # For information on the generic Dart part of this file, see the
25 | # following page: https://dart.dev/tools/pub/pubspec
26 |
27 | # The following section is specific to Flutter.
28 | flutter:
29 |
30 | # The following line ensures that the Material Icons font is
31 | # included with your application, so that you can use the icons in
32 | # the material Icons class.
33 | uses-material-design: true
34 |
35 | # To add assets to your application, add an assets section, like this:
36 | # assets:
37 | # - images/a_dot_burr.jpeg
38 | # - images/a_dot_ham.jpeg
39 |
40 | # An image asset can refer to one or more resolution-specific "variants", see
41 | # https://flutter.dev/assets-and-images/#resolution-aware.
42 |
43 | # For details regarding adding assets from package dependencies, see
44 | # https://flutter.dev/assets-and-images/#from-packages
45 |
46 | # To add custom fonts to your application, add a fonts section here,
47 | # in this "flutter" section. Each entry in this list should have a
48 | # "family" key with the font family name, and a "fonts" key with a
49 | # list giving the asset and other descriptors for the font. For
50 | # example:
51 | # fonts:
52 | # - family: Schyler
53 | # fonts:
54 | # - asset: fonts/Schyler-Regular.ttf
55 | # - asset: fonts/Schyler-Italic.ttf
56 | # style: italic
57 | # - family: Trajan Pro
58 | # fonts:
59 | # - asset: fonts/TrajanPro.ttf
60 | # - asset: fonts/TrajanPro_Bold.ttf
61 | # weight: 700
62 | #
63 | # For details regarding fonts from package dependencies,
64 | # see https://flutter.dev/custom-fonts/#from-packages
65 |
--------------------------------------------------------------------------------
/example/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/web/favicon.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | example
18 |
19 |
20 |
21 |
24 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/example/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "short_name": "example",
4 | "start_url": ".",
5 | "display": "minimal-ui",
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 |
--------------------------------------------------------------------------------
/example/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/example/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(example LANGUAGES CXX)
3 |
4 | set(BINARY_NAME "example")
5 |
6 | cmake_policy(SET CMP0063 NEW)
7 |
8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
9 |
10 | # Configure build options.
11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
12 | if(IS_MULTICONFIG)
13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
14 | CACHE STRING "" FORCE)
15 | else()
16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
17 | set(CMAKE_BUILD_TYPE "Debug" CACHE
18 | STRING "Flutter build mode" FORCE)
19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
20 | "Debug" "Profile" "Release")
21 | endif()
22 | endif()
23 |
24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
28 |
29 | # Use Unicode for all projects.
30 | add_definitions(-DUNICODE -D_UNICODE)
31 |
32 | # Compilation settings that should be applied to most targets.
33 | function(APPLY_STANDARD_SETTINGS TARGET)
34 | target_compile_features(${TARGET} PUBLIC cxx_std_17)
35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
36 | target_compile_options(${TARGET} PRIVATE /EHsc)
37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
39 | endfunction()
40 |
41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
42 |
43 | # Flutter library and tool build rules.
44 | add_subdirectory(${FLUTTER_MANAGED_DIR})
45 |
46 | # Application build
47 | add_subdirectory("runner")
48 |
49 | # Generated plugin build rules, which manage building the plugins and adding
50 | # them to the application.
51 | include(flutter/generated_plugins.cmake)
52 |
53 |
54 | # === Installation ===
55 | # Support files are copied into place next to the executable, so that it can
56 | # run in place. This is done instead of making a separate bundle (as on Linux)
57 | # so that building and running from within Visual Studio will work.
58 | set(BUILD_BUNDLE_DIR "$")
59 | # Make the "install" step default, as it's required to run.
60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
63 | endif()
64 |
65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
67 |
68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
69 | COMPONENT Runtime)
70 |
71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
72 | COMPONENT Runtime)
73 |
74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
75 | COMPONENT Runtime)
76 |
77 | if(PLUGIN_BUNDLED_LIBRARIES)
78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
80 | COMPONENT Runtime)
81 | endif()
82 |
83 | # Fully re-copy the assets directory on each build to avoid having stale files
84 | # from a previous install.
85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
86 | install(CODE "
87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
88 | " COMPONENT Runtime)
89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
91 |
92 | # Install the AOT library on non-Debug builds only.
93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
94 | CONFIGURATIONS Profile;Release
95 | COMPONENT Runtime)
96 |
--------------------------------------------------------------------------------
/example/windows/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 |
3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
4 |
5 | # Configuration provided via flutter tool.
6 | include(${EPHEMERAL_DIR}/generated_config.cmake)
7 |
8 | # TODO: Move the rest of this into files in ephemeral. See
9 | # https://github.com/flutter/flutter/issues/57146.
10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
11 |
12 | # === Flutter Library ===
13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
14 |
15 | # Published to parent scope for install step.
16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
20 |
21 | list(APPEND FLUTTER_LIBRARY_HEADERS
22 | "flutter_export.h"
23 | "flutter_windows.h"
24 | "flutter_messenger.h"
25 | "flutter_plugin_registrar.h"
26 | )
27 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
28 | add_library(flutter INTERFACE)
29 | target_include_directories(flutter INTERFACE
30 | "${EPHEMERAL_DIR}"
31 | )
32 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
33 | add_dependencies(flutter flutter_assemble)
34 |
35 | # === Wrapper ===
36 | list(APPEND CPP_WRAPPER_SOURCES_CORE
37 | "core_implementations.cc"
38 | "standard_codec.cc"
39 | )
40 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
41 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
42 | "plugin_registrar.cc"
43 | )
44 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
45 | list(APPEND CPP_WRAPPER_SOURCES_APP
46 | "flutter_engine.cc"
47 | "flutter_view_controller.cc"
48 | )
49 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
50 |
51 | # Wrapper sources needed for a plugin.
52 | add_library(flutter_wrapper_plugin STATIC
53 | ${CPP_WRAPPER_SOURCES_CORE}
54 | ${CPP_WRAPPER_SOURCES_PLUGIN}
55 | )
56 | apply_standard_settings(flutter_wrapper_plugin)
57 | set_target_properties(flutter_wrapper_plugin PROPERTIES
58 | POSITION_INDEPENDENT_CODE ON)
59 | set_target_properties(flutter_wrapper_plugin PROPERTIES
60 | CXX_VISIBILITY_PRESET hidden)
61 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
62 | target_include_directories(flutter_wrapper_plugin PUBLIC
63 | "${WRAPPER_ROOT}/include"
64 | )
65 | add_dependencies(flutter_wrapper_plugin flutter_assemble)
66 |
67 | # Wrapper sources needed for the runner.
68 | add_library(flutter_wrapper_app STATIC
69 | ${CPP_WRAPPER_SOURCES_CORE}
70 | ${CPP_WRAPPER_SOURCES_APP}
71 | )
72 | apply_standard_settings(flutter_wrapper_app)
73 | target_link_libraries(flutter_wrapper_app PUBLIC flutter)
74 | target_include_directories(flutter_wrapper_app PUBLIC
75 | "${WRAPPER_ROOT}/include"
76 | )
77 | add_dependencies(flutter_wrapper_app flutter_assemble)
78 |
79 | # === Flutter tool backend ===
80 | # _phony_ is a non-existent file to force this command to run every time,
81 | # since currently there's no way to get a full input/output list from the
82 | # flutter tool.
83 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
84 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
85 | add_custom_command(
86 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
87 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
88 | ${CPP_WRAPPER_SOURCES_APP}
89 | ${PHONY_OUTPUT}
90 | COMMAND ${CMAKE_COMMAND} -E env
91 | ${FLUTTER_TOOL_ENVIRONMENT}
92 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
93 | windows-x64 $
94 | )
95 | add_custom_target(flutter_assemble DEPENDS
96 | "${FLUTTER_LIBRARY}"
97 | ${FLUTTER_LIBRARY_HEADERS}
98 | ${CPP_WRAPPER_SOURCES_CORE}
99 | ${CPP_WRAPPER_SOURCES_PLUGIN}
100 | ${CPP_WRAPPER_SOURCES_APP}
101 | )
102 |
--------------------------------------------------------------------------------
/example/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(runner LANGUAGES CXX)
3 |
4 | add_executable(${BINARY_NAME} WIN32
5 | "flutter_window.cpp"
6 | "main.cpp"
7 | "run_loop.cpp"
8 | "utils.cpp"
9 | "win32_window.cpp"
10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
11 | "Runner.rc"
12 | "runner.exe.manifest"
13 | )
14 | apply_standard_settings(${BINARY_NAME})
15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
18 | add_dependencies(${BINARY_NAME} flutter_assemble)
19 |
--------------------------------------------------------------------------------
/example/windows/runner/Runner.rc:
--------------------------------------------------------------------------------
1 | // Microsoft Visual C++ generated resource script.
2 | //
3 | #pragma code_page(65001)
4 | #include "resource.h"
5 |
6 | #define APSTUDIO_READONLY_SYMBOLS
7 | /////////////////////////////////////////////////////////////////////////////
8 | //
9 | // Generated from the TEXTINCLUDE 2 resource.
10 | //
11 | #include "winres.h"
12 |
13 | /////////////////////////////////////////////////////////////////////////////
14 | #undef APSTUDIO_READONLY_SYMBOLS
15 |
16 | /////////////////////////////////////////////////////////////////////////////
17 | // English (United States) resources
18 |
19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
21 |
22 | #ifdef APSTUDIO_INVOKED
23 | /////////////////////////////////////////////////////////////////////////////
24 | //
25 | // TEXTINCLUDE
26 | //
27 |
28 | 1 TEXTINCLUDE
29 | BEGIN
30 | "resource.h\0"
31 | END
32 |
33 | 2 TEXTINCLUDE
34 | BEGIN
35 | "#include ""winres.h""\r\n"
36 | "\0"
37 | END
38 |
39 | 3 TEXTINCLUDE
40 | BEGIN
41 | "\r\n"
42 | "\0"
43 | END
44 |
45 | #endif // APSTUDIO_INVOKED
46 |
47 |
48 | /////////////////////////////////////////////////////////////////////////////
49 | //
50 | // Icon
51 | //
52 |
53 | // Icon with lowest ID value placed first to ensure application icon
54 | // remains consistent on all systems.
55 | IDI_APP_ICON ICON "resources\\app_icon.ico"
56 |
57 |
58 | /////////////////////////////////////////////////////////////////////////////
59 | //
60 | // Version
61 | //
62 |
63 | #ifdef FLUTTER_BUILD_NUMBER
64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
65 | #else
66 | #define VERSION_AS_NUMBER 1,0,0
67 | #endif
68 |
69 | #ifdef FLUTTER_BUILD_NAME
70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME
71 | #else
72 | #define VERSION_AS_STRING "1.0.0"
73 | #endif
74 |
75 | VS_VERSION_INFO VERSIONINFO
76 | FILEVERSION VERSION_AS_NUMBER
77 | PRODUCTVERSION VERSION_AS_NUMBER
78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
79 | #ifdef _DEBUG
80 | FILEFLAGS VS_FF_DEBUG
81 | #else
82 | FILEFLAGS 0x0L
83 | #endif
84 | FILEOS VOS__WINDOWS32
85 | FILETYPE VFT_APP
86 | FILESUBTYPE 0x0L
87 | BEGIN
88 | BLOCK "StringFileInfo"
89 | BEGIN
90 | BLOCK "040904e4"
91 | BEGIN
92 | VALUE "CompanyName", "com.example" "\0"
93 | VALUE "FileDescription", "A new Flutter project." "\0"
94 | VALUE "FileVersion", VERSION_AS_STRING "\0"
95 | VALUE "InternalName", "example" "\0"
96 | VALUE "LegalCopyright", "Copyright (C) 2020 com.example. All rights reserved." "\0"
97 | VALUE "OriginalFilename", "example.exe" "\0"
98 | VALUE "ProductName", "example" "\0"
99 | VALUE "ProductVersion", VERSION_AS_STRING "\0"
100 | END
101 | END
102 | BLOCK "VarFileInfo"
103 | BEGIN
104 | VALUE "Translation", 0x409, 1252
105 | END
106 | END
107 |
108 | #endif // English (United States) resources
109 | /////////////////////////////////////////////////////////////////////////////
110 |
111 |
112 |
113 | #ifndef APSTUDIO_INVOKED
114 | /////////////////////////////////////////////////////////////////////////////
115 | //
116 | // Generated from the TEXTINCLUDE 3 resource.
117 | //
118 |
119 |
120 | /////////////////////////////////////////////////////////////////////////////
121 | #endif // not APSTUDIO_INVOKED
122 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.cpp:
--------------------------------------------------------------------------------
1 | #include "flutter_window.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | FlutterWindow::FlutterWindow(RunLoop* run_loop,
8 | const flutter::DartProject& project)
9 | : run_loop_(run_loop), project_(project) {}
10 |
11 | FlutterWindow::~FlutterWindow() {}
12 |
13 | bool FlutterWindow::OnCreate() {
14 | if (!Win32Window::OnCreate()) {
15 | return false;
16 | }
17 |
18 | RECT frame = GetClientArea();
19 |
20 | // The size here must match the window dimensions to avoid unnecessary surface
21 | // creation / destruction in the startup path.
22 | flutter_controller_ = std::make_unique(
23 | frame.right - frame.left, frame.bottom - frame.top, project_);
24 | // Ensure that basic setup of the controller was successful.
25 | if (!flutter_controller_->engine() || !flutter_controller_->view()) {
26 | return false;
27 | }
28 | RegisterPlugins(flutter_controller_->engine());
29 | run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
30 | SetChildContent(flutter_controller_->view()->GetNativeWindow());
31 | return true;
32 | }
33 |
34 | void FlutterWindow::OnDestroy() {
35 | if (flutter_controller_) {
36 | run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
37 | flutter_controller_ = nullptr;
38 | }
39 |
40 | Win32Window::OnDestroy();
41 | }
42 |
43 | LRESULT
44 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
45 | WPARAM const wparam,
46 | LPARAM const lparam) noexcept {
47 | // Give Flutter, including plugins, an opporutunity to handle window messages.
48 | if (flutter_controller_) {
49 | std::optional result =
50 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
51 | lparam);
52 | if (result) {
53 | return *result;
54 | }
55 | }
56 |
57 | switch (message) {
58 | case WM_FONTCHANGE:
59 | flutter_controller_->engine()->ReloadSystemFonts();
60 | break;
61 | }
62 |
63 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
64 | }
65 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "run_loop.h"
10 | #include "win32_window.h"
11 |
12 | // A window that does nothing but host a Flutter view.
13 | class FlutterWindow : public Win32Window {
14 | public:
15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a
16 | // Flutter view running |project|.
17 | explicit FlutterWindow(RunLoop* run_loop,
18 | const flutter::DartProject& project);
19 | virtual ~FlutterWindow();
20 |
21 | protected:
22 | // Win32Window:
23 | bool OnCreate() override;
24 | void OnDestroy() override;
25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
26 | LPARAM const lparam) noexcept override;
27 |
28 | private:
29 | // The run loop driving events for this window.
30 | RunLoop* run_loop_;
31 |
32 | // The project to run.
33 | flutter::DartProject project_;
34 |
35 | // The Flutter instance hosted by this window.
36 | std::unique_ptr flutter_controller_;
37 | };
38 |
39 | #endif // RUNNER_FLUTTER_WINDOW_H_
40 |
--------------------------------------------------------------------------------
/example/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "run_loop.h"
7 | #include "utils.h"
8 |
9 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
10 | _In_ wchar_t *command_line, _In_ int show_command) {
11 | // Attach to console when present (e.g., 'flutter run') or create a
12 | // new console when running with a debugger.
13 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
14 | CreateAndAttachConsole();
15 | }
16 |
17 | // Initialize COM, so that it is available for use in the library and/or
18 | // plugins.
19 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
20 |
21 | RunLoop run_loop;
22 |
23 | flutter::DartProject project(L"data");
24 | FlutterWindow window(&run_loop, project);
25 | Win32Window::Point origin(10, 10);
26 | Win32Window::Size size(1280, 720);
27 | if (!window.CreateAndShow(L"example", origin, size)) {
28 | return EXIT_FAILURE;
29 | }
30 | window.SetQuitOnClose(true);
31 |
32 | run_loop.Run();
33 |
34 | ::CoUninitialize();
35 | return EXIT_SUCCESS;
36 | }
37 |
--------------------------------------------------------------------------------
/example/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/example/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/example/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/example/windows/runner/run_loop.cpp:
--------------------------------------------------------------------------------
1 | #include "run_loop.h"
2 |
3 | #include
4 |
5 | #include
6 |
7 | RunLoop::RunLoop() {}
8 |
9 | RunLoop::~RunLoop() {}
10 |
11 | void RunLoop::Run() {
12 | bool keep_running = true;
13 | TimePoint next_flutter_event_time = TimePoint::clock::now();
14 | while (keep_running) {
15 | std::chrono::nanoseconds wait_duration =
16 | std::max(std::chrono::nanoseconds(0),
17 | next_flutter_event_time - TimePoint::clock::now());
18 | ::MsgWaitForMultipleObjects(
19 | 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000),
20 | QS_ALLINPUT);
21 | bool processed_events = false;
22 | MSG message;
23 | // All pending Windows messages must be processed; MsgWaitForMultipleObjects
24 | // won't return again for items left in the queue after PeekMessage.
25 | while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
26 | processed_events = true;
27 | if (message.message == WM_QUIT) {
28 | keep_running = false;
29 | break;
30 | }
31 | ::TranslateMessage(&message);
32 | ::DispatchMessage(&message);
33 | // Allow Flutter to process messages each time a Windows message is
34 | // processed, to prevent starvation.
35 | next_flutter_event_time =
36 | std::min(next_flutter_event_time, ProcessFlutterMessages());
37 | }
38 | // If the PeekMessage loop didn't run, process Flutter messages.
39 | if (!processed_events) {
40 | next_flutter_event_time =
41 | std::min(next_flutter_event_time, ProcessFlutterMessages());
42 | }
43 | }
44 | }
45 |
46 | void RunLoop::RegisterFlutterInstance(
47 | flutter::FlutterEngine* flutter_instance) {
48 | flutter_instances_.insert(flutter_instance);
49 | }
50 |
51 | void RunLoop::UnregisterFlutterInstance(
52 | flutter::FlutterEngine* flutter_instance) {
53 | flutter_instances_.erase(flutter_instance);
54 | }
55 |
56 | RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
57 | TimePoint next_event_time = TimePoint::max();
58 | for (auto instance : flutter_instances_) {
59 | std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
60 | if (wait_duration != std::chrono::nanoseconds::max()) {
61 | next_event_time =
62 | std::min(next_event_time, TimePoint::clock::now() + wait_duration);
63 | }
64 | }
65 | return next_event_time;
66 | }
67 |
--------------------------------------------------------------------------------
/example/windows/runner/run_loop.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_RUN_LOOP_H_
2 | #define RUNNER_RUN_LOOP_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | // A runloop that will service events for Flutter instances as well
10 | // as native messages.
11 | class RunLoop {
12 | public:
13 | RunLoop();
14 | ~RunLoop();
15 |
16 | // Prevent copying
17 | RunLoop(RunLoop const&) = delete;
18 | RunLoop& operator=(RunLoop const&) = delete;
19 |
20 | // Runs the run loop until the application quits.
21 | void Run();
22 |
23 | // Registers the given Flutter instance for event servicing.
24 | void RegisterFlutterInstance(
25 | flutter::FlutterEngine* flutter_instance);
26 |
27 | // Unregisters the given Flutter instance from event servicing.
28 | void UnregisterFlutterInstance(
29 | flutter::FlutterEngine* flutter_instance);
30 |
31 | private:
32 | using TimePoint = std::chrono::steady_clock::time_point;
33 |
34 | // Processes all currently pending messages for registered Flutter instances.
35 | TimePoint ProcessFlutterMessages();
36 |
37 | std::set flutter_instances_;
38 | };
39 |
40 | #endif // RUNNER_RUN_LOOP_H_
41 |
--------------------------------------------------------------------------------
/example/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | void CreateAndAttachConsole() {
11 | if (::AllocConsole()) {
12 | FILE *unused;
13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
14 | _dup2(_fileno(stdout), 1);
15 | }
16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
17 | _dup2(_fileno(stdout), 2);
18 | }
19 | std::ios::sync_with_stdio();
20 | FlutterDesktopResyncOutputStreams();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | // Creates a console for the process, and redirects stdout and stderr to
5 | // it for both the runner and the Flutter library.
6 | void CreateAndAttachConsole();
7 |
8 | #endif // RUNNER_UTILS_H_
9 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.cpp:
--------------------------------------------------------------------------------
1 | #include "win32_window.h"
2 |
3 | #include
4 |
5 | #include "resource.h"
6 |
7 | namespace {
8 |
9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
10 |
11 | // The number of Win32Window objects that currently exist.
12 | static int g_active_window_count = 0;
13 |
14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
15 |
16 | // Scale helper to convert logical scaler values to physical using passed in
17 | // scale factor
18 | int Scale(int source, double scale_factor) {
19 | return static_cast(source * scale_factor);
20 | }
21 |
22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
23 | // This API is only needed for PerMonitor V1 awareness mode.
24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) {
25 | HMODULE user32_module = LoadLibraryA("User32.dll");
26 | if (!user32_module) {
27 | return;
28 | }
29 | auto enable_non_client_dpi_scaling =
30 | reinterpret_cast(
31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
32 | if (enable_non_client_dpi_scaling != nullptr) {
33 | enable_non_client_dpi_scaling(hwnd);
34 | FreeLibrary(user32_module);
35 | }
36 | }
37 |
38 | } // namespace
39 |
40 | // Manages the Win32Window's window class registration.
41 | class WindowClassRegistrar {
42 | public:
43 | ~WindowClassRegistrar() = default;
44 |
45 | // Returns the singleton registar instance.
46 | static WindowClassRegistrar* GetInstance() {
47 | if (!instance_) {
48 | instance_ = new WindowClassRegistrar();
49 | }
50 | return instance_;
51 | }
52 |
53 | // Returns the name of the window class, registering the class if it hasn't
54 | // previously been registered.
55 | const wchar_t* GetWindowClass();
56 |
57 | // Unregisters the window class. Should only be called if there are no
58 | // instances of the window.
59 | void UnregisterWindowClass();
60 |
61 | private:
62 | WindowClassRegistrar() = default;
63 |
64 | static WindowClassRegistrar* instance_;
65 |
66 | bool class_registered_ = false;
67 | };
68 |
69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
70 |
71 | const wchar_t* WindowClassRegistrar::GetWindowClass() {
72 | if (!class_registered_) {
73 | WNDCLASS window_class{};
74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
75 | window_class.lpszClassName = kWindowClassName;
76 | window_class.style = CS_HREDRAW | CS_VREDRAW;
77 | window_class.cbClsExtra = 0;
78 | window_class.cbWndExtra = 0;
79 | window_class.hInstance = GetModuleHandle(nullptr);
80 | window_class.hIcon =
81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
82 | window_class.hbrBackground = 0;
83 | window_class.lpszMenuName = nullptr;
84 | window_class.lpfnWndProc = Win32Window::WndProc;
85 | RegisterClass(&window_class);
86 | class_registered_ = true;
87 | }
88 | return kWindowClassName;
89 | }
90 |
91 | void WindowClassRegistrar::UnregisterWindowClass() {
92 | UnregisterClass(kWindowClassName, nullptr);
93 | class_registered_ = false;
94 | }
95 |
96 | Win32Window::Win32Window() {
97 | ++g_active_window_count;
98 | }
99 |
100 | Win32Window::~Win32Window() {
101 | --g_active_window_count;
102 | Destroy();
103 | }
104 |
105 | bool Win32Window::CreateAndShow(const std::wstring& title,
106 | const Point& origin,
107 | const Size& size) {
108 | Destroy();
109 |
110 | const wchar_t* window_class =
111 | WindowClassRegistrar::GetInstance()->GetWindowClass();
112 |
113 | const POINT target_point = {static_cast(origin.x),
114 | static_cast(origin.y)};
115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
117 | double scale_factor = dpi / 96.0;
118 |
119 | HWND window = CreateWindow(
120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor),
123 | nullptr, nullptr, GetModuleHandle(nullptr), this);
124 |
125 | if (!window) {
126 | return false;
127 | }
128 |
129 | return OnCreate();
130 | }
131 |
132 | // static
133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window,
134 | UINT const message,
135 | WPARAM const wparam,
136 | LPARAM const lparam) noexcept {
137 | if (message == WM_NCCREATE) {
138 | auto window_struct = reinterpret_cast(lparam);
139 | SetWindowLongPtr(window, GWLP_USERDATA,
140 | reinterpret_cast(window_struct->lpCreateParams));
141 |
142 | auto that = static_cast(window_struct->lpCreateParams);
143 | EnableFullDpiSupportIfAvailable(window);
144 | that->window_handle_ = window;
145 | } else if (Win32Window* that = GetThisFromHandle(window)) {
146 | return that->MessageHandler(window, message, wparam, lparam);
147 | }
148 |
149 | return DefWindowProc(window, message, wparam, lparam);
150 | }
151 |
152 | LRESULT
153 | Win32Window::MessageHandler(HWND hwnd,
154 | UINT const message,
155 | WPARAM const wparam,
156 | LPARAM const lparam) noexcept {
157 | switch (message) {
158 | case WM_DESTROY:
159 | window_handle_ = nullptr;
160 | Destroy();
161 | if (quit_on_close_) {
162 | PostQuitMessage(0);
163 | }
164 | return 0;
165 |
166 | case WM_DPICHANGED: {
167 | auto newRectSize = reinterpret_cast(lparam);
168 | LONG newWidth = newRectSize->right - newRectSize->left;
169 | LONG newHeight = newRectSize->bottom - newRectSize->top;
170 |
171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
173 |
174 | return 0;
175 | }
176 | case WM_SIZE:
177 | RECT rect = GetClientArea();
178 | if (child_content_ != nullptr) {
179 | // Size and position the child window.
180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
181 | rect.bottom - rect.top, TRUE);
182 | }
183 | return 0;
184 |
185 | case WM_ACTIVATE:
186 | if (child_content_ != nullptr) {
187 | SetFocus(child_content_);
188 | }
189 | return 0;
190 | }
191 |
192 | return DefWindowProc(window_handle_, message, wparam, lparam);
193 | }
194 |
195 | void Win32Window::Destroy() {
196 | OnDestroy();
197 |
198 | if (window_handle_) {
199 | DestroyWindow(window_handle_);
200 | window_handle_ = nullptr;
201 | }
202 | if (g_active_window_count == 0) {
203 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
204 | }
205 | }
206 |
207 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
208 | return reinterpret_cast(
209 | GetWindowLongPtr(window, GWLP_USERDATA));
210 | }
211 |
212 | void Win32Window::SetChildContent(HWND content) {
213 | child_content_ = content;
214 | SetParent(content, window_handle_);
215 | RECT frame = GetClientArea();
216 |
217 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
218 | frame.bottom - frame.top, true);
219 |
220 | SetFocus(child_content_);
221 | }
222 |
223 | RECT Win32Window::GetClientArea() {
224 | RECT frame;
225 | GetClientRect(window_handle_, &frame);
226 | return frame;
227 | }
228 |
229 | HWND Win32Window::GetHandle() {
230 | return window_handle_;
231 | }
232 |
233 | void Win32Window::SetQuitOnClose(bool quit_on_close) {
234 | quit_on_close_ = quit_on_close;
235 | }
236 |
237 | bool Win32Window::OnCreate() {
238 | // No-op; provided for subclasses.
239 | return true;
240 | }
241 |
242 | void Win32Window::OnDestroy() {
243 | // No-op; provided for subclasses.
244 | }
245 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_WIN32_WINDOW_H_
2 | #define RUNNER_WIN32_WINDOW_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be
11 | // inherited from by classes that wish to specialize with custom
12 | // rendering and input handling
13 | class Win32Window {
14 | public:
15 | struct Point {
16 | unsigned int x;
17 | unsigned int y;
18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {}
19 | };
20 |
21 | struct Size {
22 | unsigned int width;
23 | unsigned int height;
24 | Size(unsigned int width, unsigned int height)
25 | : width(width), height(height) {}
26 | };
27 |
28 | Win32Window();
29 | virtual ~Win32Window();
30 |
31 | // Creates and shows a win32 window with |title| and position and size using
32 | // |origin| and |size|. New windows are created on the default monitor. Window
33 | // sizes are specified to the OS in physical pixels, hence to ensure a
34 | // consistent size to will treat the width height passed in to this function
35 | // as logical pixels and scale to appropriate for the default monitor. Returns
36 | // true if the window was created successfully.
37 | bool CreateAndShow(const std::wstring& title,
38 | const Point& origin,
39 | const Size& size);
40 |
41 | // Release OS resources associated with window.
42 | void Destroy();
43 |
44 | // Inserts |content| into the window tree.
45 | void SetChildContent(HWND content);
46 |
47 | // Returns the backing Window handle to enable clients to set icon and other
48 | // window properties. Returns nullptr if the window has been destroyed.
49 | HWND GetHandle();
50 |
51 | // If true, closing this window will quit the application.
52 | void SetQuitOnClose(bool quit_on_close);
53 |
54 | // Return a RECT representing the bounds of the current client area.
55 | RECT GetClientArea();
56 |
57 | protected:
58 | // Processes and route salient window messages for mouse handling,
59 | // size change and DPI. Delegates handling of these to member overloads that
60 | // inheriting classes can handle.
61 | virtual LRESULT MessageHandler(HWND window,
62 | UINT const message,
63 | WPARAM const wparam,
64 | LPARAM const lparam) noexcept;
65 |
66 | // Called when CreateAndShow is called, allowing subclass window-related
67 | // setup. Subclasses should return false if setup fails.
68 | virtual bool OnCreate();
69 |
70 | // Called when Destroy is called.
71 | virtual void OnDestroy();
72 |
73 | private:
74 | friend class WindowClassRegistrar;
75 |
76 | // OS callback called by message pump. Handles the WM_NCCREATE message which
77 | // is passed when the non-client area is being created and enables automatic
78 | // non-client DPI scaling so that the non-client area automatically
79 | // responsponds to changes in DPI. All other messages are handled by
80 | // MessageHandler.
81 | static LRESULT CALLBACK WndProc(HWND const window,
82 | UINT const message,
83 | WPARAM const wparam,
84 | LPARAM const lparam) noexcept;
85 |
86 | // Retrieves a class instance pointer for |window|
87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept;
88 |
89 | bool quit_on_close_ = false;
90 |
91 | // window handle for top level window.
92 | HWND window_handle_ = nullptr;
93 |
94 | // window handle for hosted content.
95 | HWND child_content_ = nullptr;
96 | };
97 |
98 | #endif // RUNNER_WIN32_WINDOW_H_
99 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/optimized_cached_image.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint optimized_cached_image.podspec' to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'optimized_cached_image'
7 | s.version = '0.0.1'
8 | s.summary = 'A library for loading images from network, resizing and caching them for memory sensitivity'
9 | s.description = <<-DESC
10 | A library for loading images from network, resizing and caching them for memory sensitivity
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.dependency 'Flutter'
18 | s.platform = :ios, '8.0'
19 |
20 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/lib/optimized_cached_image.dart:
--------------------------------------------------------------------------------
1 | /// A flutter library for loading images from network, resizing and caching them
2 | /// for memory sensitivity. This resizes and stores the images in cache based on
3 | /// parent container constraints and hence loads images of lower size into
4 | /// memory. This is heavily inspired by cached_network_image library.
5 | library optimized_cached_image;
6 |
7 | export 'package:flutter_cache_manager/src/result/download_progress.dart';
8 | export 'src/oci_widget.dart';
9 | export 'src/image_provider/optimized_cached_image_provider.dart';
10 | export 'src/image_provider/multi_image_stream_completer.dart';
11 | export 'src/debug_tools.dart';
12 |
--------------------------------------------------------------------------------
/lib/src/cache/default_image_cache_manager.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_cache_manager/flutter_cache_manager.dart';
2 | import 'package:optimized_cached_image/src/cache/image_cache_manager.dart';
3 |
4 | /// The DefaultCacheManager that can be easily used directly. The code of
5 | /// this implementation can be used as inspiration for more complex cache
6 | /// managers.
7 | class DefaultImageCacheManager extends CacheManager with OicImageCacheManager {
8 | static const key = 'libCachedImageData';
9 |
10 | static DefaultImageCacheManager? _instance;
11 | factory DefaultImageCacheManager() {
12 | _instance ??= DefaultImageCacheManager._();
13 | return _instance!;
14 | }
15 | DefaultImageCacheManager._() : super(Config(key));
16 | }
17 |
--------------------------------------------------------------------------------
/lib/src/cache/image_cache_manager.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:math';
3 | import 'dart:ui' as ui;
4 |
5 | import 'package:file/file.dart';
6 | import 'package:flutter/widgets.dart';
7 | import 'package:flutter_cache_manager/flutter_cache_manager.dart';
8 |
9 | import '../transformer/image_transformer.dart';
10 |
11 | const supportedFileNames = ['jpg', 'jpeg', 'png', 'tga', 'gif', 'cur', 'ico'];
12 | mixin OicImageCacheManager on BaseCacheManager {
13 | /// Returns a resized image file to fit within maxHeight and maxWidth. It
14 | /// tries to keep the aspect ratio. It stores the resized image by adding
15 | /// the size to the key or url. For example when resizing
16 | /// https://via.placeholder.com/150 to max width 100 and height 75 it will
17 | /// store it with cacheKey resized_w100_h75_https://via.placeholder.com/150.
18 | ///
19 | /// When the resized file is not found in the cache the original is fetched
20 | /// from the cache or online and stored in the cache. Then it is resized
21 | /// and returned to the caller.
22 | ///
23 | final ImageTransformer transformer = DefaultImageTransformer();
24 |
25 | Stream getImageFile(
26 | String url, {
27 | String? key,
28 | Map? headers,
29 | bool withProgress = false,
30 | int? maxHeight,
31 | int? maxWidth,
32 | }) async* {
33 | if (maxHeight == null && maxWidth == null) {
34 | yield* getFileStream(url,
35 | key: key, headers: headers, withProgress: withProgress);
36 | return;
37 | }
38 | key ??= url;
39 | var resizedKey = 'resized';
40 | if (maxWidth != null) resizedKey += '_w$maxWidth';
41 | if (maxHeight != null) resizedKey += '_h$maxHeight';
42 | resizedKey += '_$key';
43 |
44 | var fromCache = await getFileFromCache(resizedKey);
45 | if (fromCache != null) {
46 | yield fromCache;
47 | if (fromCache.validTill.isAfter(DateTime.now())) {
48 | return;
49 | }
50 | withProgress = false;
51 | }
52 | if (!_runningResizes.containsKey(resizedKey)) {
53 | _runningResizes[resizedKey] = _fetchedResizedFile(
54 | url,
55 | key,
56 | resizedKey,
57 | headers,
58 | withProgress,
59 | maxWidth: maxWidth,
60 | maxHeight: maxHeight,
61 | );
62 | }
63 | yield* _runningResizes[resizedKey]!;
64 | _runningResizes.remove(resizedKey);
65 | }
66 |
67 | final Map> _runningResizes = {};
68 | Future _resizeImageFile(
69 | FileInfo originalFile,
70 | String key,
71 | int? maxWidth,
72 | int? maxHeight,
73 | ) async {
74 | var originalFileName = originalFile.file.path;
75 | var fileExtension = originalFileName.split('.').last;
76 | if (!supportedFileNames.contains(fileExtension)) {
77 | return originalFile;
78 | }
79 |
80 | var image = await _decodeImage(originalFile.file);
81 | var shouldResize = maxWidth != null
82 | ? image.width > maxWidth
83 | : false || maxHeight != null
84 | ? image.height > maxHeight
85 | : false;
86 |
87 | if (!shouldResize) return originalFile;
88 |
89 | if (maxWidth != null && maxHeight != null) {
90 | var resizeFactorWidth = image.width / maxWidth;
91 | var resizeFactorHeight = image.height / maxHeight;
92 | var resizeFactor = max(resizeFactorHeight, resizeFactorWidth);
93 |
94 | maxWidth = (image.width / resizeFactor).round();
95 | maxHeight = (image.height / resizeFactor).round();
96 | }
97 | var resizedFile =
98 | await transformer.transform(originalFile, maxWidth, maxHeight);
99 | var maxAge = originalFile.validTill.difference(DateTime.now());
100 |
101 | var resizedFileContent = resizedFile.file.openRead();
102 | var file = await putFileStream(
103 | originalFile.originalUrl,
104 | resizedFileContent,
105 | key: key,
106 | maxAge: maxAge,
107 | fileExtension: fileExtension,
108 | );
109 |
110 | return FileInfo(
111 | file,
112 | originalFile.source,
113 | originalFile.validTill,
114 | originalFile.originalUrl,
115 | );
116 | }
117 |
118 | Stream _fetchedResizedFile(
119 | String url,
120 | String originalKey,
121 | String resizedKey,
122 | Map? headers,
123 | bool withProgress, {
124 | int? maxWidth,
125 | int? maxHeight,
126 | }) async* {
127 | await for (var response in getFileStream(
128 | url,
129 | key: originalKey,
130 | headers: headers,
131 | withProgress: withProgress,
132 | )) {
133 | if (response is DownloadProgress) {
134 | yield response;
135 | }
136 | if (response is FileInfo) {
137 | yield await _resizeImageFile(
138 | response,
139 | resizedKey,
140 | maxWidth,
141 | maxHeight,
142 | );
143 | }
144 | }
145 | }
146 | }
147 |
148 | Future _decodeImage(File file,
149 | {int? width, int? height, bool allowUpscaling = false}) {
150 | var shouldResize = width != null || height != null;
151 | var fileImage = FileImage(file);
152 | final image = shouldResize
153 | ? ResizeImage(fileImage,
154 | width: width, height: height, allowUpscaling: allowUpscaling)
155 | : fileImage as ImageProvider;
156 | final completer = Completer();
157 | image
158 | .resolve(const ImageConfiguration())
159 | .addListener(ImageStreamListener((info, _) {
160 | completer.complete(info.image);
161 | image.evict();
162 | }));
163 | return completer.future;
164 | }
165 |
--------------------------------------------------------------------------------
/lib/src/debug_tools.dart:
--------------------------------------------------------------------------------
1 | import 'dart:developer' as developer;
2 |
3 | abstract class Logger {
4 | static bool enableLogging = false;
5 | static const TAG = "optimized_image_cache";
6 | }
7 |
8 | void log(String message) {
9 | if (Logger.enableLogging) {
10 | developer.log(message, name: Logger.TAG);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/src/image_provider/_image_provider_io.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async' show Future, StreamController, scheduleMicrotask;
2 | import 'dart:ui' as ui show Codec;
3 | import 'dart:ui';
4 |
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_cache_manager/flutter_cache_manager.dart';
8 | import 'package:optimized_cached_image/src/cache/default_image_cache_manager.dart';
9 | import 'package:optimized_cached_image/src/cache/image_cache_manager.dart';
10 |
11 | import '../../optimized_cached_image.dart' show ImageRenderMethodForWeb;
12 | import 'multi_image_stream_completer.dart';
13 | import 'optimized_cached_image_provider.dart' as image_provider;
14 |
15 | /// IO implementation of the CachedNetworkImageProvider; the ImageProvider to
16 | /// load network images using a cache.
17 | class OptimizedCacheImageProvider
18 | extends ImageProvider
19 | implements image_provider.OptimizedCacheImageProvider {
20 | /// Creates an ImageProvider which loads an image from the [url], using the [scale].
21 | /// When the image fails to load [errorListener] is called.
22 | const OptimizedCacheImageProvider(
23 | this.url, {
24 | this.maxHeight,
25 | this.maxWidth,
26 | this.scale = 1.0,
27 | this.errorListener,
28 | this.headers,
29 | this.cacheManager,
30 | this.cacheKey,
31 | //ignore: avoid_unused_constructor_parameters
32 | ImageRenderMethodForWeb? imageRenderMethodForWeb,
33 | });
34 |
35 | @override
36 | final BaseCacheManager? cacheManager;
37 |
38 | /// Web url of the image to load
39 | @override
40 | final String url;
41 |
42 | /// Cache key of the image to cache
43 | @override
44 | final String? cacheKey;
45 |
46 | /// Scale of the image
47 | @override
48 | final double scale;
49 |
50 | /// Listener to be called when images fails to load.
51 | @override
52 | final image_provider.ErrorListener? errorListener;
53 |
54 | /// Set headers for the image provider, for example for authentication
55 | @override
56 | final Map? headers;
57 |
58 | @override
59 | final int? maxHeight;
60 |
61 | @override
62 | final int? maxWidth;
63 |
64 | @override
65 | Future obtainKey(
66 | ImageConfiguration configuration) {
67 | return SynchronousFuture(this);
68 | }
69 |
70 | @override
71 | ImageStreamCompleter loadImage(
72 | image_provider.OptimizedCacheImageProvider key,
73 | ImageDecoderCallback decode,
74 | ) {
75 | final chunkEvents = StreamController();
76 | return MultiImageStreamCompleter(
77 | codec: _loadAsync(key, chunkEvents, decode),
78 | chunkEvents: chunkEvents.stream,
79 | scale: key.scale,
80 | informationCollector: () sync* {
81 | yield DiagnosticsProperty(
82 | 'Image provider: $this \n Image key: $key',
83 | this,
84 | style: DiagnosticsTreeStyle.errorProperty,
85 | );
86 | },
87 | );
88 | }
89 |
90 | Stream _loadAsync(
91 | image_provider.OptimizedCacheImageProvider key,
92 | StreamController chunkEvents,
93 | ImageDecoderCallback decode,
94 | ) async* {
95 | assert(key == this);
96 | try {
97 | var mngr = cacheManager ?? DefaultImageCacheManager();
98 | assert(
99 | mngr is OicImageCacheManager ||
100 | (maxWidth == null && maxHeight == null),
101 | 'To resize the image with a CacheManager the '
102 | 'CacheManager needs to be an ImageCacheManager. maxWidth and '
103 | 'maxHeight will be ignored when a normal CacheManager is used.');
104 |
105 | var stream = mngr is OicImageCacheManager
106 | ? mngr.getImageFile(key.url,
107 | maxHeight: maxHeight,
108 | maxWidth: maxWidth,
109 | withProgress: true,
110 | headers: headers,
111 | key: key.cacheKey)
112 | : mngr.getFileStream(key.url,
113 | withProgress: true, headers: headers, key: key.cacheKey);
114 |
115 | await for (var result in stream) {
116 | if (result is DownloadProgress) {
117 | chunkEvents.add(ImageChunkEvent(
118 | cumulativeBytesLoaded: result.downloaded,
119 | expectedTotalBytes: result.totalSize,
120 | ));
121 | }
122 | if (result is FileInfo) {
123 | var file = result.file;
124 | var bytes =
125 | await ImmutableBuffer.fromUint8List(await file.readAsBytes());
126 | var decoded = await decode(bytes);
127 | yield decoded;
128 | }
129 | }
130 | } catch (e) {
131 | // Depending on where the exception was thrown, the image cache may not
132 | // have had a chance to track the key in the cache at all.
133 | // Schedule a microtask to give the cache a chance to add the key.
134 | scheduleMicrotask(() {
135 | PaintingBinding.instance.imageCache.evict(key);
136 | });
137 |
138 | errorListener?.call();
139 | rethrow;
140 | } finally {
141 | await chunkEvents.close();
142 | }
143 | }
144 |
145 | @override
146 | bool operator ==(dynamic other) {
147 | if (other is OptimizedCacheImageProvider) {
148 | return ((cacheKey ?? url) == (other.cacheKey ?? other.url)) &&
149 | scale == other.scale &&
150 | maxHeight == other.maxHeight &&
151 | maxWidth == other.maxWidth;
152 | }
153 | return false;
154 | }
155 |
156 | @override
157 | int get hashCode => Object.hash(cacheKey ?? url, scale, maxHeight, maxWidth);
158 |
159 | @override
160 | String toString() => '$runtimeType("$url", scale: $scale)';
161 | }
162 |
--------------------------------------------------------------------------------
/lib/src/image_provider/_image_provider_web.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:ui' as ui;
3 | import 'dart:ui';
4 |
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/painting.dart';
7 | import 'package:flutter_cache_manager/flutter_cache_manager.dart';
8 |
9 | import '../../optimized_cached_image.dart' show ImageRenderMethodForWeb;
10 | import '_load_async_web.dart';
11 | import 'multi_image_stream_completer.dart';
12 | import 'optimized_cached_image_provider.dart' as image_provider;
13 |
14 | /// The dart:html implementation of [image_provider.OptimizedCacheImageProvider].
15 | class OptimizedCacheImageProvider
16 | extends ImageProvider
17 | implements image_provider.OptimizedCacheImageProvider {
18 | /// Creates an object that fetches the image at the given URL.
19 | ///
20 | /// The arguments [url] and [scale] must not be null.
21 | const OptimizedCacheImageProvider(
22 | this.url, {
23 | this.maxHeight,
24 | this.maxWidth,
25 | this.scale = 1.0,
26 | this.errorListener,
27 | this.headers,
28 | this.cacheManager,
29 | this.cacheKey,
30 | ImageRenderMethodForWeb? imageRenderMethodForWeb,
31 | }) : _imageRenderMethodForWeb =
32 | imageRenderMethodForWeb ?? ImageRenderMethodForWeb.HtmlImage;
33 |
34 | @override
35 | final BaseCacheManager? cacheManager;
36 |
37 | @override
38 | final String url;
39 |
40 | @override
41 | final String? cacheKey;
42 |
43 | @override
44 | final double scale;
45 |
46 | /// Listener to be called when images fails to load.
47 | @override
48 | final image_provider.ErrorListener? errorListener;
49 |
50 | @override
51 | final Map? headers;
52 |
53 | @override
54 | final int? maxHeight;
55 |
56 | @override
57 | final int? maxWidth;
58 |
59 | final ImageRenderMethodForWeb _imageRenderMethodForWeb;
60 |
61 | @override
62 | Future obtainKey(
63 | ImageConfiguration configuration) {
64 | return SynchronousFuture(this);
65 | }
66 |
67 | @override
68 | ImageStreamCompleter loadImage(
69 | image_provider.OptimizedCacheImageProvider key,
70 | ImageDecoderCallback decode,
71 | ) {
72 | final chunkEvents = StreamController();
73 |
74 | return MultiImageStreamCompleter(
75 | chunkEvents: chunkEvents.stream,
76 | codec:
77 | _loadAsync(key as OptimizedCacheImageProvider, chunkEvents, decode),
78 | scale: key.scale,
79 | informationCollector: _imageStreamInformationCollector(key));
80 | }
81 |
82 | InformationCollector? _imageStreamInformationCollector(
83 | image_provider.OptimizedCacheImageProvider key) {
84 | InformationCollector? collector;
85 | assert(() {
86 | collector = () {
87 | return [
88 | DiagnosticsProperty('Image provider', this),
89 | DiagnosticsProperty(
90 | 'Image key', key as OptimizedCacheImageProvider),
91 | ];
92 | };
93 | return true;
94 | }());
95 | return collector;
96 | }
97 |
98 | Stream _loadAsync(
99 | OptimizedCacheImageProvider key,
100 | StreamController chunkEvents,
101 | ImageDecoderCallback decode,
102 | ) {
103 | switch (_imageRenderMethodForWeb) {
104 | case ImageRenderMethodForWeb.HttpGet:
105 | return _loadAsyncHttpGet(key, chunkEvents, decode);
106 | case ImageRenderMethodForWeb.HtmlImage:
107 | return loadAsyncHtmlImage(key, chunkEvents, decode).asStream();
108 | }
109 | }
110 |
111 | Stream _loadAsyncHttpGet(
112 | OptimizedCacheImageProvider key,
113 | StreamController chunkEvents,
114 | ImageDecoderCallback decode,
115 | ) async* {
116 | assert(key == this);
117 | try {
118 | var mngr = cacheManager ?? DefaultCacheManager();
119 | await for (var result in mngr.getFileStream(key.url,
120 | withProgress: true, headers: headers)) {
121 | if (result is DownloadProgress) {
122 | chunkEvents.add(ImageChunkEvent(
123 | cumulativeBytesLoaded: result.downloaded,
124 | expectedTotalBytes: result.totalSize,
125 | ));
126 | }
127 | if (result is FileInfo) {
128 | var file = result.file;
129 | var bytes =
130 | await ImmutableBuffer.fromUint8List(await file.readAsBytes());
131 | var decoded = await decode(bytes);
132 | yield decoded;
133 | }
134 | }
135 | } catch (e) {
136 | // Depending on where the exception was thrown, the image cache may not
137 | // have had a chance to track the key in the cache at all.
138 | // Schedule a microtask to give the cache a chance to add the key.
139 | scheduleMicrotask(() {
140 | PaintingBinding.instance.imageCache.evict(key);
141 | });
142 |
143 | errorListener?.call();
144 | rethrow;
145 | } finally {
146 | await chunkEvents.close();
147 | }
148 | }
149 |
150 | @override
151 | bool operator ==(Object other) {
152 | if (other.runtimeType != runtimeType) {
153 | return false;
154 | }
155 | if (other is OptimizedCacheImageProvider) {
156 | var sameKey = (cacheKey ?? url) == (other.cacheKey ?? other.url);
157 | return sameKey && scale == other.scale;
158 | } else {
159 | return false;
160 | }
161 | }
162 |
163 | @override
164 | int get hashCode => Object.hash(url, scale, cacheKey);
165 |
166 | @override
167 | String toString() => '$runtimeType("$url", scale: $scale)';
168 | }
169 |
--------------------------------------------------------------------------------
/lib/src/image_provider/_load_async_web.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:ui' as ui;
3 |
4 | import 'package:flutter/rendering.dart';
5 |
6 | import '../../optimized_cached_image.dart';
7 |
8 | /// Method to load html images using the webOnlyInstantiateImageCodecFromUrl.
9 | /// This method is not recognized by the flutter analyzer.
10 | Future loadAsyncHtmlImage(
11 | OptimizedCacheImageProvider key,
12 | StreamController chunkEvents,
13 | ImageDecoderCallback decode,
14 | ) {
15 | final resolved = Uri.base.resolve(key.url);
16 | // ignore: undefined_function
17 | return ui.webOnlyInstantiateImageCodecFromUrl(
18 | resolved,
19 | chunkCallback: (int bytes, int total) {
20 | chunkEvents.add(
21 | ImageChunkEvent(
22 | cumulativeBytesLoaded: bytes,
23 | expectedTotalBytes: total,
24 | ),
25 | );
26 | },
27 | ) as Future;
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/image_provider/multi_image_stream_completer.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:ui' as ui show Codec, FrameInfo;
3 |
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter/painting.dart';
6 | import 'package:flutter/scheduler.dart';
7 |
8 | /// Slows down animations by this factor to help in development.
9 | double get timeDilation => _timeDilation;
10 | double _timeDilation = 1.0;
11 |
12 | /// An ImageStreamCompleter with support for loading multiple images.
13 | class MultiImageStreamCompleter extends ImageStreamCompleter {
14 | /// The constructor to create an MultiImageStreamCompleter. The [codec]
15 | /// should be a stream with the images that should be shown. The
16 | /// [chunkEvents] should indicate the [ImageChunkEvent]s of the first image
17 | /// to show.
18 | MultiImageStreamCompleter({
19 | required Stream codec,
20 | required double scale,
21 | Stream? chunkEvents,
22 | InformationCollector? informationCollector,
23 | }) : _informationCollector = informationCollector,
24 | _scale = scale {
25 | codec.listen((event) {
26 | if (_timer != null) {
27 | _nextImageCodec = event;
28 | } else {
29 | _handleCodecReady(event);
30 | }
31 | }, onError: (dynamic error, StackTrace stack) {
32 | reportError(
33 | context: ErrorDescription('resolving an image codec'),
34 | exception: error,
35 | stack: stack,
36 | informationCollector: informationCollector,
37 | silent: true,
38 | );
39 | });
40 | if (chunkEvents != null) {
41 | chunkEvents.listen(
42 | reportImageChunkEvent,
43 | onError: (dynamic error, StackTrace stack) {
44 | reportError(
45 | context: ErrorDescription('loading an image'),
46 | exception: error,
47 | stack: stack,
48 | informationCollector: informationCollector,
49 | silent: true,
50 | );
51 | },
52 | );
53 | }
54 | }
55 |
56 | ui.Codec? _codec;
57 | ui.Codec? _nextImageCodec;
58 | final double _scale;
59 | final InformationCollector? _informationCollector;
60 | ui.FrameInfo? _nextFrame;
61 | // When the current was first shown.
62 | Duration? _shownTimestamp;
63 | // The requested duration for the current frame;
64 | Duration? _frameDuration;
65 | // How many frames have been emitted so far.
66 | int _framesEmitted = 0;
67 | Timer? _timer;
68 |
69 | // Used to guard against registering multiple _handleAppFrame callbacks for the same frame.
70 | bool _frameCallbackScheduled = false;
71 |
72 | void _switchToNewCodec() {
73 | _framesEmitted = 0;
74 | _timer = null;
75 | _handleCodecReady(_nextImageCodec!);
76 | _nextImageCodec = null;
77 | }
78 |
79 | void _handleCodecReady(ui.Codec codec) {
80 | _codec = codec;
81 | assert(_codec != null);
82 |
83 | if (hasListeners) {
84 | _decodeNextFrameAndSchedule();
85 | }
86 | }
87 |
88 | void _handleAppFrame(Duration timestamp) {
89 | _frameCallbackScheduled = false;
90 | if (!hasListeners) return;
91 | if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) {
92 | _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale));
93 | _shownTimestamp = timestamp;
94 | _frameDuration = _nextFrame!.duration;
95 | _nextFrame = null;
96 | if (_framesEmitted % _codec!.frameCount == 0 && _nextImageCodec != null) {
97 | _switchToNewCodec();
98 | } else {
99 | final completedCycles = _framesEmitted ~/ _codec!.frameCount;
100 | if (_codec!.repetitionCount == -1 ||
101 | completedCycles <= _codec!.repetitionCount) {
102 | _decodeNextFrameAndSchedule();
103 | }
104 | }
105 | return;
106 | }
107 | final delay = _frameDuration! - (timestamp - _shownTimestamp!);
108 | _timer = Timer(delay * timeDilation, _scheduleAppFrame);
109 | }
110 |
111 | bool _isFirstFrame() {
112 | return _frameDuration == null;
113 | }
114 |
115 | bool _hasFrameDurationPassed(Duration timestamp) {
116 | assert(_shownTimestamp != null);
117 | return timestamp - _shownTimestamp! >= _frameDuration!;
118 | }
119 |
120 | Future _decodeNextFrameAndSchedule() async {
121 | try {
122 | _nextFrame = await _codec!.getNextFrame();
123 | } catch (exception, stack) {
124 | reportError(
125 | context: ErrorDescription('resolving an image frame'),
126 | exception: exception,
127 | stack: stack,
128 | informationCollector: _informationCollector,
129 | silent: true,
130 | );
131 | return;
132 | }
133 | if (_codec!.frameCount == 1) {
134 | // ImageStreamCompleter listeners removed while waiting for next frame to
135 | // be decoded.
136 | // There's no reason to emit the frame without active listeners.
137 | if (!hasListeners) {
138 | return;
139 | }
140 |
141 | // This is not an animated image, just return it and don't schedule more
142 | // frames.
143 | _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale));
144 | return;
145 | }
146 | _scheduleAppFrame();
147 | }
148 |
149 | void _scheduleAppFrame() {
150 | if (_frameCallbackScheduled) {
151 | return;
152 | }
153 | _frameCallbackScheduled = true;
154 | SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
155 | }
156 |
157 | void _emitFrame(ImageInfo imageInfo) {
158 | setImage(imageInfo);
159 | _framesEmitted += 1;
160 | }
161 |
162 | @override
163 | void addListener(ImageStreamListener listener) {
164 | if (!hasListeners && _codec != null) _decodeNextFrameAndSchedule();
165 | super.addListener(listener);
166 | }
167 |
168 | @override
169 | void removeListener(ImageStreamListener listener) {
170 | super.removeListener(listener);
171 | if (!hasListeners) {
172 | _timer?.cancel();
173 | _timer = null;
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/lib/src/image_provider/optimized_cached_image_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/painting.dart';
2 | import 'package:flutter/widgets.dart';
3 | import 'package:flutter_cache_manager/flutter_cache_manager.dart';
4 |
5 | import '_image_provider_io.dart'
6 | if (dart.library.html) '_image_provider_web.dart' as image_provider;
7 |
8 | /// Function which is called after loading the image failed.
9 | typedef ErrorListener = void Function();
10 |
11 | /// Currently there are 2 different ways to show an image on the web with both
12 | /// their own pros and cons, using a custom [HttpGet]
13 | /// or an HTML Image element mentioned [here on a GitHub issue](https://github.com/flutter/flutter/issues/57187#issuecomment-635637494).
14 | ///
15 | /// When using HttpGet the image will work on Skia and it will use the [OptimizedCacheImageProvider.headers]
16 | /// when they are provided. In this package it also uses any url transformations that might
17 | /// be executed by the [OptimizedCacheImageProvider.cacheManager]. However, this method does require a CORS
18 | /// handshake and will not just work for every image from the web.
19 | ///
20 | /// The [HtmlImage] does not need a CORS handshake, but it also does not use your
21 | /// provided headers and it does not work when using Skia to render the page.
22 | enum ImageRenderMethodForWeb {
23 | /// HtmlImage uses a default web image including default browser caching.
24 | /// This is the recommended and default choice.
25 | HtmlImage,
26 |
27 | /// HttpGet uses an http client to fetch an image. It enables the use of
28 | /// headers, but loses some default web functionality.
29 | HttpGet,
30 | }
31 |
32 | /// An ImageProvider to load images from the network with caching functionality.
33 | abstract class OptimizedCacheImageProvider
34 | extends ImageProvider {
35 | /// Creates an object that fetches the image at the given URL.
36 | ///
37 | /// The arguments [url] and [scale] must not be null.
38 | /// The [imageRenderMethodForWeb] defines the behavior of the ImageProvider
39 | /// when compiled for web. See the documentation of [ImageRenderMethodForWeb]
40 | /// for the benefits of each method.
41 | const factory OptimizedCacheImageProvider(
42 | String url, {
43 | int? maxHeight,
44 | int? maxWidth,
45 | String? cacheKey,
46 | double scale,
47 | @Deprecated('ErrorListener is deprecated, use listeners on the imagestream')
48 | ErrorListener? errorListener,
49 | Map? headers,
50 | BaseCacheManager? cacheManager,
51 | ImageRenderMethodForWeb? imageRenderMethodForWeb,
52 | }) = image_provider.OptimizedCacheImageProvider;
53 |
54 | /// Optional cache manager. If no cache manager is defined DefaultCacheManager()
55 | /// will be used.
56 | ///
57 | /// When running flutter on the web, the cacheManager is not used.
58 | BaseCacheManager? get cacheManager;
59 |
60 | /// The errorListener is called when the ImageProvider failed loading the
61 | /// image. Deprecated in favor of [ImageStreamListener.onError].
62 | @deprecated
63 | ErrorListener? get errorListener;
64 |
65 | /// The URL from which the image will be fetched.
66 | String get url;
67 |
68 | /// The Key from image for cache
69 | String? get cacheKey;
70 |
71 | /// The scale to place in the [ImageInfo] object of the image.
72 | double get scale;
73 |
74 | /// The HTTP headers that will be used to fetch image from network.
75 | Map? get headers;
76 |
77 | /// Max height in pixels for the image. When set the resized image is
78 | /// stored in the cache.
79 | int? get maxHeight;
80 |
81 | /// Max width in pixels for the image. When set the resized image is
82 | /// stored in the cache.
83 | int? get maxWidth;
84 |
85 | @override
86 | ImageStreamCompleter loadImage(
87 | OptimizedCacheImageProvider key, ImageDecoderCallback decode);
88 | }
89 |
--------------------------------------------------------------------------------
/lib/src/oci_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:optimized_cached_image/optimized_cached_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/widgets.dart';
4 | import 'package:flutter_cache_manager/flutter_cache_manager.dart';
5 | import 'package:octo_image/octo_image.dart';
6 |
7 | /// Builder function to create an image widget. The function is called after
8 | /// the ImageProvider completes the image loading.
9 | typedef ImageWidgetBuilder = Widget Function(
10 | BuildContext context,
11 | ImageProvider imageProvider,
12 | );
13 |
14 | /// Builder function to create a placeholder widget. The function is called
15 | /// once while the ImageProvider is loading the image.
16 | typedef PlaceholderWidgetBuilder = Widget Function(
17 | BuildContext context,
18 | String url,
19 | );
20 |
21 | /// Builder function to create a progress indicator widget. The function is
22 | /// called every time a chuck of the image is downloaded from the web, but at
23 | /// least once during image loading.
24 | typedef ProgressIndicatorBuilder = Widget Function(
25 | BuildContext context,
26 | String url,
27 | DownloadProgress progress,
28 | );
29 |
30 | /// Builder function to create an error widget. This builder is called when
31 | /// the image failed loading, for example due to a 404 NotFound exception.
32 | typedef LoadingErrorWidgetBuilder = Widget Function(
33 | BuildContext context,
34 | String url,
35 | dynamic error,
36 | );
37 |
38 | /// Image widget to show NetworkImage with caching functionality.
39 | // ignore: must_be_immutable
40 | class OptimizedCacheImage extends StatelessWidget {
41 | /// Evict an image from both the disk file based caching system of the
42 | /// [BaseCacheManager] as the in memory [ImageCache] of the [ImageProvider].
43 | /// [url] is used by both the disk and memory cache. The scale is only used
44 | /// to clear the image from the [ImageCache].
45 | static Future evictFromCache(
46 | String url, {
47 | String? cacheKey,
48 | BaseCacheManager? cacheManager,
49 | double scale = 1.0,
50 | }) async {
51 | cacheManager = cacheManager ?? DefaultCacheManager();
52 | await cacheManager.removeFile(cacheKey ?? url);
53 | return OptimizedCacheImageProvider(url, scale: scale).evict();
54 | }
55 |
56 | OptimizedCacheImageProvider? _image;
57 |
58 | /// Option to use cachemanager with other settings
59 | final BaseCacheManager? cacheManager;
60 |
61 | /// The target image that is displayed.
62 | final String imageUrl;
63 |
64 | /// The target image's cache key.
65 | final String? cacheKey;
66 |
67 | /// Optional builder to further customize the display of the image.
68 | final ImageWidgetBuilder? imageBuilder;
69 |
70 | /// Widget displayed while the target [imageUrl] is loading.
71 | final PlaceholderWidgetBuilder? placeholder;
72 |
73 | /// Widget displayed while the target [imageUrl] is loading.
74 | final ProgressIndicatorBuilder? progressIndicatorBuilder;
75 |
76 | /// Widget displayed while the target [imageUrl] failed loading.
77 | final LoadingErrorWidgetBuilder? errorWidget;
78 |
79 | /// The duration of the fade-in animation for the [placeholder].
80 | final Duration? placeholderFadeInDuration;
81 |
82 | /// The duration of the fade-out animation for the [placeholder].
83 | final Duration? fadeOutDuration;
84 |
85 | /// The curve of the fade-out animation for the [placeholder].
86 | final Curve fadeOutCurve;
87 |
88 | /// The duration of the fade-in animation for the [imageUrl].
89 | final Duration fadeInDuration;
90 |
91 | /// The curve of the fade-in animation for the [imageUrl].
92 | final Curve fadeInCurve;
93 |
94 | /// If non-null, require the image to have this width.
95 | ///
96 | /// If null, the image will pick a size that best preserves its intrinsic
97 | /// aspect ratio. This may result in a sudden change if the size of the
98 | /// placeholder widget does not match that of the target image. The size is
99 | /// also affected by the scale factor.
100 | final double? width;
101 |
102 | /// If non-null, require the image to have this height.
103 | ///
104 | /// If null, the image will pick a size that best preserves its intrinsic
105 | /// aspect ratio. This may result in a sudden change if the size of the
106 | /// placeholder widget does not match that of the target image. The size is
107 | /// also affected by the scale factor.
108 | final double? height;
109 |
110 | /// How to inscribe the image into the space allocated during layout.
111 | ///
112 | /// The default varies based on the other fields. See the discussion at
113 | /// [paintImage].
114 | final BoxFit? fit;
115 |
116 | /// How to align the image within its bounds.
117 | ///
118 | /// The alignment aligns the given position in the image to the given position
119 | /// in the layout bounds. For example, a [Alignment] alignment of (-1.0,
120 | /// -1.0) aligns the image to the top-left corner of its layout bounds, while a
121 | /// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the
122 | /// image with the bottom right corner of its layout bounds. Similarly, an
123 | /// alignment of (0.0, 1.0) aligns the bottom middle of the image with the
124 | /// middle of the bottom edge of its layout bounds.
125 | ///
126 | /// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
127 | /// [AlignmentDirectional]), then an ambient [Directionality] widget
128 | /// must be in scope.
129 | ///
130 | /// Defaults to [Alignment.center].
131 | ///
132 | /// See also:
133 | ///
134 | /// * [Alignment], a class with convenient constants typically used to
135 | /// specify an [AlignmentGeometry].
136 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments
137 | /// relative to text direction.
138 | final AlignmentGeometry alignment;
139 |
140 | /// How to paint any portions of the layout bounds not covered by the image.
141 | final ImageRepeat repeat;
142 |
143 | /// Whether to paint the image in the direction of the [TextDirection].
144 | ///
145 | /// If this is true, then in [TextDirection.ltr] contexts, the image will be
146 | /// drawn with its origin in the top left (the "normal" painting direction for
147 | /// children); and in [TextDirection.rtl] contexts, the image will be drawn with
148 | /// a scaling factor of -1 in the horizontal direction so that the origin is
149 | /// in the top right.
150 | ///
151 | /// This is occasionally used with children in right-to-left environments, for
152 | /// children that were designed for left-to-right locales. Be careful, when
153 | /// using this, to not flip children with integral shadows, text, or other
154 | /// effects that will look incorrect when flipped.
155 | ///
156 | /// If this is true, there must be an ambient [Directionality] widget in
157 | /// scope.
158 | final bool matchTextDirection;
159 |
160 | /// Optional headers for the http request of the image url
161 | final Map? httpHeaders;
162 |
163 | /// When set to true it will animate from the old image to the new image
164 | /// if the url changes.
165 | final bool useOldImageOnUrlChange;
166 |
167 | /// If non-null, this color is blended with each image pixel using [colorBlendMode].
168 | final Color? color;
169 |
170 | /// Used to combine [color] with this image.
171 | ///
172 | /// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is
173 | /// the source and this image is the destination.
174 | ///
175 | /// See also:
176 | ///
177 | /// * [BlendMode], which includes an illustration of the effect of each blend mode.
178 | final BlendMode? colorBlendMode;
179 |
180 | /// Target the interpolation quality for image scaling.
181 | ///
182 | /// If not given a value, defaults to FilterQuality.low.
183 | final FilterQuality filterQuality;
184 |
185 | /// Will resize the image in memory to have a certain width using [ResizeImage]
186 | final int? memCacheWidth;
187 |
188 | /// Will resize the image in memory to have a certain height using [ResizeImage]
189 | final int? memCacheHeight;
190 |
191 | /// Will resize the image and store the resized image in the disk cache.
192 | final int? maxWidthDiskCache;
193 |
194 | /// Will resize the image and store the resized image in the disk cache.
195 | final int? maxHeightDiskCache;
196 |
197 | final ImageRenderMethodForWeb? imageRenderMethodForWeb;
198 |
199 | /// OptimizedCacheImage shows a network image using a caching mechanism. It also
200 | /// provides support for a placeholder, showing an error and fading into the
201 | /// loaded image. Next to that it supports most features of a default Image
202 | /// widget.
203 | OptimizedCacheImage({
204 | Key? key,
205 | required this.imageUrl,
206 | this.httpHeaders,
207 | this.imageBuilder,
208 | this.placeholder,
209 | this.progressIndicatorBuilder,
210 | this.errorWidget,
211 | this.fadeOutDuration = const Duration(milliseconds: 1000),
212 | this.fadeOutCurve = Curves.easeOut,
213 | this.fadeInDuration = const Duration(milliseconds: 500),
214 | this.fadeInCurve = Curves.easeIn,
215 | this.width,
216 | this.height,
217 | this.fit,
218 | this.alignment = Alignment.center,
219 | this.repeat = ImageRepeat.noRepeat,
220 | this.matchTextDirection = false,
221 | this.cacheManager,
222 | this.useOldImageOnUrlChange = false,
223 | this.color,
224 | this.filterQuality = FilterQuality.low,
225 | this.colorBlendMode,
226 | this.placeholderFadeInDuration,
227 | this.memCacheWidth,
228 | this.memCacheHeight,
229 | this.cacheKey,
230 | this.maxWidthDiskCache,
231 | this.maxHeightDiskCache,
232 | this.imageRenderMethodForWeb,
233 | }) : super(key: key);
234 |
235 | @override
236 | Widget build(BuildContext context) {
237 | var octoPlaceholderBuilder =
238 | placeholder != null ? _octoPlaceholderBuilder : null;
239 | var octoProgressIndicatorBuilder =
240 | progressIndicatorBuilder != null ? _octoProgressIndicatorBuilder : null;
241 |
242 | ///If there is no placeholder OctoImage does not fade, so always set an
243 | ///(empty) placeholder as this always used to be the behaviour of
244 | ///OptimizedCacheImage.
245 | if (octoPlaceholderBuilder == null &&
246 | octoProgressIndicatorBuilder == null) {
247 | octoPlaceholderBuilder = (context) => Container();
248 | }
249 |
250 | return LayoutBuilder(builder: (ctx, constraints) {
251 | int? _constrainWidth = width?.toInt() ?? maxWidthDiskCache;
252 | int? _constrainHeight = height?.toInt() ?? maxHeightDiskCache;
253 |
254 | if (_constrainWidth == null && _constrainHeight == null) {
255 | _constrainWidth = constraints.maxWidth != double.infinity
256 | ? constraints.maxWidth.toInt()
257 | : null;
258 | _constrainHeight = constraints.maxHeight != double.infinity
259 | ? constraints.maxHeight.toInt()
260 | : null;
261 | }
262 |
263 | final ratio = MediaQuery.of(context).devicePixelRatio;
264 | _constrainWidth =
265 | _constrainWidth != null ? (_constrainWidth * ratio).toInt() : null;
266 | _constrainHeight =
267 | _constrainHeight != null ? (_constrainHeight * ratio).toInt() : null;
268 |
269 | if (_image == null ||
270 | _image?.maxHeight != _constrainHeight ||
271 | _image?.maxWidth != _constrainHeight) {
272 | _image = OptimizedCacheImageProvider(
273 | imageUrl,
274 | headers: httpHeaders,
275 | cacheManager: cacheManager,
276 | cacheKey: cacheKey,
277 | imageRenderMethodForWeb: imageRenderMethodForWeb,
278 | maxWidth: _constrainWidth,
279 | maxHeight: _constrainHeight,
280 | );
281 | }
282 | return OctoImage(
283 | image: _image!,
284 | imageBuilder: imageBuilder != null ? _octoImageBuilder : null,
285 | placeholderBuilder: octoPlaceholderBuilder,
286 | progressIndicatorBuilder: octoProgressIndicatorBuilder,
287 | errorBuilder: errorWidget != null ? _octoErrorBuilder : null,
288 | fadeOutDuration: fadeOutDuration,
289 | fadeOutCurve: fadeOutCurve,
290 | fadeInDuration: fadeInDuration,
291 | fadeInCurve: fadeInCurve,
292 | width: width,
293 | height: height,
294 | fit: fit,
295 | alignment: alignment as Alignment?,
296 | repeat: repeat,
297 | matchTextDirection: matchTextDirection,
298 | color: color,
299 | filterQuality: filterQuality,
300 | colorBlendMode: colorBlendMode,
301 | placeholderFadeInDuration: placeholderFadeInDuration,
302 | gaplessPlayback: useOldImageOnUrlChange,
303 | );
304 | });
305 | }
306 |
307 | Widget _octoImageBuilder(BuildContext context, Widget child) {
308 | return imageBuilder!(context, _image!);
309 | }
310 |
311 | Widget _octoPlaceholderBuilder(BuildContext context) {
312 | return placeholder!(context, imageUrl);
313 | }
314 |
315 | Widget _octoProgressIndicatorBuilder(
316 | BuildContext context,
317 | ImageChunkEvent? progress,
318 | ) {
319 | int? totalSize;
320 | var downloaded = 0;
321 | if (progress != null) {
322 | totalSize = progress.expectedTotalBytes;
323 | downloaded = progress.cumulativeBytesLoaded;
324 | }
325 | return progressIndicatorBuilder!(
326 | context, imageUrl, DownloadProgress(imageUrl, totalSize, downloaded));
327 | }
328 |
329 | Widget _octoErrorBuilder(
330 | BuildContext context,
331 | Object error,
332 | StackTrace? stackTrace,
333 | ) {
334 | return errorWidget!(context, imageUrl, error);
335 | }
336 | }
337 |
--------------------------------------------------------------------------------
/lib/src/transformer/image_transformer.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:file/local.dart' as fileIo;
5 | import 'package:flutter_cache_manager/flutter_cache_manager.dart' show FileInfo;
6 | import 'package:flutter_image_compress/flutter_image_compress.dart';
7 | import 'package:path/path.dart' as p;
8 | import 'package:sprintf/sprintf.dart';
9 |
10 | import '../debug_tools.dart';
11 | import 'scale_info.dart';
12 |
13 | class DefaultImageTransformer extends ImageTransformer {
14 | DefaultImageTransformer();
15 |
16 | final tmpFileSuffix = '_w%d_h%d';
17 |
18 | final _compressionFormats = {
19 | '.jpg': CompressFormat.jpeg,
20 | '.jpeg': CompressFormat.jpeg,
21 | '.webp': CompressFormat.webp,
22 | '.gif': CompressFormat.webp,
23 | '.png': CompressFormat.png,
24 | '.heic': CompressFormat.heic
25 | };
26 | final _extensionFormats = {
27 | CompressFormat.jpeg: '.jpg',
28 | CompressFormat.webp: '.webp',
29 | CompressFormat.png: '.png',
30 | CompressFormat.heic: '.heic'
31 | };
32 |
33 | @override
34 | Future transform(FileInfo info, int? width, int? height) async {
35 | final value = await _scaleImageFile(info, width, height);
36 | return value;
37 | }
38 |
39 | Future _scaleImageFile(
40 | FileInfo info, int? width, int? height) async {
41 | FileInfo fileInfo = info;
42 | final file = fileInfo.file;
43 | log("Scaling file.. ${fileInfo.originalUrl}");
44 | var resizedFile = file;
45 | final canResize = file.existsSync() && (width != null || height != null);
46 | if (canResize) {
47 | final scaleInfo = getScaledFileInfo(file, width, height);
48 | final srcSize = file.lengthSync();
49 | log("Dimensions width=${scaleInfo.width}, height=${scaleInfo.height}, format ${scaleInfo.compressFormat}");
50 | await FlutterImageCompress.compressAndGetFile(
51 | file.path, scaleInfo.file.path,
52 | minWidth: scaleInfo.width,
53 | minHeight: scaleInfo.height,
54 | format: scaleInfo.compressFormat,
55 | quality: 90);
56 | final localFileSystem = fileIo.LocalFileSystem();
57 | resizedFile = localFileSystem.file(scaleInfo.file.path);
58 |
59 | if (resizedFile.existsSync()) {
60 | if (resizedFile.lengthSync() < srcSize) {
61 | log("Resized success ${fileInfo.originalUrl}");
62 | } else {
63 | log("Resized image is bigger, deleting and using original ${fileInfo.originalUrl}");
64 | resizedFile.deleteSync();
65 | resizedFile = file;
66 | }
67 | } else {
68 | resizedFile = file;
69 | log("Resize Failure for ${fileInfo.originalUrl}");
70 | }
71 | }
72 | fileInfo = FileInfo(
73 | resizedFile, fileInfo.source, fileInfo.validTill, fileInfo.originalUrl);
74 | return fileInfo;
75 | }
76 |
77 | @override
78 | ScaleInfo getScaledFileInfo(File file, int? width, int? height) {
79 | final format = _getCompressionFormat(file);
80 |
81 | final directory = file.parent;
82 | final destPath = directory.path +
83 | "/" +
84 | p.basenameWithoutExtension(file.path) +
85 | sprintf(tmpFileSuffix, [width ?? 1, height ?? 1]) +
86 | _extensionFormats[format]!;
87 | final scaleFile = File(destPath);
88 | return ScaleInfo(scaleFile, width ?? 1, height ?? 1, format);
89 | }
90 |
91 | CompressFormat _getCompressionFormat(File file) {
92 | String extension = p.extension(file.path);
93 | return _compressionFormats[extension] ?? CompressFormat.png;
94 | }
95 | }
96 |
97 | abstract class ImageTransformer {
98 | Future transform(FileInfo info, int? width, int? height);
99 | ScaleInfo getScaledFileInfo(File file, int width, int height);
100 | }
101 |
--------------------------------------------------------------------------------
/lib/src/transformer/scale_info.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter_image_compress/flutter_image_compress.dart';
4 |
5 | class ScaleInfo {
6 | final File file;
7 | final int width;
8 | final int height;
9 | final CompressFormat compressFormat;
10 |
11 | const ScaleInfo(this.file, this.width, this.height, this.compressFormat);
12 | }
13 |
--------------------------------------------------------------------------------
/optimized_cached_image.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: optimized_cached_image
2 | description: A library for loading images from network, resizing as per container size and caching while being memory sensitive.
3 | version: 3.0.1
4 | homepage: https://github.com/humblerookie/optimized_cached_image
5 |
6 | environment:
7 | sdk: ">=3.0.0 <4.0.0"
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 | flutter_cache_manager: ^3.3.0
12 | flutter_image_compress: ^2.0.3
13 | uuid: ^3.0.3
14 | http: ^0.13.1
15 | path_provider: ^2.0.15
16 | path: ^1.8.0
17 | pedantic: ^1.11.0
18 | sprintf: ^7.0.0
19 | octo_image: ^1.0.0
20 | image: ^4.0.17
21 | file: ^6.1.0
22 | dev_dependencies:
23 | test: ^1.16.8
--------------------------------------------------------------------------------
/screenshots/streamed_vs_nonstreamed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humblerookie/optimized_cached_image/c760d1d3a1b525cc1e71c93a808135d1f10d4d1e/screenshots/streamed_vs_nonstreamed.jpg
--------------------------------------------------------------------------------
/scripts/checks.sh:
--------------------------------------------------------------------------------
1 | dartfmt -w --fix .;
2 |
3 |
--------------------------------------------------------------------------------