├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── bk_flutter_image_android.iml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── bk
│ │ └── flutter
│ │ └── bk_texture_image
│ │ ├── BkCallArgs.java
│ │ ├── BkFlutterImagePlugin.java
│ │ ├── BkLogger.java
│ │ ├── BkLruCache.java
│ │ ├── BkTextureRecord.java
│ │ └── BkTextureRecordManager.java
│ └── res
│ └── drawable
│ └── ic_launcher.png
├── bk_flutter_image.iml
├── example
├── .gitignore
├── .metadata
├── .vscode
│ └── settings.json
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ ├── com
│ │ │ │ │ └── example
│ │ │ │ │ │ └── bk_flutter_image_example
│ │ │ │ │ │ └── MainActivity.java
│ │ │ │ └── io
│ │ │ │ │ └── flutter
│ │ │ │ │ └── plugins
│ │ │ │ │ └── GeneratedPluginRegistrant.java
│ │ │ └── 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.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── local.properties
│ └── settings.gradle
├── images
│ ├── 00.jpg
│ ├── 01.jpg
│ ├── 02.jpg
│ ├── 03.jpg
│ ├── 04.jpg
│ ├── 05.jpg
│ ├── 06.jpg
│ ├── 07.jpg
│ ├── 08.jpg
│ └── 09.jpg
├── ios
│ ├── Flutter
│ │ ├── .last_build_id
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── beike.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ └── 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
│ │ ├── GeneratedPluginRegistrant.h
│ │ ├── GeneratedPluginRegistrant.m
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ ├── exts.dart
│ └── main.dart
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── ios
├── Assets
│ ├── .gitkeep
│ ├── glsl.fsh
│ └── glsl.vsh
├── Classes
│ ├── BkFlutterImagePlugin.h
│ ├── BkFlutterImagePlugin.m
│ ├── BkFlutterPixelBufferCache.h
│ ├── BkFlutterPixelBufferCache.m
│ ├── BkImageRenderWorker.h
│ └── BkImageRenderWorker.m
└── bk_flutter_image.podspec
├── lib
├── bk_flutter_image.dart
├── bk_flutter_image_native.dart
└── bk_flutter_image_web.dart
├── pubspec.yaml
└── test
└── bk_flutter_image_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .idea/
10 | pubspec.lock
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.1
2 |
3 | * TODO: Describe initial release.
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright(c) 2017 Lianjia, Inc. All Rights Reserved
2 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
4 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bk_flutter_image——Flutter图片内存优化库
2 |
3 | ## 背景
4 | 随着移动端业务中更多Flutter应用, 多图、大图复杂页面使用Flutter的Image.network(..) , FadeInImage.network(..)易出现出现OOM问题
5 | * [实践](https://mp.weixin.qq.com/s/yUm4UFggYLgDbj4_JCjEdg)
6 |
7 | ## 指南
8 |
9 | ### Android依赖 Glide 4.11.0
10 |
11 | ```gradle
12 | dependencies {
13 | implementation 'com.github.bumptech.glide:glide:4.11.0'
14 | }
15 | ```
16 |
17 | ### iOS 依赖 SDWebImage 5.12.6及以上 https://github.com/SDWebImage/SDWebImage/issues/3351
18 | pod 'SDWebImage','5.12.6'
19 |
20 | ### 使用方式
21 |
22 | ```
23 | BkFlutterImage(
24 | url: imageUrl,
25 | width: width,
26 | height: height,
27 | autoResize: true,
28 | ...
29 | )
30 | ```
31 |
32 | ## License
33 |
34 | 详情参见 [LICENSE](./LICENSE)。
35 |
36 | ## 版本历史
37 | 具体版本历史请参看 [CHANGELOG.md](./CHANGELOG.md)。
38 |
--------------------------------------------------------------------------------
/android/bk_flutter_image_android.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 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.ke.flutter.texture_image'
2 | version '1.0'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.5.0'
12 | }
13 | }
14 |
15 | rootProject.allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | apply plugin: 'com.android.library'
23 |
24 | android {
25 | compileSdkVersion 28
26 |
27 | defaultConfig {
28 | minSdkVersion 21
29 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30 | }
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 | }
39 |
40 | dependencies {
41 | implementation 'com.github.bumptech.glide:glide:4.11.0'
42 | }
43 |
--------------------------------------------------------------------------------
/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-5.6.2-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | package android
2 |
3 | rootProject.name = 'bk_flutter_image'
4 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/com/bk/flutter/bk_texture_image/BkCallArgs.java:
--------------------------------------------------------------------------------
1 | package com.bk.flutter.bk_texture_image;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import io.flutter.plugin.common.MethodCall;
6 |
7 | public class BkCallArgs {
8 |
9 | MethodCall call;
10 | String url;
11 | int width;
12 | int height;
13 | boolean autoResize;
14 |
15 | long textureId;
16 |
17 | public BkCallArgs(@NonNull MethodCall call) {
18 | this.call = call;
19 | this.url = call.argument("url");
20 |
21 | if (call.argument("width") != null) {
22 | double w = call.argument("width");
23 | this.width = (int) w;
24 | }
25 |
26 | if (call.argument("height") != null) {
27 | double h = call.argument("height");
28 | this.height = (int) h;
29 | }
30 |
31 | if (call.argument("autoResize") != null) {
32 | this.autoResize = call.argument("autoResize");
33 | }
34 |
35 | }
36 |
37 | public long getTextureId() {
38 | return textureId;
39 | }
40 |
41 | public void setTextureId(long textureId) {
42 | this.textureId = textureId;
43 | }
44 |
45 | public String uniqueKey() {
46 | return String.valueOf(url);
47 | }
48 |
49 | public boolean hasReusableTexture(BkCallArgs targetArgs) {
50 | return (this.url.equals(targetArgs.url) && width >= targetArgs.width && height >= targetArgs.height);
51 | }
52 |
53 | @Override
54 | public String toString() {
55 | return "CallArgs{" +
56 | "url='" + url + '\'' +
57 | ", width=" + width +
58 | ", height=" + height +
59 | ", autoResize=" + autoResize +
60 | ", textureId=" + textureId +
61 | '}';
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/android/src/main/java/com/bk/flutter/bk_texture_image/BkFlutterImagePlugin.java:
--------------------------------------------------------------------------------
1 | package com.bk.flutter.bk_texture_image;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.PaintFlagsDrawFilter;
8 | import android.graphics.Rect;
9 | import android.graphics.SurfaceTexture;
10 | import android.os.Debug;
11 | import android.view.Surface;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.annotation.Nullable;
15 |
16 | import com.bumptech.glide.Glide;
17 | import com.bumptech.glide.MemoryCategory;
18 | import com.bumptech.glide.load.DataSource;
19 | import com.bumptech.glide.load.engine.DiskCacheStrategy;
20 | import com.bumptech.glide.load.engine.GlideException;
21 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
22 | import com.bumptech.glide.load.engine.cache.MemoryCache;
23 | import com.bumptech.glide.load.resource.bitmap.BitmapEncoder;
24 | import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
25 | import com.bumptech.glide.request.RequestListener;
26 | import com.bumptech.glide.request.RequestOptions;
27 | import com.bumptech.glide.request.target.Target;
28 | import com.bumptech.glide.util.Executors;
29 | import com.bumptech.glide.util.Util;
30 |
31 | import io.flutter.embedding.engine.plugins.activity.ActivityAware;
32 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
33 |
34 | import javax.microedition.khronos.opengles.GL10;
35 |
36 | import io.flutter.embedding.engine.plugins.FlutterPlugin;
37 | import io.flutter.plugin.common.MethodCall;
38 | import io.flutter.plugin.common.MethodChannel;
39 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
40 | import io.flutter.plugin.common.MethodChannel.Result;
41 | import io.flutter.plugin.common.PluginRegistry.Registrar;
42 | import io.flutter.view.TextureRegistry;
43 |
44 | /**
45 | * BkFlutterTextureImageViewPlugin
46 | */
47 | public class BkFlutterImagePlugin implements FlutterPlugin, MethodCallHandler,
48 | ActivityAware {
49 |
50 | private static final String CHANNEL_NAME = "bk_flutter_image";
51 | private static final String METHOD_CREATE = "create";
52 | private static final String METHOD_DISPOSE = "dispose";
53 | private static final String METHOD_CACHE = "setCacheSize";
54 | private static final String METHOD_UPDATE = "updateUrl";
55 | private static final String TAG = "BkFlutterImagePlugin";
56 |
57 | // surfaceTexture 宽高限制
58 | private static final int MAX_PIXELS = Math.min(GL10.GL_MAX_VIEWPORT_DIMS, GL10.GL_MAX_TEXTURE_SIZE);
59 | private static final int LIMIT_NATIVE_HEAP_SIZE = 157286400; //[经验值] 内存大于150MB的时候清理bitmap内存
60 | private Context mContext;
61 | // private Activity mActivity;
62 | private TextureRegistry mTextureRegistry;
63 | private float density;
64 | private int widthPixels;
65 | private int heightPixels;
66 |
67 | private BkTextureRecordManager mRecordManager;
68 |
69 | public BkFlutterImagePlugin() {
70 | mRecordManager = new BkTextureRecordManager();
71 | }
72 |
73 | @Override
74 | public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
75 | final MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_NAME);
76 | channel.setMethodCallHandler(this);
77 | mContext = flutterPluginBinding.getApplicationContext();
78 | /// 插件接口获取texture注册器
79 | mTextureRegistry = flutterPluginBinding.getTextureRegistry();
80 |
81 | density = mContext.getResources().getDisplayMetrics().density;
82 | widthPixels = mContext.getResources().getDisplayMetrics().widthPixels;
83 | heightPixels = mContext.getResources().getDisplayMetrics().heightPixels;
84 | }
85 |
86 | public static void registerWith(Registrar registrar) {
87 | throw new IllegalArgumentException("Error, please use v2 FlutterActivity!!");
88 | }
89 |
90 | @Override
91 | public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
92 | try {
93 | if (METHOD_CREATE.equals(call.method)) {
94 | createTextureImage(call, result);
95 | } else if (METHOD_DISPOSE.equals(call.method)) {
96 | disposeTextureImage(call, result);
97 | } else if (METHOD_CACHE.equals(call.method)) {
98 | setMaxCacheSize(call, result);
99 | }else {
100 | result.notImplemented();
101 | }
102 | } catch (Exception e) {
103 | e.printStackTrace();
104 | }
105 | }
106 |
107 | private void setMaxCacheSize(MethodCall call, Result result) {
108 | double memoryMaxSize = call.argument("memoryMaxSize");;
109 | double diskMaxSize = call.argument("diskMaxSize");
110 | mRecordManager.setCacheMaxSize(diskMaxSize, memoryMaxSize);
111 | }
112 |
113 | private void createTextureImage(MethodCall call, Result result) {
114 | BkCallArgs args = new BkCallArgs(call);
115 |
116 | BkTextureRecord textureRecord = mRecordManager.findReusableByCall(args);
117 |
118 | if (textureRecord != null && textureRecord.isValid()) {
119 | safeReply(result, textureRecord.response());
120 | } else {
121 | BkTextureRecord record = new BkTextureRecord(args, mTextureRegistry.createSurfaceTexture());
122 | mRecordManager.add(record);
123 | args.setTextureId(record.getTextureId());
124 | loadTextureImage(args, result, record);
125 | }
126 |
127 | }
128 |
129 | private void disposeTextureImage(MethodCall call, Result result) {
130 | try {
131 | final int textureId = call.argument("textureId");
132 | BkTextureRecord record = mRecordManager.maybeCacheAndReleaseOldest(textureId);
133 | if (record != null) {
134 | BkLogger.d("release texture memory : " + record);
135 | }
136 | } catch (Exception e) {
137 | e.printStackTrace();
138 | }
139 | }
140 |
141 | private void loadTextureImage(final BkCallArgs callArgs, final MethodChannel.Result result, final BkTextureRecord textureRecord) {
142 |
143 | final String url = callArgs.url;
144 |
145 | Glide.get(mContext).setMemoryCategory(MemoryCategory.LOW);
146 | final boolean autoResize = callArgs.autoResize;
147 | textureRecord.setFullPixel(!autoResize);
148 | RequestOptions options = new RequestOptions()
149 | .diskCacheStrategy(DiskCacheStrategy.ALL)
150 | .downsample(DownsampleStrategy.DEFAULT);
151 | if (autoResize) {
152 | options = options.override(Math.min(callArgs.width, MAX_PIXELS), Math.min(callArgs.height, MAX_PIXELS));
153 | }
154 |
155 | Glide.with(mContext).asBitmap().load(url).listener(new RequestListener() {
156 | @Override
157 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
158 | BkLogger.d("glide onLoadFailed callArgs=" + callArgs.toString());
159 | textureRecord.setTextureValid(false);
160 | textureRecord.setError(e != null ? e.getMessage() : "");
161 | safeReply(result, textureRecord.response());
162 | return true;
163 | }
164 |
165 | @Override
166 | public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
167 | try {
168 | int targetWidth = Math.min(resource.getWidth(), MAX_PIXELS);
169 | int targetHeight = Math.min(resource.getHeight(), MAX_PIXELS);
170 | textureRecord.setTextureWH(targetWidth, targetHeight);
171 |
172 | BkLogger.d("glide onResourceReady w:h = " + targetWidth + ":" + targetHeight + ", fr:" + isFirstResource + ", ds:" +dataSource);
173 | // 画布显示区域
174 | Rect canvasRect = new Rect(0, 0, targetWidth, targetHeight);
175 | SurfaceTexture surfaceTexture = textureRecord.getTextureEntry().surfaceTexture();
176 | Surface surface = new Surface(surfaceTexture);
177 | if (!surface.isValid()) {
178 | BkLogger.d("surface invalid");
179 | return false;
180 | }
181 | surfaceTexture.setDefaultBufferSize(targetWidth, targetHeight);
182 | Canvas canvas = surface.lockCanvas(canvasRect);
183 |
184 | // 图片显示区域
185 | Rect dstRect = new Rect(0, 0, targetWidth, targetHeight);
186 |
187 | // 图片是否scale ?
188 | canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
189 | canvas.drawBitmap(resource, null, dstRect, null);
190 |
191 | surface.unlockCanvasAndPost(canvas);
192 | surface.release();
193 |
194 | safeReply(result, textureRecord.response());
195 |
196 | } catch (Exception e) {
197 | e.printStackTrace();
198 | BkLogger.d("glide onResourceReady Exception callArgs=" + callArgs.toString());
199 | //result.success(-1);
200 | //标志纹理失败
201 | textureRecord.setError(e.getMessage());
202 | textureRecord.setTextureValid(false);
203 | safeReply(result, textureRecord.response());
204 | } finally {
205 | long nativeHeapAllocatedSize = Debug.getNativeHeapAllocatedSize();
206 | if (nativeHeapAllocatedSize >= LIMIT_NATIVE_HEAP_SIZE) {
207 | BkLogger.d("NativeHeapAllocatedSize: " + (nativeHeapAllocatedSize >> 20) + "MB");
208 | clearGlideMemory();
209 | }
210 | }
211 |
212 | return false;
213 | }
214 | }).apply(options).preload();
215 |
216 |
217 | }
218 | @Override
219 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
220 | mContext = null;
221 | mTextureRegistry = null;
222 | cleanResource();
223 | }
224 |
225 | private void safeReply(final MethodChannel.Result result, final String response) {
226 | Executors.mainThreadExecutor().execute(new Runnable() {
227 | @Override
228 | public void run() {
229 | try {
230 | result.success(response);
231 | } catch (Exception e) {
232 | BkLogger.d("safeReply MainLooper Exception =" + e.getLocalizedMessage());
233 | }
234 | }
235 | });
236 | }
237 |
238 | private void cleanResource() {
239 | mRecordManager.clean();
240 | }
241 |
242 | private void clearGlideMemory() {
243 | try {
244 | if (Util.isOnMainThread()) {
245 | Glide.get(mContext).clearMemory();
246 | }
247 | } catch (Exception e) {
248 | BkLogger.d("clearGlideMemory : " + e.getCause());
249 | }
250 | }
251 |
252 | @Override
253 | public void onAttachedToActivity(ActivityPluginBinding binding) {
254 |
255 | }
256 |
257 | @Override
258 | public void onDetachedFromActivityForConfigChanges() {
259 | clearGlideMemory();
260 | }
261 |
262 | @Override
263 | public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
264 |
265 | }
266 |
267 | @Override
268 | public void onDetachedFromActivity() {
269 | clearGlideMemory();
270 | }
271 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/bk/flutter/bk_texture_image/BkLogger.java:
--------------------------------------------------------------------------------
1 | package com.bk.flutter.bk_texture_image;
2 |
3 | import io.flutter.Log;
4 |
5 | public final class BkLogger {
6 | private static final String TAG = "TextureImageLogger";
7 | private static final boolean DEVELOP = BuildConfig.DEBUG;
8 |
9 | public static void i(Object msg) {
10 | if (DEVELOP) {
11 | android.util.Log.i(TAG, String.valueOf(msg));
12 | } else {
13 | Log.i(TAG, String.valueOf(msg));
14 | }
15 | }
16 |
17 | public static void d(Object msg) {
18 | if (DEVELOP) {
19 | android.util.Log.d(TAG, String.valueOf(msg));
20 | } else {
21 | Log.d(TAG, String.valueOf(msg));
22 | }
23 | }
24 |
25 | public static void v(Object msg) {
26 | if (DEVELOP) {
27 | android.util.Log.v(TAG, String.valueOf(msg));
28 | } else {
29 | Log.v(TAG, String.valueOf(msg));
30 | }
31 | }
32 |
33 | public static void w(Object msg) {
34 | if (DEVELOP) {
35 | android.util.Log.w(TAG, String.valueOf(msg));
36 | } else {
37 | Log.w(TAG, String.valueOf(msg));
38 | }
39 | }
40 |
41 | public static void e(Object msg) {
42 | if (DEVELOP) {
43 | android.util.Log.e(TAG, String.valueOf(msg));
44 | } else {
45 | Log.e(TAG, String.valueOf(msg));
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/android/src/main/java/com/bk/flutter/bk_texture_image/BkLruCache.java:
--------------------------------------------------------------------------------
1 | package com.bk.flutter.bk_texture_image;
2 |
3 | import android.util.LruCache;
4 |
5 | public class BkLruCache extends LruCache {
6 |
7 | /**
8 | * @param maxSize for caches that do not override {@link #sizeOf}, this is
9 | * the maximum number of entries in the cache. For all other caches,
10 | * this is the maximum sum of the sizes of the entries in this cache.
11 | */
12 | public BkLruCache(int maxSize) {
13 | super(maxSize);
14 | }
15 |
16 | @Override
17 | protected void entryRemoved(boolean evicted, BkCallArgs key, BkTextureRecord oldValue, BkTextureRecord newValue) {
18 | super.entryRemoved(evicted, key, oldValue, newValue);
19 | oldValue.tryRelease();
20 | }
21 |
22 | @Override
23 | protected int sizeOf(BkCallArgs key, BkTextureRecord textureRecord) {
24 | return textureRecord.sizeOfMegaBytes();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/android/src/main/java/com/bk/flutter/bk_texture_image/BkTextureRecord.java:
--------------------------------------------------------------------------------
1 | package com.bk.flutter.bk_texture_image;
2 |
3 |
4 | import android.os.Build.VERSION;
5 | import android.os.Build.VERSION_CODES;
6 |
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import java.util.concurrent.atomic.AtomicBoolean;
11 | import java.util.concurrent.atomic.AtomicInteger;
12 |
13 | import io.flutter.view.TextureRegistry;
14 |
15 | public class BkTextureRecord {
16 | // 同一个纹理多次使用计数,当最后一个dispose时才移除
17 | private AtomicInteger refCount = new AtomicInteger(1);
18 | // 纹理是否有效
19 | private final AtomicBoolean textureValid = new AtomicBoolean(true);
20 |
21 | private TextureRegistry.SurfaceTextureEntry textureEntry;
22 |
23 | private BkCallArgs call;
24 | private long textureId = 0L;
25 | private double textureWidth = 0L;
26 | private double textureHeight = 0L;
27 | private boolean isFullPixel = false;
28 | private String mError;
29 |
30 | public BkTextureRecord(BkCallArgs call, TextureRegistry.SurfaceTextureEntry textureEntry) {
31 | this.textureEntry = textureEntry;
32 | this.call = call;
33 | }
34 |
35 | public int refIncrementAndGet() {
36 | return refCount.incrementAndGet();
37 | }
38 |
39 | public int refDecrementAndGet() {
40 | return refCount.decrementAndGet();
41 | }
42 |
43 | public AtomicBoolean getTextureValid() {
44 | return textureValid;
45 | }
46 |
47 | public void setTextureValid(boolean valid) {
48 | this.textureValid.set(valid);
49 | }
50 |
51 | public boolean isValid() {
52 | return textureEntry != null && textureValid.get();
53 | }
54 |
55 | public TextureRegistry.SurfaceTextureEntry getTextureEntry() {
56 | textureId = textureEntry.id();
57 | return textureEntry;
58 | }
59 |
60 | public void setTextureEntry(TextureRegistry.SurfaceTextureEntry textureEntry) {
61 | this.textureEntry = textureEntry;
62 | }
63 |
64 | public BkCallArgs getCall() {
65 | return call;
66 | }
67 |
68 | public long getTextureId() {
69 | return textureId;
70 | }
71 |
72 | public void tryRelease() {
73 | if (textureEntry != null && textureEntry.surfaceTexture() != null) {
74 | if (VERSION.SDK_INT >= VERSION_CODES.O) {
75 | if (!textureEntry.surfaceTexture().isReleased()) {
76 | textureEntry.release();
77 | }
78 | } else {
79 | textureEntry.release();
80 | }
81 | }
82 | }
83 |
84 | public void setTextureWH(double w, double h) {
85 | textureWidth = w;
86 | textureHeight = h;
87 | }
88 |
89 | public double getTextureHeight() {
90 | return textureHeight;
91 | }
92 |
93 | public double getTextureWidth() {
94 | return textureWidth;
95 | }
96 |
97 | public void setFullPixel(boolean fullPixel) {
98 | isFullPixel = fullPixel;
99 | }
100 |
101 | public void setError(String error) {
102 | this.mError = error;
103 | }
104 |
105 | /**
106 | * 纹理占用内容(MB)
107 | */
108 | public int sizeOfMegaBytes() {
109 | return (int) (textureWidth * textureHeight * 4) >> 20;
110 | }
111 |
112 | public String response() {
113 | JSONObject jsonObject = new JSONObject();
114 | try {
115 | jsonObject.put("textureId", isValid() ? textureId : -1);
116 | jsonObject.put("textureWidth", textureWidth);
117 | jsonObject.put("textureHeight", textureHeight);
118 | jsonObject.put("isFullPixel", isFullPixel);
119 | jsonObject.put("error", mError);
120 | } catch (JSONException e) {
121 | e.printStackTrace();
122 | }
123 | return jsonObject.toString();
124 | }
125 |
126 | @Override
127 | public String toString() {
128 | return "TextureRecord{" +
129 | "refCount=" + refCount.get() +
130 | ", textureValid=" + textureValid.get() +
131 | ", textureId=" + textureEntry.id() +
132 | ", callArgs='" + call + '\'' +
133 | '}';
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/android/src/main/java/com/bk/flutter/bk_texture_image/BkTextureRecordManager.java:
--------------------------------------------------------------------------------
1 | package com.bk.flutter.bk_texture_image;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 |
5 |
6 | public class BkTextureRecordManager {
7 |
8 | private static final int DEF_CACHE_SIZE = 268435456; // 256MB
9 | private static final int MAX_CACHE_SIZE = 1073741824; // 1024MB
10 | private final ConcurrentHashMap textureRecords = new ConcurrentHashMap<>();
11 | private final BkLruCache textureCacheRecords = new BkLruCache(DEF_CACHE_SIZE);
12 |
13 | BkTextureRecordManager() {}
14 |
15 | public void add(BkTextureRecord record) {
16 | textureRecords.put(record.getCall(), record);
17 | }
18 |
19 | public BkTextureRecord remove(BkTextureRecord record) {
20 | return textureRecords.remove(record.getCall());
21 | }
22 |
23 | public BkTextureRecord findReusableByCall(BkCallArgs key) {
24 | for (BkCallArgs bkCallArgs : textureRecords.keySet()) {
25 | if (bkCallArgs.hasReusableTexture(key)) {
26 | return textureRecords.get(bkCallArgs);
27 | }
28 | }
29 | for (BkCallArgs bkCallArgs : textureCacheRecords.snapshot().keySet()) {
30 | // 如果找到可复用的纹理直接返回
31 | if (bkCallArgs.hasReusableTexture(key)) {
32 | return textureCacheRecords.get(bkCallArgs);
33 | }
34 | }
35 | return null;
36 | }
37 |
38 | /**
39 | * 通过纹理 id 找到
40 | * @param textureId
41 | * @return
42 | */
43 | public BkCallArgs findCallById(long textureId) {
44 | for (BkCallArgs bkCallArgs : textureRecords.keySet()) {
45 | if (textureId == bkCallArgs.getTextureId()) {
46 | return bkCallArgs;
47 | }
48 | }
49 | for (BkCallArgs bkCallArgs : textureCacheRecords.snapshot().keySet()) {
50 | if (textureId == bkCallArgs.getTextureId()) {
51 | return bkCallArgs;
52 | }
53 | }
54 | return null;
55 | }
56 |
57 | /**
58 | *
59 | * @param textureId: 待释放纹理 id
60 | * @return BkTextureRecord: 从缓存中清理的记录
61 | */
62 | public BkTextureRecord maybeCacheAndReleaseOldest(long textureId) {
63 | BkCallArgs bkCallArgs = findCallById(textureId);
64 | if (bkCallArgs == null) {
65 | return null;
66 | }
67 | BkTextureRecord bkTextureRecord = textureRecords.get(bkCallArgs);
68 | textureRecords.remove(bkCallArgs);
69 | if (bkTextureRecord != null) {
70 | return textureCacheRecords.put(bkCallArgs, bkTextureRecord);
71 | }
72 | return null;
73 | }
74 |
75 | public void clean() {
76 | textureRecords.clear();
77 | }
78 |
79 | /**
80 | * diskCacheMaxSize 磁盘缓存Android暂不关注.
81 | * memoryCacheMaxSize 设置 LruCache MaxSize, 当纹理需要释放时我们优先将其缓存起来
82 | */
83 | public void setCacheMaxSize(double diskCacheMaxSize, double memoryCacheMaxSize) {
84 | if (memoryCacheMaxSize > MAX_CACHE_SIZE) {
85 | BkLogger.w(String.format("memoryCacheMaxSize > %s, set to MAX_CACHE_SIZE: %s", MAX_CACHE_SIZE, MAX_CACHE_SIZE));
86 | this.textureCacheRecords.resize(MAX_CACHE_SIZE);
87 | } else {
88 | this.textureCacheRecords.resize((int) memoryCacheMaxSize);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/android/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/android/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/bk_flutter_image.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/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/Pods/*
33 | ios/Runner.xcworkspace/xcuserdata/*
34 | ios/.symlinks/
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Exceptions to above rules.
39 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
40 |
--------------------------------------------------------------------------------
/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: f139b11009aeb8ed2a3a3aa8b0066e482709dde3
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # bk_flutter_image_example
2 |
3 | Demonstrates how to use the bk_flutter_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/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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 |
31 | lintOptions {
32 | disable 'InvalidPackage'
33 | }
34 |
35 | compileOptions {
36 | sourceCompatibility JavaVersion.VERSION_1_8
37 | targetCompatibility JavaVersion.VERSION_1_8
38 | }
39 |
40 | defaultConfig {
41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
42 | applicationId "com.example.bk_flutter_image_example"
43 | minSdkVersion 21
44 | targetSdkVersion 28
45 | versionCode flutterVersionCode.toInteger()
46 | versionName flutterVersionName
47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
48 | }
49 |
50 | buildTypes {
51 | release {
52 | // TODO: Add your own signing config for the release build.
53 | // Signing with the debug keys for now, so `flutter run --release` works.
54 | signingConfig signingConfigs.debug
55 | }
56 | }
57 | }
58 |
59 | flutter {
60 | source '../..'
61 | }
62 |
63 | dependencies {
64 | 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 |
9 |
10 |
11 |
12 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/bk_flutter_image_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.bk_flutter_image_example;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.annotation.NonNull;
6 | import io.flutter.embedding.android.FlutterActivity;
7 | import io.flutter.embedding.engine.FlutterEngine;
8 | import io.flutter.plugins.GeneratedPluginRegistrant;
9 |
10 | public class MainActivity extends FlutterActivity {
11 | @Override
12 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
13 | GeneratedPluginRegistrant.registerWith(flutterEngine);
14 | }
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | try {
20 | Class.forName("dalvik.system.CloseGuard")
21 | .getMethod("setEnabled", boolean.class)
22 | .invoke(null, true);
23 | } catch (ReflectiveOperationException e) {
24 | throw new RuntimeException(e);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java:
--------------------------------------------------------------------------------
1 | package io.flutter.plugins;
2 |
3 | import androidx.annotation.Keep;
4 | import androidx.annotation.NonNull;
5 | import io.flutter.Log;
6 |
7 | import io.flutter.embedding.engine.FlutterEngine;
8 |
9 | /**
10 | * Generated file. Do not edit.
11 | * This file is generated by the Flutter tool based on the
12 | * plugins that support the Android platform.
13 | */
14 | @Keep
15 | public final class GeneratedPluginRegistrant {
16 | private static final String TAG = "GeneratedPluginRegistrant";
17 | public static void registerWith(@NonNull FlutterEngine flutterEngine) {
18 | try {
19 | flutterEngine.getPlugins().add(new com.bk.flutter.bk_texture_image.BkFlutterImagePlugin());
20 | } catch(Exception e) {
21 | Log.e(TAG, "Error registering plugin bk_flutter_image, com.bk.flutter.bk_texture_image.BkFlutterImagePlugin", e);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | google()
16 | jcenter()
17 | }
18 | }
19 |
20 | rootProject.buildDir = '../build'
21 | subprojects {
22 | project.buildDir = "${rootProject.buildDir}/${project.name}"
23 | }
24 | subprojects {
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/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.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/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-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/example/android/local.properties:
--------------------------------------------------------------------------------
1 | sdk.dir=/Users/zhaohongwei/Library/Android/sdk
2 | flutter.sdk=/Users/zhaohongwei/.flutterw/lianjia/2.10.4.5
--------------------------------------------------------------------------------
/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/images/00.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/00.jpg
--------------------------------------------------------------------------------
/example/images/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/01.jpg
--------------------------------------------------------------------------------
/example/images/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/02.jpg
--------------------------------------------------------------------------------
/example/images/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/03.jpg
--------------------------------------------------------------------------------
/example/images/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/04.jpg
--------------------------------------------------------------------------------
/example/images/05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/05.jpg
--------------------------------------------------------------------------------
/example/images/06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/06.jpg
--------------------------------------------------------------------------------
/example/images/07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/07.jpg
--------------------------------------------------------------------------------
/example/images/08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/08.jpg
--------------------------------------------------------------------------------
/example/images/09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/images/09.jpg
--------------------------------------------------------------------------------
/example/ios/Flutter/.last_build_id:
--------------------------------------------------------------------------------
1 | 4405250439844095dccd0762deff4833
--------------------------------------------------------------------------------
/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 | 8.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, '10.0'
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 flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_ios_podfile_setup
28 |
29 | target 'Runner' do
30 | pod 'SDWebImage', '5.12.6'
31 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
32 | end
33 |
34 | post_install do |installer|
35 | installer.pods_project.targets.each do |target|
36 | flutter_additional_ios_build_settings(target)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 5715BEDF2832790100A48E87 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5715BEDE2832790100A48E87 /* CoreGraphics.framework */; };
13 | 5715BEE228327A8500A48E87 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5715BEE128327A8500A48E87 /* CoreServices.framework */; };
14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
16 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
17 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
18 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
19 | A9A3ADC9B5F84455289AC94B /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B5759CD057BF385707014CC /* libPods-Runner.a */; };
20 | E482E97D24EE7522009BF6BF /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXCopyFilesBuildPhase section */
24 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
25 | isa = PBXCopyFilesBuildPhase;
26 | buildActionMask = 2147483647;
27 | dstPath = "";
28 | dstSubfolderSpec = 10;
29 | files = (
30 | );
31 | name = "Embed Frameworks";
32 | runOnlyForDeploymentPostprocessing = 0;
33 | };
34 | /* End PBXCopyFilesBuildPhase section */
35 |
36 | /* Begin PBXFileReference section */
37 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
38 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
39 | 2CCFF51D683FE8585F6C845D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
40 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
41 | 3B5759CD057BF385707014CC /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
43 | 5715BEDE2832790100A48E87 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
44 | 5715BEE128327A8500A48E87 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
45 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
46 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
47 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
48 | 7BAA500530288766DF596E24 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
49 | 7EF06221AEFF8DE7B023EAA7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
50 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
51 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
52 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
53 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | 5715BEDF2832790100A48E87 /* CoreGraphics.framework in Frameworks */,
66 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
67 | E482E97D24EE7522009BF6BF /* App.framework in Frameworks */,
68 | 5715BEE228327A8500A48E87 /* CoreServices.framework in Frameworks */,
69 | A9A3ADC9B5F84455289AC94B /* libPods-Runner.a in Frameworks */,
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | /* End PBXFrameworksBuildPhase section */
74 |
75 | /* Begin PBXGroup section */
76 | 27CCEDA1863563A67524BED3 /* Frameworks */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 5715BEE128327A8500A48E87 /* CoreServices.framework */,
80 | 5715BEDE2832790100A48E87 /* CoreGraphics.framework */,
81 | 3B5759CD057BF385707014CC /* libPods-Runner.a */,
82 | );
83 | name = Frameworks;
84 | sourceTree = "";
85 | };
86 | 9740EEB11CF90186004384FC /* Flutter */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 3B80C3931E831B6300D905FE /* App.framework */,
90 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
91 | 9740EEBA1CF902C7004384FC /* Flutter.framework */,
92 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
93 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
94 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
95 | );
96 | name = Flutter;
97 | sourceTree = "";
98 | };
99 | 97C146E51CF9000F007C117D = {
100 | isa = PBXGroup;
101 | children = (
102 | 9740EEB11CF90186004384FC /* Flutter */,
103 | 97C146F01CF9000F007C117D /* Runner */,
104 | 97C146EF1CF9000F007C117D /* Products */,
105 | DD5A77578AE4E06F588FC829 /* Pods */,
106 | 27CCEDA1863563A67524BED3 /* Frameworks */,
107 | );
108 | sourceTree = "";
109 | };
110 | 97C146EF1CF9000F007C117D /* Products */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 97C146EE1CF9000F007C117D /* Runner.app */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 97C146F01CF9000F007C117D /* Runner */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
122 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
123 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
124 | 97C147021CF9000F007C117D /* Info.plist */,
125 | 97C146F11CF9000F007C117D /* Supporting Files */,
126 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
127 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
128 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
129 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
130 | );
131 | path = Runner;
132 | sourceTree = "";
133 | };
134 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
135 | isa = PBXGroup;
136 | children = (
137 | );
138 | name = "Supporting Files";
139 | sourceTree = "";
140 | };
141 | DD5A77578AE4E06F588FC829 /* Pods */ = {
142 | isa = PBXGroup;
143 | children = (
144 | 7BAA500530288766DF596E24 /* Pods-Runner.debug.xcconfig */,
145 | 7EF06221AEFF8DE7B023EAA7 /* Pods-Runner.release.xcconfig */,
146 | 2CCFF51D683FE8585F6C845D /* Pods-Runner.profile.xcconfig */,
147 | );
148 | path = Pods;
149 | sourceTree = "";
150 | };
151 | /* End PBXGroup section */
152 |
153 | /* Begin PBXNativeTarget section */
154 | 97C146ED1CF9000F007C117D /* Runner */ = {
155 | isa = PBXNativeTarget;
156 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
157 | buildPhases = (
158 | CACC213BD4D747A2309C8921 /* [CP] Check Pods Manifest.lock */,
159 | 9740EEB61CF901F6004384FC /* Run Script */,
160 | 97C146EA1CF9000F007C117D /* Sources */,
161 | 97C146EB1CF9000F007C117D /* Frameworks */,
162 | 97C146EC1CF9000F007C117D /* Resources */,
163 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
164 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
165 | EAB7C4205C42D36A094F8DDB /* [CP] Copy Pods Resources */,
166 | );
167 | buildRules = (
168 | );
169 | dependencies = (
170 | );
171 | name = Runner;
172 | productName = Runner;
173 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
174 | productType = "com.apple.product-type.application";
175 | };
176 | /* End PBXNativeTarget section */
177 |
178 | /* Begin PBXProject section */
179 | 97C146E61CF9000F007C117D /* Project object */ = {
180 | isa = PBXProject;
181 | attributes = {
182 | LastUpgradeCheck = 1020;
183 | ORGANIZATIONNAME = "The Chromium Authors";
184 | TargetAttributes = {
185 | 97C146ED1CF9000F007C117D = {
186 | CreatedOnToolsVersion = 7.3.1;
187 | LastSwiftMigration = 1100;
188 | ProvisioningStyle = Manual;
189 | };
190 | };
191 | };
192 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
193 | compatibilityVersion = "Xcode 3.2";
194 | developmentRegion = en;
195 | hasScannedForEncodings = 0;
196 | knownRegions = (
197 | en,
198 | Base,
199 | );
200 | mainGroup = 97C146E51CF9000F007C117D;
201 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
202 | projectDirPath = "";
203 | projectRoot = "";
204 | targets = (
205 | 97C146ED1CF9000F007C117D /* Runner */,
206 | );
207 | };
208 | /* End PBXProject section */
209 |
210 | /* Begin PBXResourcesBuildPhase section */
211 | 97C146EC1CF9000F007C117D /* Resources */ = {
212 | isa = PBXResourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
216 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
217 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXShellScriptBuildPhase section */
225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | inputPaths = (
231 | );
232 | name = "Thin Binary";
233 | outputPaths = (
234 | );
235 | runOnlyForDeploymentPostprocessing = 0;
236 | shellPath = /bin/sh;
237 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n\n";
238 | };
239 | 9740EEB61CF901F6004384FC /* Run Script */ = {
240 | isa = PBXShellScriptBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | );
244 | inputPaths = (
245 | );
246 | name = "Run Script";
247 | outputPaths = (
248 | );
249 | runOnlyForDeploymentPostprocessing = 0;
250 | shellPath = /bin/sh;
251 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
252 | };
253 | CACC213BD4D747A2309C8921 /* [CP] Check Pods Manifest.lock */ = {
254 | isa = PBXShellScriptBuildPhase;
255 | buildActionMask = 2147483647;
256 | files = (
257 | );
258 | inputFileListPaths = (
259 | );
260 | inputPaths = (
261 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
262 | "${PODS_ROOT}/Manifest.lock",
263 | );
264 | name = "[CP] Check Pods Manifest.lock";
265 | outputFileListPaths = (
266 | );
267 | outputPaths = (
268 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
269 | );
270 | runOnlyForDeploymentPostprocessing = 0;
271 | shellPath = /bin/sh;
272 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
273 | showEnvVarsInLog = 0;
274 | };
275 | EAB7C4205C42D36A094F8DDB /* [CP] Copy Pods Resources */ = {
276 | isa = PBXShellScriptBuildPhase;
277 | buildActionMask = 2147483647;
278 | files = (
279 | );
280 | inputPaths = (
281 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
282 | "${PODS_CONFIGURATION_BUILD_DIR}/bk_flutter_image/BkFlutterImage.bundle",
283 | );
284 | name = "[CP] Copy Pods Resources";
285 | outputPaths = (
286 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/BkFlutterImage.bundle",
287 | );
288 | runOnlyForDeploymentPostprocessing = 0;
289 | shellPath = /bin/sh;
290 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
291 | showEnvVarsInLog = 0;
292 | };
293 | /* End PBXShellScriptBuildPhase section */
294 |
295 | /* Begin PBXSourcesBuildPhase section */
296 | 97C146EA1CF9000F007C117D /* Sources */ = {
297 | isa = PBXSourcesBuildPhase;
298 | buildActionMask = 2147483647;
299 | files = (
300 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
301 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
302 | );
303 | runOnlyForDeploymentPostprocessing = 0;
304 | };
305 | /* End PBXSourcesBuildPhase section */
306 |
307 | /* Begin PBXVariantGroup section */
308 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
309 | isa = PBXVariantGroup;
310 | children = (
311 | 97C146FB1CF9000F007C117D /* Base */,
312 | );
313 | name = Main.storyboard;
314 | sourceTree = "";
315 | };
316 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
317 | isa = PBXVariantGroup;
318 | children = (
319 | 97C147001CF9000F007C117D /* Base */,
320 | );
321 | name = LaunchScreen.storyboard;
322 | sourceTree = "";
323 | };
324 | /* End PBXVariantGroup section */
325 |
326 | /* Begin XCBuildConfiguration section */
327 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
328 | isa = XCBuildConfiguration;
329 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
330 | buildSettings = {
331 | ALWAYS_SEARCH_USER_PATHS = NO;
332 | CLANG_ANALYZER_NONNULL = YES;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
338 | CLANG_WARN_BOOL_CONVERSION = YES;
339 | CLANG_WARN_COMMA = YES;
340 | CLANG_WARN_CONSTANT_CONVERSION = YES;
341 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
342 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
343 | CLANG_WARN_EMPTY_BODY = YES;
344 | CLANG_WARN_ENUM_CONVERSION = YES;
345 | CLANG_WARN_INFINITE_RECURSION = YES;
346 | CLANG_WARN_INT_CONVERSION = YES;
347 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
348 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
349 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
351 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
352 | CLANG_WARN_STRICT_PROTOTYPES = YES;
353 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
354 | CLANG_WARN_UNREACHABLE_CODE = YES;
355 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
356 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
357 | COPY_PHASE_STRIP = NO;
358 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
359 | ENABLE_NS_ASSERTIONS = NO;
360 | ENABLE_STRICT_OBJC_MSGSEND = YES;
361 | GCC_C_LANGUAGE_STANDARD = gnu99;
362 | GCC_NO_COMMON_BLOCKS = YES;
363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
365 | GCC_WARN_UNDECLARED_SELECTOR = YES;
366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
367 | GCC_WARN_UNUSED_FUNCTION = YES;
368 | GCC_WARN_UNUSED_VARIABLE = YES;
369 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
370 | MTL_ENABLE_DEBUG_INFO = NO;
371 | SDKROOT = iphoneos;
372 | SUPPORTED_PLATFORMS = iphoneos;
373 | TARGETED_DEVICE_FAMILY = "1,2";
374 | VALIDATE_PRODUCT = YES;
375 | };
376 | name = Profile;
377 | };
378 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
379 | isa = XCBuildConfiguration;
380 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
381 | buildSettings = {
382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
383 | CLANG_ENABLE_MODULES = YES;
384 | CODE_SIGN_IDENTITY = "Apple Development";
385 | CODE_SIGN_STYLE = Automatic;
386 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
387 | DEVELOPMENT_TEAM = W4PCLY57NK;
388 | ENABLE_BITCODE = NO;
389 | FRAMEWORK_SEARCH_PATHS = (
390 | "$(inherited)",
391 | "$(PROJECT_DIR)/Flutter",
392 | );
393 | INFOPLIST_FILE = Runner/Info.plist;
394 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
396 | LIBRARY_SEARCH_PATHS = (
397 | "$(inherited)",
398 | "$(PROJECT_DIR)/Flutter",
399 | );
400 | PRODUCT_BUNDLE_IDENTIFIER = com.zhw.test3;
401 | PRODUCT_NAME = "$(TARGET_NAME)";
402 | PROVISIONING_PROFILE_SPECIFIER = "";
403 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
404 | SWIFT_VERSION = 5.0;
405 | VERSIONING_SYSTEM = "apple-generic";
406 | };
407 | name = Profile;
408 | };
409 | 97C147031CF9000F007C117D /* Debug */ = {
410 | isa = XCBuildConfiguration;
411 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
412 | buildSettings = {
413 | ALWAYS_SEARCH_USER_PATHS = NO;
414 | CLANG_ANALYZER_NONNULL = YES;
415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
416 | CLANG_CXX_LIBRARY = "libc++";
417 | CLANG_ENABLE_MODULES = YES;
418 | CLANG_ENABLE_OBJC_ARC = YES;
419 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
420 | CLANG_WARN_BOOL_CONVERSION = YES;
421 | CLANG_WARN_COMMA = YES;
422 | CLANG_WARN_CONSTANT_CONVERSION = YES;
423 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
424 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
425 | CLANG_WARN_EMPTY_BODY = YES;
426 | CLANG_WARN_ENUM_CONVERSION = YES;
427 | CLANG_WARN_INFINITE_RECURSION = YES;
428 | CLANG_WARN_INT_CONVERSION = YES;
429 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
430 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
431 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
432 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
433 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
434 | CLANG_WARN_STRICT_PROTOTYPES = YES;
435 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
436 | CLANG_WARN_UNREACHABLE_CODE = YES;
437 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
438 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
439 | COPY_PHASE_STRIP = NO;
440 | DEBUG_INFORMATION_FORMAT = dwarf;
441 | ENABLE_STRICT_OBJC_MSGSEND = YES;
442 | ENABLE_TESTABILITY = YES;
443 | GCC_C_LANGUAGE_STANDARD = gnu99;
444 | GCC_DYNAMIC_NO_PIC = NO;
445 | GCC_NO_COMMON_BLOCKS = YES;
446 | GCC_OPTIMIZATION_LEVEL = 0;
447 | GCC_PREPROCESSOR_DEFINITIONS = (
448 | "DEBUG=1",
449 | "$(inherited)",
450 | );
451 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
452 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
453 | GCC_WARN_UNDECLARED_SELECTOR = YES;
454 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
455 | GCC_WARN_UNUSED_FUNCTION = YES;
456 | GCC_WARN_UNUSED_VARIABLE = YES;
457 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
458 | MTL_ENABLE_DEBUG_INFO = YES;
459 | ONLY_ACTIVE_ARCH = YES;
460 | SDKROOT = iphoneos;
461 | TARGETED_DEVICE_FAMILY = "1,2";
462 | };
463 | name = Debug;
464 | };
465 | 97C147041CF9000F007C117D /* Release */ = {
466 | isa = XCBuildConfiguration;
467 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
468 | buildSettings = {
469 | ALWAYS_SEARCH_USER_PATHS = NO;
470 | CLANG_ANALYZER_NONNULL = YES;
471 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
472 | CLANG_CXX_LIBRARY = "libc++";
473 | CLANG_ENABLE_MODULES = YES;
474 | CLANG_ENABLE_OBJC_ARC = YES;
475 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
476 | CLANG_WARN_BOOL_CONVERSION = YES;
477 | CLANG_WARN_COMMA = YES;
478 | CLANG_WARN_CONSTANT_CONVERSION = YES;
479 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
480 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
481 | CLANG_WARN_EMPTY_BODY = YES;
482 | CLANG_WARN_ENUM_CONVERSION = YES;
483 | CLANG_WARN_INFINITE_RECURSION = YES;
484 | CLANG_WARN_INT_CONVERSION = YES;
485 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
486 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
487 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
488 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
489 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
490 | CLANG_WARN_STRICT_PROTOTYPES = YES;
491 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
492 | CLANG_WARN_UNREACHABLE_CODE = YES;
493 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
494 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
495 | COPY_PHASE_STRIP = NO;
496 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
497 | ENABLE_NS_ASSERTIONS = NO;
498 | ENABLE_STRICT_OBJC_MSGSEND = YES;
499 | GCC_C_LANGUAGE_STANDARD = gnu99;
500 | GCC_NO_COMMON_BLOCKS = YES;
501 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
502 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
503 | GCC_WARN_UNDECLARED_SELECTOR = YES;
504 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
505 | GCC_WARN_UNUSED_FUNCTION = YES;
506 | GCC_WARN_UNUSED_VARIABLE = YES;
507 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
508 | MTL_ENABLE_DEBUG_INFO = NO;
509 | SDKROOT = iphoneos;
510 | SUPPORTED_PLATFORMS = iphoneos;
511 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
512 | TARGETED_DEVICE_FAMILY = "1,2";
513 | VALIDATE_PRODUCT = YES;
514 | };
515 | name = Release;
516 | };
517 | 97C147061CF9000F007C117D /* Debug */ = {
518 | isa = XCBuildConfiguration;
519 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
520 | buildSettings = {
521 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
522 | CLANG_ENABLE_MODULES = YES;
523 | CODE_SIGN_IDENTITY = "Apple Development";
524 | CODE_SIGN_STYLE = Manual;
525 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
526 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
527 | DEVELOPMENT_TEAM = "";
528 | ENABLE_BITCODE = NO;
529 | FRAMEWORK_SEARCH_PATHS = (
530 | "$(inherited)",
531 | "$(PROJECT_DIR)/Flutter",
532 | );
533 | INFOPLIST_FILE = Runner/Info.plist;
534 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
535 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
536 | LIBRARY_SEARCH_PATHS = (
537 | "$(inherited)",
538 | "$(PROJECT_DIR)/Flutter",
539 | );
540 | PRODUCT_BUNDLE_IDENTIFIER = com.zhw.test3;
541 | PRODUCT_NAME = "$(TARGET_NAME)";
542 | PROVISIONING_PROFILE_SPECIFIER = "";
543 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
544 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
545 | SWIFT_VERSION = 5.0;
546 | VERSIONING_SYSTEM = "apple-generic";
547 | };
548 | name = Debug;
549 | };
550 | 97C147071CF9000F007C117D /* Release */ = {
551 | isa = XCBuildConfiguration;
552 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
553 | buildSettings = {
554 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
555 | CLANG_ENABLE_MODULES = YES;
556 | CODE_SIGN_IDENTITY = "Apple Development";
557 | CODE_SIGN_STYLE = Manual;
558 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
559 | DEVELOPMENT_TEAM = "";
560 | ENABLE_BITCODE = NO;
561 | FRAMEWORK_SEARCH_PATHS = (
562 | "$(inherited)",
563 | "$(PROJECT_DIR)/Flutter",
564 | );
565 | INFOPLIST_FILE = Runner/Info.plist;
566 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
567 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
568 | LIBRARY_SEARCH_PATHS = (
569 | "$(inherited)",
570 | "$(PROJECT_DIR)/Flutter",
571 | );
572 | ONLY_ACTIVE_ARCH = YES;
573 | PRODUCT_BUNDLE_IDENTIFIER = com.zhw.test3;
574 | PRODUCT_NAME = "$(TARGET_NAME)";
575 | PROVISIONING_PROFILE_SPECIFIER = "";
576 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
577 | SWIFT_VERSION = 5.0;
578 | VERSIONING_SYSTEM = "apple-generic";
579 | };
580 | name = Release;
581 | };
582 | /* End XCBuildConfiguration section */
583 |
584 | /* Begin XCConfigurationList section */
585 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
586 | isa = XCConfigurationList;
587 | buildConfigurations = (
588 | 97C147031CF9000F007C117D /* Debug */,
589 | 97C147041CF9000F007C117D /* Release */,
590 | 249021D3217E4FDB00AE95B9 /* Profile */,
591 | );
592 | defaultConfigurationIsVisible = 0;
593 | defaultConfigurationName = Release;
594 | };
595 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
596 | isa = XCConfigurationList;
597 | buildConfigurations = (
598 | 97C147061CF9000F007C117D /* Debug */,
599 | 97C147071CF9000F007C117D /* Release */,
600 | 249021D4217E4FDB00AE95B9 /* Profile */,
601 | );
602 | defaultConfigurationIsVisible = 0;
603 | defaultConfigurationName = Release;
604 | };
605 | /* End XCConfigurationList section */
606 | };
607 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
608 | }
609 |
--------------------------------------------------------------------------------
/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 |
37 |
38 |
39 |
40 |
41 |
42 |
53 |
55 |
61 |
62 |
63 |
64 |
67 |
68 |
69 |
70 |
74 |
75 |
79 |
80 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcuserdata/beike.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/ios/Runner.xcworkspace/xcuserdata/beike.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcuserdata/beike.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
25 |
37 |
38 |
39 |
41 |
53 |
54 |
55 |
57 |
69 |
70 |
71 |
73 |
85 |
86 |
87 |
89 |
101 |
102 |
103 |
105 |
117 |
118 |
119 |
121 |
129 |
130 |
131 |
133 |
141 |
142 |
143 |
145 |
150 |
151 |
152 |
154 |
166 |
167 |
168 |
170 |
182 |
183 |
184 |
186 |
198 |
199 |
200 |
202 |
214 |
215 |
216 |
218 |
230 |
231 |
232 |
234 |
246 |
247 |
248 |
250 |
262 |
263 |
264 |
266 |
278 |
279 |
280 |
282 |
294 |
295 |
296 |
298 |
310 |
311 |
312 |
314 |
326 |
327 |
328 |
330 |
342 |
343 |
344 |
346 |
358 |
359 |
360 |
362 |
374 |
375 |
376 |
378 |
390 |
391 |
392 |
394 |
406 |
407 |
408 |
410 |
422 |
423 |
424 |
426 |
438 |
439 |
440 |
442 |
454 |
455 |
456 |
457 |
458 |
--------------------------------------------------------------------------------
/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/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/GeneratedPluginRegistrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GeneratedPluginRegistrant_h
8 | #define GeneratedPluginRegistrant_h
9 |
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface GeneratedPluginRegistrant : NSObject
15 | + (void)registerWithRegistry:(NSObject*)registry;
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 | #endif /* GeneratedPluginRegistrant_h */
20 |
--------------------------------------------------------------------------------
/example/ios/Runner/GeneratedPluginRegistrant.m:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #import "GeneratedPluginRegistrant.h"
8 |
9 | #if __has_include()
10 | #import
11 | #else
12 | @import bk_flutter_image;
13 | #endif
14 |
15 | @implementation GeneratedPluginRegistrant
16 |
17 | + (void)registerWithRegistry:(NSObject*)registry {
18 | [BkFlutterImagePlugin registerWithRegistrar:[registry registrarForPlugin:@"BkFlutterImagePlugin"]];
19 | }
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/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 | bk_flutter_image_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 |
45 |
46 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/example/lib/exts.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 | import 'package:bk_flutter_image/bk_flutter_image.dart';
7 |
8 | enum GridDemoTileStyle {
9 | deleteMode,
10 | selectMode
11 | }
12 |
13 | typedef BannerTapCallback = void Function(Photo photo);
14 |
15 | const double _kMinFlingVelocity = 800.0;
16 | const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
17 |
18 | class Photo {
19 | Photo({
20 | this.url,
21 | this.assetPackage,
22 | this.title,
23 | this.caption,
24 | this.isFavorite = false,
25 | this.mode=BoxFit.fill,
26 | });
27 |
28 | final String url;
29 | final String assetPackage;
30 | final String title;
31 | final String caption;
32 | final BoxFit mode;
33 |
34 | bool isFavorite;
35 | String get tag => url; // Assuming that all asset names are unique.
36 |
37 | bool get isValid => url != null && title != null && caption != null && isFavorite != null;
38 | }
39 |
40 | class GridPhotoViewer extends StatefulWidget {
41 | const GridPhotoViewer({ Key key, this.photo }) : super(key: key);
42 |
43 | final Photo photo;
44 |
45 | @override
46 | _GridPhotoViewerState createState() => _GridPhotoViewerState();
47 | }
48 |
49 | class _GridTitleText extends StatelessWidget {
50 | const _GridTitleText(this.text);
51 |
52 | final String text;
53 |
54 | @override
55 | Widget build(BuildContext context) {
56 | return FittedBox(
57 | fit: BoxFit.scaleDown,
58 | alignment: Alignment.centerLeft,
59 | child: Text(text),
60 | );
61 | }
62 | }
63 |
64 | class _GridPhotoViewerState extends State with SingleTickerProviderStateMixin {
65 | AnimationController _controller;
66 | Animation _flingAnimation;
67 | Offset _offset = Offset.zero;
68 | double _scale = 1.0;
69 | Offset _normalizedOffset;
70 | double _previousScale;
71 |
72 | @override
73 | void initState() {
74 | super.initState();
75 | _controller = AnimationController(vsync: this)
76 | ..addListener(_handleFlingAnimation);
77 | }
78 |
79 | @override
80 | void dispose() {
81 | _controller.dispose();
82 | super.dispose();
83 | }
84 |
85 | // The maximum offset value is 0,0. If the size of this renderer's box is w,h
86 | // then the minimum offset value is w - _scale * w, h - _scale * h.
87 | Offset _clampOffset(Offset offset) {
88 | final Size size = context.size;
89 | final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale);
90 | return Offset(offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
91 | }
92 |
93 | void _handleFlingAnimation() {
94 | setState(() {
95 | _offset = _flingAnimation.value;
96 | });
97 | }
98 |
99 | void _handleOnScaleStart(ScaleStartDetails details) {
100 | setState(() {
101 | _previousScale = _scale;
102 | _normalizedOffset = (details.focalPoint - _offset) / _scale;
103 | // The fling animation stops if an input gesture starts.
104 | _controller.stop();
105 | });
106 | }
107 |
108 | void _handleOnScaleUpdate(ScaleUpdateDetails details) {
109 | setState(() {
110 | _scale = (_previousScale * details.scale).clamp(1.0, 4.0);
111 | // Ensure that image location under the focal point stays in the same place despite scaling.
112 | _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
113 | });
114 | }
115 |
116 | void _handleOnScaleEnd(ScaleEndDetails details) {
117 | final double magnitude = details.velocity.pixelsPerSecond.distance;
118 | if (magnitude < _kMinFlingVelocity)
119 | return;
120 | final Offset direction = details.velocity.pixelsPerSecond / magnitude;
121 | final double distance = (Offset.zero & context.size).shortestSide;
122 | _flingAnimation = _controller.drive(Tween(
123 | begin: _offset,
124 | end: _clampOffset(_offset + direction * distance),
125 | ));
126 | _controller
127 | ..value = 0.0
128 | ..fling(velocity: magnitude / 1000.0);
129 | }
130 |
131 | @override
132 | Widget build(BuildContext context) {
133 | return GestureDetector(
134 | onScaleStart: _handleOnScaleStart,
135 | onScaleUpdate: _handleOnScaleUpdate,
136 | onScaleEnd: _handleOnScaleEnd,
137 | child: ClipRect(
138 | child: Transform(
139 | transform: Matrix4.identity()
140 | ..translate(_offset.dx, _offset.dy)
141 | ..scale(_scale),
142 | child:
143 | // Image.network(widget.photo.url,),
144 | BkFlutterImage(
145 | url: widget.photo.url,
146 | width: 150,
147 | height: 150,
148 | //package: widget.photo.assetPackage,
149 | fit: BoxFit.contain,
150 | autoResize: false,
151 | ),
152 | ),
153 | ),
154 | );
155 | }
156 | }
157 |
158 | class GridDemoPhotoItem extends StatelessWidget {
159 | GridDemoPhotoItem({
160 | Key key,
161 | @required this.photo,
162 | @required this.tileStyle,
163 | @required this.onBannerTap,
164 | }) : assert(photo != null && photo.isValid),
165 | assert(tileStyle != null),
166 | assert(onBannerTap != null),
167 | super(key: key);
168 |
169 | final Photo photo;
170 | final GridDemoTileStyle tileStyle;
171 | final BannerTapCallback onBannerTap; // User taps on the photo's header or footer.
172 |
173 | void showPhoto(BuildContext context) {
174 | Navigator.push(context, MaterialPageRoute(
175 | builder: (BuildContext context) {
176 | return Scaffold(
177 | appBar: AppBar(
178 | title: Text(photo.title),
179 | ),
180 | body: SizedBox.expand(
181 | child: Hero(
182 | tag: photo.tag,
183 | child: GridPhotoViewer(photo: photo),
184 | ),
185 | ),
186 | );
187 | }
188 | ));
189 | }
190 |
191 | @override
192 | Widget build(BuildContext context) {
193 | final Widget image = GestureDetector(
194 | onTap: () { showPhoto(context); },
195 | child: BkFlutterImage(
196 | url: photo.url,
197 | fit: photo.mode,
198 | placeholder: 'images/01.jpg',
199 | autoResize: true,
200 | imageErrorBuilder: (BuildContext context,Object error,StackTrace stacktrace){
201 | return Text(error);
202 | },
203 | ),
204 | );
205 |
206 | final IconData icon = photo.isFavorite ? Icons.star : Icons.star_border;
207 |
208 | switch (tileStyle) {
209 | case GridDemoTileStyle.deleteMode:
210 | return GridTile(
211 | header: GestureDetector(
212 | onTap: () { onBannerTap(photo); },
213 | child: GridTileBar(
214 | title: _GridTitleText(photo.title),
215 | backgroundColor: Colors.black45,
216 | leading: Icon(
217 | Icons.delete_forever,
218 | color: Colors.white,
219 | ),
220 | ),
221 | ),
222 | child: image,
223 | );
224 |
225 | case GridDemoTileStyle.selectMode:
226 | return GridTile(
227 | footer: GestureDetector(
228 | onTap: () { onBannerTap(photo); },
229 | child: GridTileBar(
230 | backgroundColor: Colors.black45,
231 | title: _GridTitleText(photo.title),
232 | subtitle: _GridTitleText(photo.caption),
233 | trailing: Icon(
234 | icon,
235 | color: Colors.white,
236 | ),
237 | ),
238 | ),
239 | child: image,
240 | );
241 | }
242 | assert(tileStyle != null);
243 | return null;
244 | }
245 |
246 | }
247 |
248 | class GridListDemo extends StatefulWidget {
249 | const GridListDemo({ Key key }) : super(key: key);
250 |
251 | @override
252 | GridListDemoState createState() => GridListDemoState();
253 |
254 | }
255 |
256 | class GridListDemoState extends State {
257 | GridDemoTileStyle _tileStyle = GridDemoTileStyle.selectMode;
258 |
259 | @override
260 | void dispose() {
261 | super.dispose();
262 | }
263 |
264 | @override
265 | @override
266 | void initState() {
267 | super.initState();
268 | for(int i = 0; i < 10; i ++) {
269 | photos.addAll(allModephotos(i));
270 | }
271 | }
272 |
273 | List allModephotos(int i){
274 | List temp = [
275 | Photo(
276 | url: photosUrls[i],
277 | assetPackage: _kGalleryAssetsPackage,
278 | title: 'BoxFit.fill',
279 | caption: 'Fisherman',mode: BoxFit.fill,
280 | ),
281 | Photo(
282 | url: photosUrls[i],
283 | assetPackage: _kGalleryAssetsPackage,
284 | title: 'BoxFit.cover',
285 | caption: 'Fisherman',mode: BoxFit.cover,
286 | ),
287 | Photo(
288 | url: photosUrls[i],
289 | assetPackage: _kGalleryAssetsPackage,
290 | title: 'BoxFit.contain',
291 | caption: 'Fisherman',mode: BoxFit.contain,
292 | ),
293 | Photo(
294 | url: photosUrls[i],
295 | assetPackage: _kGalleryAssetsPackage,
296 | title: 'BoxFit.fitWidth',
297 | caption: 'Fisherman',mode: BoxFit.fitWidth,
298 | ),
299 | Photo(
300 | url: photosUrls[i],
301 | assetPackage: _kGalleryAssetsPackage,
302 | title: 'BoxFit.fitHeight',
303 | caption: 'Fisherman',
304 | mode: BoxFit.fitHeight,
305 | ),
306 | Photo(
307 | url: photosUrls[i],
308 | assetPackage: _kGalleryAssetsPackage,
309 | title: 'BoxFit.scaleDown',
310 | caption: 'Fisherman',
311 | mode: BoxFit.scaleDown,
312 | ),
313 | ];
314 | return temp;
315 | }
316 |
317 | List photos = [
318 | ];
319 |
320 | void changeTileStyle(GridDemoTileStyle value) {
321 | setState(() {
322 | _tileStyle = value;
323 | });
324 | }
325 |
326 | @override
327 | Widget build(BuildContext context) {
328 | final Orientation orientation = MediaQuery.of(context).orientation;
329 | return Scaffold(
330 | appBar: AppBar(
331 | title: const Text('Grid list'),
332 | actions: [
333 | //MaterialDemoDocumentationButton(GridListDemo.routeName),
334 | PopupMenuButton(
335 | onSelected: changeTileStyle,
336 | itemBuilder: (BuildContext context) => >[
337 | const PopupMenuItem(
338 | value: GridDemoTileStyle.deleteMode,
339 | child: Text('Delete Mode'),
340 | ),
341 | const PopupMenuItem(
342 | value: GridDemoTileStyle.selectMode,
343 | child: Text('Select Mode'),
344 | ),
345 | ],
346 | ),
347 | ],
348 | ),
349 | body:
350 | Column( children: [
351 | Expanded( child: SafeArea(
352 | top: false,
353 | bottom: false,
354 | child: GridView.count(
355 | crossAxisCount: (orientation == Orientation.portrait) ? 2 : 3,
356 | mainAxisSpacing: 4.0,
357 | crossAxisSpacing: 4.0,
358 | padding: const EdgeInsets.all(4.0),
359 | childAspectRatio: (orientation == Orientation.portrait) ? 1.0 : 1.3,
360 | children: photos.map((Photo photo) {
361 | return GridDemoPhotoItem(
362 | photo: photo,
363 | tileStyle: _tileStyle,
364 | onBannerTap: (Photo photo) {
365 | setState(() {
366 | if (GridDemoTileStyle.deleteMode == _tileStyle) {
367 | photos.remove(photo);
368 | } else {
369 | photo.isFavorite = !photo.isFavorite;
370 | }
371 | });
372 | },
373 | );
374 | }).toList(),
375 | ),
376 | ),
377 | ),
378 | ],
379 | ),
380 | );
381 | }
382 | }
383 |
384 | List photosUrls = [
385 | 'https://images.pexels.com/photos/6383210/pexels-photo-6383210.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
386 | 'https://images.pexels.com/photos/1751279/pexels-photo-1751279.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
387 | 'https://images.pexels.com/photos/2434269/pexels-photo-2434269.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
388 | 'https://images.pexels.com/photos/7307618/pexels-photo-7307618.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
389 | 'https://images.pexels.com/photos/3932944/pexels-photo-3932944.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
390 | 'https://images.pexels.com/photos/4578770/pexels-photo-4578770.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
391 | 'https://images.pexels.com/photos/9060607/pexels-photo-9060607.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
392 | 'https://images.pexels.com/photos/1915715/pexels-photo-1915715.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
393 | 'https://images.pexels.com/photos/4683675/pexels-photo-4683675.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
394 | 'https://images.pexels.com/photos/9166412/pexels-photo-9166412.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
395 | 'https://images.pexels.com/photos/2681319/pexels-photo-2681319.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
396 | 'https://images.pexels.com/photos/6243804/pexels-photo-6243804.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
397 | 'https://image2.ljcdn.com/utopia-file/p1/215a16c18b2c6ad44a61ff077f12b64fd937398c-3840-2560!m_fit,w_2560,o_auto,f_jpg',
398 | 'https://image2.ljcdn.com/utopia-file/p1/215a16c18b2c6ad44a61ff077f12b64fd937398c-3840-2560!m_fit,w_2560,o_auto,f_jpg',
399 | 'https://image2.ljcdn.com/utopia-file/p1/d7884f6c4cf7e43e826018a8576e6bf53d07fe40-5424-3632!m_fit,w_2560,o_auto,f_jpg',
400 | 'https://image2.ljcdn.com/utopia-file/p1/e279f611cea722a7225ef581bace829d68dd3463-5328-4000!m_fit,w_2560,o_auto,f_jpg',
401 | 'https://image2.ljcdn.com/utopia-file/p1/8834c7631bfddee25391079b6951e1d6c10c40b8-2560-1920!m_fit,w_2560,o_auto,f_jpg',
402 | 'https://image2.ljcdn.com/utopia-file/p1/90aacb7b156c0670dd8fe203a6cf372d5b4bc1ca-5376-3314!m_fit,w_2560,o_auto,f_jpg',
403 | 'https://image2.ljcdn.com/utopia-file/p1/d57bad88338fcdba2405a02dda5415b73e7cbb4c-6480-4320!m_fit,w_2560,o_auto,f_jpg',
404 | 'https://image2.ljcdn.com/utopia-file/p1/b7e71ed8ec092e6b25ca4315d43b11aee85a76b7-2560-1707!m_fit,w_2560,o_auto,f_jpg',
405 | 'https://image2.ljcdn.com/utopia-file/p1/a2909d3b7d35165daf0cea9697df470e3e61d747-5973-4480!m_fit,w_2560,o_auto,f_jpg',
406 | 'https://image2.ljcdn.com/utopia-file/p1/d5202b01af7a978a9f4332208780265bf1ac3617-5760-3840!m_fit,w_2560,o_auto,f_jpg',
407 | 'https://image2.ljcdn.com/utopia-file/p1/51392b46baa507e7b97ad9fadbdb0bf6bcf0bc06-2667-2000!m_fit,w_2560,o_auto,f_jpg',
408 | 'https://image2.ljcdn.com/utopia-file/p1/861520ecf875fc750de9565fb01548e7e94fa95a-5994-3996!m_fit,w_2560,o_auto,f_jpg',
409 | 'https://image2.ljcdn.com/utopia-file/p1/3e1ebe4a9bb66b1322bd90e5b185b8e0d9661dc1-5472-3648!m_fit,w_2560,o_auto,f_jpg',
410 | 'https://image2.ljcdn.com/utopia-file/p1/5e1d544c33118451ebaca9aae349a9c704e5cb2c-8448-6336!m_fit,w_2560,o_auto,f_jpg',
411 | 'https://image2.ljcdn.com/utopia-file/p1/e7eeeb5b4a079a0e4f51c7c955da85464f2e93a5-2400-1800!m_fit,w_2560,o_auto,f_jpg',
412 | "https://image2.ljcdn.com/utopia-file/p1/215a16c18b2c6ad44a61ff077f12b64fd937398c-3840-2560!m_fit,w_2560,o_auto,f_jpg",
413 | "https://image2.ljcdn.com/utopia-file/p1/d7884f6c4cf7e43e826018a8576e6bf53d07fe40-5424-3632!m_fit,w_2560,o_auto,f_jpg",
414 | "https://image2.ljcdn.com/utopia-file/p1/e279f611cea722a7225ef581bace829d68dd3463-5328-4000!m_fit,w_2560,o_auto,f_jpg",
415 | "https://image2.ljcdn.com/utopia-file/p1/90aacb7b156c0670dd8fe203a6cf372d5b4bc1ca-5376-3314!m_fit,w_2560,o_auto,f_jpg",
416 | ];
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:bk_flutter_image/bk_flutter_image.dart';
3 |
4 | import 'exts.dart';
5 |
6 | void main() => runApp(MyApp());
7 |
8 | class MyApp extends StatefulWidget {
9 | @override
10 | _MyAppState createState() => _MyAppState();
11 | }
12 |
13 | class _MyAppState extends State {
14 | @override
15 | void initState() {
16 | super.initState();
17 | }
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return MaterialApp(
22 | home: MainPage(),
23 | );
24 | }
25 |
26 | }
27 |
28 | class MainPage extends StatefulWidget {
29 | @override
30 | _MainPageState createState() => _MainPageState();
31 | }
32 |
33 | class _MainPageState extends State {
34 |
35 | double width = 300;
36 | double height = 300;
37 | BoxFit fit = BoxFit.cover;
38 | bool useFlutterImage = true;
39 | String imageUrl ='https://image2.ljcdn.com/utopia-file/p1/215a16c18b2c6ad44a61ff077f12b64fd937398c-3840-2560!m_fit,w_2560,o_auto,f_jpg';
40 |
41 | @override
42 | Widget build(BuildContext context) {
43 | return Scaffold(
44 | appBar: AppBar(title:Text('flutter 使用原生image view'),actions: [
45 | IconButton(icon: Icon(Icons.list), onPressed: (){
46 | Navigator.push(context, DialogRoute(context: context, builder: (c) {
47 | return GridListDemo();
48 | }));
49 | // Navigator.push(context, MaterialPageRoute(builder: (c) {
50 | // return GridListDemo();
51 | // }));
52 | })
53 | ],),
54 | body: Stack(
55 | children: [
56 | SafeArea(
57 | child: Column(
58 | children: [
59 | Expanded(
60 | flex: 1,
61 | child: Container(),
62 | ),
63 | Text('flutter这边的图片container参数设置'),
64 | Row(
65 | children: [
66 | Text('flutter上的宽度'),
67 | Slider(
68 | value: width,
69 | min: 100,
70 | max: 500,
71 | onChanged: (value) {
72 | setState(() {
73 | width = value.roundToDouble();
74 | });
75 | }),
76 | ],
77 | ),
78 | Row(
79 | children: [
80 | Text('flutter上的高度'),
81 | Slider(
82 | value: height,
83 | min: 100,
84 | max: 500,
85 | onChanged: (value) {
86 | setState(() {
87 | height = value.roundToDouble();
88 | });
89 | }),
90 | ],
91 | ),
92 | Row(
93 | children: [
94 | Text('使用Texture加载图片: $useFlutterImage'),
95 | Checkbox(
96 | value: useFlutterImage,
97 | onChanged: (value) {
98 | setState(() {
99 | useFlutterImage = value;
100 | });
101 | },
102 | ),
103 | Text('Cover:'),
104 | Checkbox(
105 | value: fit == BoxFit.cover,
106 | onChanged: (value) {
107 | setState(() {
108 | fit = value ? BoxFit.cover : BoxFit.none;
109 | });
110 | },
111 | ),
112 | Text('Contain:'),
113 | Checkbox(
114 | value: fit == BoxFit.contain,
115 | onChanged: (value) {
116 | setState(() {
117 | fit = value ? BoxFit.contain : BoxFit.none;
118 | });
119 | },
120 | ),
121 | Text('fill:'),
122 | Checkbox(
123 | value: fit == BoxFit.fill,
124 | onChanged: (value) {
125 | setState(() {
126 | fit = value ? BoxFit.fill : BoxFit.none;
127 | });
128 | },
129 | ),
130 | Text('fitWidth:'),
131 | Checkbox(
132 | value: fit == BoxFit.fitWidth,
133 | onChanged: (value) {
134 | setState(() {
135 | fit = value ? BoxFit.fitWidth : BoxFit.none;
136 | });
137 | },
138 | ),
139 | Text('fitHeight:'),
140 | Checkbox(
141 | value: fit == BoxFit.fitHeight,
142 | onChanged: (value) {
143 | setState(() {
144 | fit = value ? BoxFit.fitHeight : BoxFit.none;
145 | });
146 | },
147 | ),
148 | Text('scale:'),
149 | Checkbox(
150 | value: fit == BoxFit.scaleDown,
151 | onChanged: (value) {
152 | setState(() {
153 | fit = value ? BoxFit.scaleDown : BoxFit.none;
154 | });
155 | },
156 | ),
157 | ],
158 | ),
159 | ],
160 | ),
161 | ),
162 | Center(
163 | child: SafeArea(
164 | child:Column(
165 | children:[
166 | Container(// width: 400,
167 | // height: 100,
168 | child: useFlutterImage
169 | ? BkFlutterImage(
170 | url: imageUrl,
171 | height: height,
172 | width: width,
173 | fit: fit,
174 | )
175 | : Image.network(
176 | imageUrl,
177 | fit: fit,
178 | width: width,
179 | height: height,
180 | ),
181 |
182 | ), ]),
183 |
184 | ),
185 | )
186 | ],
187 | ),
188 | );
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: bk_flutter_image_example
2 | description: Demonstrates how to use the bk_flutter_image plugin.
3 | publish_to: 'none'
4 |
5 | environment:
6 | sdk: ">=2.1.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: ^0.1.2
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 |
20 | bk_flutter_image:
21 | path: ../
22 |
23 | # For information on the generic Dart part of this file, see the
24 | # following page: https://dart.dev/tools/pub/pubspec
25 |
26 | # The following section is specific to Flutter.
27 | flutter:
28 |
29 | # The following line ensures that the Material Icons font is
30 | # included with your application, so that you can use the icons in
31 | # the material Icons class.
32 | uses-material-design: true
33 |
34 | # To add assets to your application, add an assets section, like this:
35 | assets:
36 | - images/01.jpg
37 | # An image asset can refer to one or more resolution-specific "variants", see
38 | # https://flutter.dev/assets-and-images/#resolution-aware.
39 |
40 | # For details regarding adding assets from package dependencies, see
41 | # https://flutter.dev/assets-and-images/#from-packages
42 |
43 | # To add custom fonts to your application, add a fonts section here,
44 | # in this "flutter" section. Each entry in this list should have a
45 | # "family" key with the font family name, and a "fonts" key with a
46 | # list giving the asset and other descriptors for the font. For
47 | # example:
48 | # fonts:
49 | # - family: Schyler
50 | # fonts:
51 | # - asset: fonts/Schyler-Regular.ttf
52 | # - asset: fonts/Schyler-Italic.ttf
53 | # style: italic
54 | # - family: Trajan Pro
55 | # fonts:
56 | # - asset: fonts/TrajanPro.ttf
57 | # - asset: fonts/TrajanPro_Bold.ttf
58 | # weight: 700
59 | #
60 | # For details regarding fonts from package dependencies,
61 | # see https://flutter.dev/custom-fonts/#from-packages
62 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:bk_flutter_image_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) => widget is Text &&
22 | widget.data.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LianjiaTech/bk_flutter_image/752408f944aad295668fec068658a7be8ecf8dd0/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Assets/glsl.fsh:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform sampler2D Texture;
4 | varying vec2 TextureCoordsVarying;
5 |
6 | void main (void) {
7 | vec4 mask = texture2D(Texture, TextureCoordsVarying);
8 | gl_FragColor = vec4(mask.rgb, 1.0);
9 | }
10 |
--------------------------------------------------------------------------------
/ios/Assets/glsl.vsh:
--------------------------------------------------------------------------------
1 | attribute vec4 Position;
2 | attribute vec2 TextureCoords;
3 | varying vec2 TextureCoordsVarying;
4 |
5 | void main (void) {
6 | gl_Position = Position;
7 | TextureCoordsVarying = TextureCoords;
8 | }
9 |
--------------------------------------------------------------------------------
/ios/Classes/BkFlutterImagePlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface BkFlutterImagePlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/BkFlutterImagePlugin.m:
--------------------------------------------------------------------------------
1 | #import "BkFlutterImagePlugin.h"
2 | #import "BkImageRenderWorker.h"
3 | #import "BkFlutterPixelBufferCache.h"
4 |
5 |
6 | @interface BkFlutterImagePlugin ()
7 |
8 | @property (nonatomic, strong) NSMutableDictionary *workers;
9 | @property (nonatomic, weak) NSObject *textures;
10 |
11 | @end
12 |
13 | @implementation BkFlutterImagePlugin
14 |
15 | + (void)registerWithRegistrar:(NSObject*)registrar{
16 | FlutterMethodChannel *channel = [FlutterMethodChannel
17 | methodChannelWithName:@"bk_flutter_image"
18 | binaryMessenger:[registrar messenger]];
19 | BkFlutterImagePlugin *instance = [[BkFlutterImagePlugin alloc] initWithTextures:[registrar textures]];
20 | [registrar addMethodCallDelegate:instance channel:channel];
21 | }
22 |
23 | - (instancetype)initWithTextures:(NSObject *)textures {
24 | self = [super init];
25 | if (self) {
26 | _workers = [[NSMutableDictionary alloc] init];
27 | _textures = textures;
28 | }
29 | return self;
30 | }
31 |
32 | - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
33 | if ([@"create" isEqualToString:call.method]) {
34 | NSInteger __block textureId = -1;
35 | id __weak registry = self.textures;
36 | BkImageRenderWorker *worker = [[BkImageRenderWorker alloc] initWithParams:[self convertToNoNullDic:call.arguments] onNewFrame:^(NSError * error, CGSize size, BOOL isFullPixel) {
37 | NSMutableDictionary *textureInfo = [NSMutableDictionary dictionary];
38 | [textureInfo setValue:error.userInfo[NSLocalizedDescriptionKey] ?: @"" forKey:@"error"];
39 | [textureInfo setValue:@(error ? -1 : textureId) forKey:@"textureId"];
40 | [textureInfo setValue:@(size.width) forKey:@"textureWidth"];
41 | [textureInfo setValue:@(size.height) forKey:@"textureHeight"];
42 | [textureInfo setValue:@(isFullPixel) forKey:@"isFullPixel"];
43 | result([[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:textureInfo options:NSJSONWritingPrettyPrinted error:nil] encoding:NSUTF8StringEncoding]);
44 | [registry textureFrameAvailable:textureId];
45 | }];
46 | textureId = (NSInteger) [self.textures registerTexture:worker];
47 | [worker startWork];
48 | } else if ([@"dispose" isEqualToString:call.method]) {
49 | NSNumber *textureId = call.arguments[@"textureId"];
50 | if (![textureId isKindOfClass:[NSNull class]] && textureId.integerValue != -1) {
51 | [self.textures unregisterTexture:[textureId integerValue]];
52 | }
53 | result(nil);
54 | } else if ([@"setCacheSize" isEqualToString:call.method]) {
55 | NSDictionary *cacheData = [self convertToNoNullDic:call.arguments];
56 | [[BkFlutterPixelBufferCache sharedInstance] setDiskCacheMaxSize:[cacheData[@"diskMaxSize"] unsignedIntValue]];
57 | [[BkFlutterPixelBufferCache sharedInstance] setMemoryCacheMaxSize:[cacheData[@"memoryMaxSize"] unsignedIntValue]];
58 | result(nil);
59 | } else {
60 | result(FlutterMethodNotImplemented);
61 | }
62 | }
63 |
64 | - (NSString *)stringFromDictionary:(NSDictionary *)dict {
65 |
66 | NSString *jsonString = nil;
67 | if ([NSJSONSerialization isValidJSONObject:dict]) {
68 | NSError *error;
69 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
70 | jsonString =[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
71 | if (error) {
72 | //return [self errorString:error];
73 | }
74 | }
75 | return jsonString;
76 | }
77 |
78 | - (NSDictionary *)convertToNoNullDic:(NSDictionary *)dic {
79 | NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithCapacity:dic.count];
80 | for (id key in dic.allKeys) {
81 | if (![dic[key] isEqual:[NSNull null]]){
82 | mDic[key] = dic[key];
83 | }
84 | }
85 | return mDic;
86 | }
87 |
88 | @end
89 |
--------------------------------------------------------------------------------
/ios/Classes/BkFlutterPixelBufferCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // BkFlutterPixelBufferCache.h
3 | // bk_flutter_image
4 | //
5 | // Created by zhao hongwei on 2022/5/13.
6 | //
7 |
8 | #import
9 | #import "SDMemoryCache.h"
10 | #import "SDImageCacheConfig.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface BkFlutterPixelBufferRef : NSObject
15 |
16 | @property (nonatomic, unsafe_unretained) CVPixelBufferRef ref;
17 |
18 | + (BkFlutterPixelBufferRef *)pixelBufferRefFromData:(NSData *)data size:(CGSize)size;
19 | + (BkFlutterPixelBufferRef *)pixelBufferRefWithImage:(UIImage *)sImage;
20 |
21 | + (NSData *)dataFromPixelBufferRef:(BkFlutterPixelBufferRef *)pixelBufferRef;
22 |
23 | - (id)initWithRef:(CVPixelBufferRef)ref;
24 |
25 | @end
26 |
27 | @interface BkFlutterPixelBufferCache : NSObject
28 |
29 | + (instancetype)sharedInstance;
30 |
31 | - (void)loadImageAndPixelBuffer:(NSString *)url size:(CGSize)size completion:(void (^)(BkFlutterPixelBufferRef *buffer, NSError* error, BOOL isFullPixel))completion;
32 |
33 | - (void)setMemoryCacheMaxSize:(NSUInteger)maxSize;
34 |
35 | - (void)setDiskCacheMaxSize:(NSUInteger)maxSize;
36 |
37 | @end
38 |
39 | NS_ASSUME_NONNULL_END
40 |
--------------------------------------------------------------------------------
/ios/Classes/BkFlutterPixelBufferCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // BkFlutterPixelBufferCache.m
3 | // bk_flutter_image
4 | //
5 | // Created by zhao hongwei on 2022/5/13.
6 | //
7 |
8 | #import "BkFlutterPixelBufferCache.h"
9 |
10 | #import
11 | #import
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | API_AVAILABLE(ios(10.0))
18 | @interface BkFlutterPixelBufferCache ()
19 |
20 | @property (nonatomic, strong) SDMemoryCache *cache;
21 | @property (nonatomic, strong) SDDiskCache *diskCache;
22 | @property (nonatomic, strong) dispatch_queue_t queue;
23 |
24 | @end
25 |
26 | @implementation BkFlutterPixelBufferCache
27 |
28 | #pragma init
29 | + (BkFlutterPixelBufferCache *)sharedInstance {
30 | static BkFlutterPixelBufferCache *_sharedInstance = nil;
31 | static dispatch_once_t oncePredicate;
32 | dispatch_once(&oncePredicate, ^{
33 | _sharedInstance = [[self alloc] init];
34 | });
35 | return _sharedInstance;
36 | }
37 |
38 |
39 | + (void)swizzlingOriginClass:(Class)originClass originMethodName:(NSString *)originMethodName
40 | currentClass:(Class)currentClass currentMethodName:(NSString *)currentMethodName
41 | {
42 | SEL originSelector = NSSelectorFromString(originMethodName);
43 | SEL currentSelector = NSSelectorFromString(currentMethodName);
44 | Method originMethod = class_getInstanceMethod(originClass, originSelector);
45 | Method currentMethod = class_getInstanceMethod(currentClass, currentSelector);
46 | BOOL isSwizzed = class_addMethod(originClass, originSelector, method_getImplementation(currentMethod), method_getTypeEncoding(currentMethod));
47 | if (isSwizzed) {
48 | class_replaceMethod(originClass, currentSelector,method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
49 | } else {
50 | method_exchangeImplementations(originMethod, currentMethod);
51 | }
52 | }
53 |
54 | - (id)init {
55 | if (self = [super init]) {
56 | SDImageCacheConfig *config = [SDImageCacheConfig new];
57 | self.diskCache = [[SDDiskCache alloc] initWithCachePath: [[SDImageCache defaultDiskCacheDirectory] stringByAppendingPathComponent:@"bkFlutterImage"] config:config];
58 | self.cache = [[SDMemoryCache alloc] initWithConfig:config];
59 | self.queue = dispatch_queue_create("com.BK.flutterImage", DISPATCH_QUEUE_SERIAL);
60 | }
61 | return self;
62 | }
63 |
64 | - (void)setMemoryCacheMaxSize:(NSUInteger)maxSize {
65 | self.cache.config.maxMemoryCost = maxSize * 1024 * 1024;
66 | }
67 |
68 | - (void)setDiskCacheMaxSize:(NSUInteger)maxSize {
69 | self.cache.config.maxDiskSize = maxSize * 1024 * 1024;
70 | }
71 |
72 | #pragma load
73 | - (void)loadImageAndPixelBuffer:(NSString *)url size:(CGSize)size completion:(void (^)(BkFlutterPixelBufferRef *buffer, NSError* error, BOOL isFullPixel))completion {
74 |
75 | __weak typeof(self) weakSelf = self;
76 | dispatch_async(self.queue, ^{
77 | BOOL isFullPixel;
78 | __strong typeof(weakSelf) strongSelf = weakSelf;
79 | BkFlutterPixelBufferRef *bufferRef = [strongSelf pixelBufferRefWithUrl:url size:size isFullPixelImage:&isFullPixel];
80 | if (!bufferRef) {
81 | NSDictionary *context = CGSizeEqualToSize(size, CGSizeMake(-1, -1)) ? nil : @{SDWebImageContextImageThumbnailPixelSize:@(size)};
82 |
83 | //先检查缓存中的Image
84 | UIImage *memoryImage = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:url];
85 | NSData *data;
86 | BOOL isFullPixel;
87 | if (memoryImage) {
88 | data = [[SDImageCache sharedImageCache] diskImageDataForKey:url];
89 | isFullPixel = [strongSelf isFullPixelImage:memoryImage data:data];
90 | if (isFullPixel || [strongSelf sizeMatch:memoryImage.size toSize:size]) {
91 | [strongSelf handleResult:nil image:memoryImage url:url isFullPixel:isFullPixel error:nil completion:completion];
92 | return;
93 | }
94 | }
95 |
96 | //检查diskImage
97 | UIImage *diskImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:url options:0 context:context];
98 | if (diskImage) {
99 | if (context){
100 | data = data ?: [[SDImageCache sharedImageCache] diskImageDataForKey:url];
101 | isFullPixel = [strongSelf isFullPixelImage:diskImage data:data];
102 | } else {
103 | isFullPixel = YES;
104 | }
105 | [strongSelf handleResult:nil image:diskImage url:url isFullPixel:isFullPixel error:nil completion:completion];
106 | return;
107 | }
108 |
109 | __weak typeof(self) weakSelf = strongSelf;
110 | [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[[NSURL alloc] initWithString:url] options:SDWebImageDownloaderScaleDownLargeImages | SDWebImageDownloaderUseNSURLCache | SDWebImageDownloaderAvoidDecodeImage context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
111 | dispatch_async(strongSelf.queue, ^{
112 | __strong typeof(weakSelf) strongSelf = weakSelf;
113 | if (!error && image) {
114 | [strongSelf handleResult:nil image:image url:url isFullPixel:[strongSelf isFullPixelImage:image data:data] error:error completion:completion];
115 | [[SDImageCache sharedImageCache] storeImageDataToDisk:data forKey:url];
116 | } else {
117 | [strongSelf handleResult:nil image:nil url:url isFullPixel:NO error:error completion:completion];
118 | }
119 | });
120 | }];
121 | } else if (completion) {
122 | [strongSelf handleResult:bufferRef image:nil url:url isFullPixel:isFullPixel error:nil completion:completion];
123 | }
124 | });
125 | }
126 |
127 |
128 | - (void)handleResult:(BkFlutterPixelBufferRef *)bufferRef image:(UIImage *)image url:(NSString *)url isFullPixel:(BOOL)isFullPixel error:(NSError *)error completion:(void (^)( BkFlutterPixelBufferRef *buffer, NSError* error, BOOL isFullPixel))completion {
129 | bufferRef = bufferRef ?: [BkFlutterPixelBufferRef pixelBufferRefWithImage:image];
130 | [self storePixelBufferRef:bufferRef url:url isFullPixelImage:isFullPixel];
131 | if (completion)
132 | completion(bufferRef, error, isFullPixel);
133 | }
134 |
135 | #pragma memory op
136 |
137 | - (BkFlutterPixelBufferRef *)pixelBufferRefWithUrl:(NSString *)url size:(CGSize)size isFullPixelImage:(BOOL *)isFullPixelImage{
138 | //get memory
139 | NSString *fullSizeCacheKey = [url stringByAppendingString:@"&BkFlutterFullSize"];
140 | BkFlutterPixelBufferRef *fullRef = [self.cache objectForKey:fullSizeCacheKey];
141 | BkFlutterPixelBufferRef *thumbRef = [self.cache objectForKey:url];
142 | if (fullRef || thumbRef) { //check memory
143 | CGSize pixelSize = fullRef ? CGSizeMake(INT_MAX, INT_MAX) : CGSizeMake(CVPixelBufferGetWidth(thumbRef.ref), CVPixelBufferGetHeight(thumbRef.ref));
144 |
145 | if ([self sizeMatch:pixelSize toSize:size]) {
146 | CGSize logSize = fullRef ? CGSizeMake(CVPixelBufferGetWidth(fullRef.ref), CVPixelBufferGetHeight(fullRef.ref)) : CGSizeMake(CVPixelBufferGetWidth(thumbRef.ref), CVPixelBufferGetHeight(thumbRef.ref));
147 | *isFullPixelImage = fullRef != nil;
148 | return fullRef ?:thumbRef;
149 | }
150 | } else { //check disk
151 | BkFlutterPixelBufferRef *diskRef = [self diskPixelBufferRefWithUrl:url size:size isFullPixelImage:isFullPixelImage];
152 | //store in menory cache
153 | [self storePixelBufferRef:diskRef url:url isFullPixelImage:*isFullPixelImage];
154 | return diskRef;
155 | }
156 | return nil;
157 | }
158 |
159 | - (void)storePixelBufferRef:(BkFlutterPixelBufferRef *)buffer url:(NSString *)url isFullPixelImage:(BOOL)isFullPixelImage {
160 | if (buffer == nil)
161 | return;
162 | CGSize targetSize = CGSizeMake(CVPixelBufferGetWidth(buffer.ref), CVPixelBufferGetHeight(buffer.ref));
163 |
164 | NSString *fullSizeCacheKey = [url stringByAppendingString:@"&BkFlutterFullSize"];
165 | NSString *targetCacheKey = isFullPixelImage ? fullSizeCacheKey : url;
166 |
167 | BkFlutterPixelBufferRef *fullRef = [self.cache objectForKey:fullSizeCacheKey];
168 | BkFlutterPixelBufferRef *thumbRef = [self.cache objectForKey:targetCacheKey];
169 |
170 | if (fullRef == buffer || thumbRef == buffer)
171 | return;
172 |
173 | if (!fullRef) {
174 | if (isFullPixelImage || !thumbRef) {
175 | [self.cache setObject:buffer forKey:targetCacheKey cost:targetSize.width * targetSize.height * 4];
176 | [self storeDiskPixelBufferRef:buffer url:url isFullPixelImage:isFullPixelImage];
177 | } else {
178 | CGSize thumbSize = CGSizeMake(CVPixelBufferGetWidth(thumbRef.ref), CVPixelBufferGetHeight(thumbRef.ref));
179 | if ([self sizeMatch:targetSize toSize:thumbSize]) {
180 | [self.cache setObject:buffer forKey:targetCacheKey cost:targetSize.width * targetSize.height * 4];
181 | [self storeDiskPixelBufferRef:buffer url:url isFullPixelImage:isFullPixelImage];
182 | }
183 | }
184 | }
185 | }
186 |
187 |
188 | #pragma disk op
189 | - (BkFlutterPixelBufferRef *)diskPixelBufferRefWithUrl:(NSString *)url size:(CGSize)size isFullPixelImage:(BOOL *)isFullPixelImage {
190 |
191 | BkFlutterPixelBufferRef *ref = nil;
192 | BOOL tempIsFullPixelImage = NO;
193 | if ([self.diskCache containsDataForKey:url]) {
194 | NSData *data = [self.diskCache extendedDataForKey:url];
195 | NSError *error = nil;
196 | NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
197 | NSDictionary *sizeDic = [unarchiver decodeObjectForKey:@"sizeInfo"];
198 | if (!error && sizeDic && sizeDic[@"PixelBufferSize"]) {
199 | CGSize pixelSize = [sizeDic[@"IsFullPixelImage"] boolValue] ? CGSizeMake(INT_MAX, INT_MAX) : [sizeDic[@"PixelBufferSize"] CGSizeValue];
200 | if ([self sizeMatch:pixelSize toSize:size]) {
201 | ref = [BkFlutterPixelBufferRef pixelBufferRefFromData:[self.diskCache dataForKey:url] size:[sizeDic[@"PixelBufferSize"] CGSizeValue]];
202 | tempIsFullPixelImage = [sizeDic[@"IsFullPixelImage"] boolValue];
203 | }
204 | }
205 | }
206 |
207 | *isFullPixelImage = tempIsFullPixelImage;
208 | return ref;
209 | }
210 |
211 | - (void)storeDiskPixelBufferRef:(BkFlutterPixelBufferRef *)buffer url:(NSString *)url isFullPixelImage:(BOOL)isFullPixelImage {
212 | dispatch_async(self.queue, ^{
213 | if (![self.diskCache containsDataForKey:url]) {
214 | //sizekey
215 | CGSize targetSize = CGSizeMake(CVPixelBufferGetWidth(buffer.ref), CVPixelBufferGetHeight(buffer.ref));
216 | NSMutableData *data = [[NSMutableData alloc]init];
217 | NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
218 | [archiver encodeObject:@{@"PixelBufferSize":@(targetSize),@"IsFullPixelImage":@(isFullPixelImage)}
219 | forKey:@"sizeInfo"];
220 | [archiver finishEncoding];
221 |
222 | [self.diskCache setData:[BkFlutterPixelBufferRef dataFromPixelBufferRef:buffer] forKey:url];
223 | [self.diskCache setExtendedData:data forKey:url];
224 | }
225 | });
226 | }
227 |
228 | #pragma tool
229 | - (BOOL)sizeMatch:(CGSize)size toSize:(CGSize)toSize {
230 | //前置处理
231 | size = [self convertSize:size];
232 | toSize = [self convertSize:toSize];
233 |
234 | CGFloat pixelRatio = size.width / size.height;
235 | CGFloat thumbnailRatio = toSize.width / toSize.height;
236 |
237 | if ((pixelRatio >= thumbnailRatio && size.width >= toSize.width)
238 | || (pixelRatio < thumbnailRatio && size.height >= toSize.height)) {
239 | return YES;
240 | }
241 | return NO;
242 | }
243 |
244 | - (CGSize)convertSize:(CGSize)size {
245 | CGFloat width = size.width;
246 | CGFloat height = size.height;
247 |
248 | if (width < 0 || height < 0) {
249 | width = INT_MAX;
250 | height = INT_MAX;
251 | }
252 |
253 | width = width == 0 ? 1 : width;
254 | height = height == 0 ? 1 : height;
255 | return CGSizeMake(width, height);
256 | }
257 |
258 | - (BOOL)isFullPixelImage:(UIImage *)image data:(NSData *)imageData {
259 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
260 |
261 | NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
262 | double pixelWidth = [properties[(__bridge NSString *)kCGImagePropertyPixelWidth] doubleValue];
263 | double pixelHeight = [properties[(__bridge NSString *)kCGImagePropertyPixelHeight] doubleValue];
264 | CFRelease(source);
265 | return CGSizeEqualToSize(image.size, CGSizeMake(pixelWidth, pixelHeight));
266 | }
267 |
268 | //fix SDWebImage Bug
269 | + (UIImage *)createFrameAtIndex:(NSUInteger)index source:(CGImageSourceRef)source scale:(CGFloat)scale preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize options:(NSDictionary *)options {
270 | // Some options need to pass to `CGImageSourceCopyPropertiesAtIndex` before `CGImageSourceCreateImageAtIndex`, or ImageIO will ignore them because they parse once :)
271 | // Parse the image properties
272 | NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, index, (__bridge CFDictionaryRef)options);
273 | double pixelWidth = [properties[(__bridge NSString *)kCGImagePropertyPixelWidth] doubleValue];
274 | double pixelHeight = [properties[(__bridge NSString *)kCGImagePropertyPixelHeight] doubleValue];
275 | CGImagePropertyOrientation exifOrientation = (CGImagePropertyOrientation)[properties[(__bridge NSString *)kCGImagePropertyOrientation] unsignedIntegerValue];
276 | if (!exifOrientation) {
277 | exifOrientation = kCGImagePropertyOrientationUp;
278 | }
279 |
280 | CFStringRef uttype = CGImageSourceGetType(source);
281 | // Check vector format
282 | BOOL isVector = NO;
283 | if ([NSData sd_imageFormatFromUTType:uttype] == SDImageFormatPDF) {
284 | isVector = YES;
285 | }
286 |
287 | NSMutableDictionary *decodingOptions;
288 | if (options) {
289 | decodingOptions = [NSMutableDictionary dictionaryWithDictionary:options];
290 | } else {
291 | decodingOptions = [NSMutableDictionary dictionary];
292 | }
293 | CGImageRef imageRef;
294 | BOOL createFullImage = thumbnailSize.width == 0 || thumbnailSize.height == 0 || pixelWidth == 0 || pixelHeight == 0 || (pixelWidth <= thumbnailSize.width && pixelHeight <= thumbnailSize.height);
295 | if (createFullImage) {
296 | if (isVector) {
297 | if (thumbnailSize.width == 0 || thumbnailSize.height == 0) {
298 | // Provide the default pixel count for vector images, simply just use the screen size
299 | #if SD_WATCH
300 | thumbnailSize = WKInterfaceDevice.currentDevice.screenBounds.size;
301 | #elif SD_UIKIT
302 | thumbnailSize = UIScreen.mainScreen.bounds.size;
303 | #elif SD_MAC
304 | thumbnailSize = NSScreen.mainScreen.frame.size;
305 | #endif
306 | }
307 | CGFloat maxPixelSize = MAX(thumbnailSize.width, thumbnailSize.height);
308 | NSUInteger DPIPerPixel = 2;
309 | NSUInteger rasterizationDPI = maxPixelSize * DPIPerPixel;
310 | decodingOptions[@"kCGImageSourceRasterizationDPI"] = @(rasterizationDPI);
311 | }
312 | imageRef = CGImageSourceCreateImageAtIndex(source, index, (__bridge CFDictionaryRef)[decodingOptions copy]);
313 | } else {
314 | decodingOptions[(__bridge NSString *)kCGImageSourceCreateThumbnailWithTransform] = @(preserveAspectRatio);
315 | CGFloat maxPixelSize;
316 | if (preserveAspectRatio) {
317 | CGFloat pixelRatio = pixelWidth / pixelHeight;
318 | CGFloat thumbnailRatio = thumbnailSize.width / thumbnailSize.height;
319 | if (pixelRatio > thumbnailRatio) {
320 | maxPixelSize = MAX(thumbnailSize.width, thumbnailSize.width / pixelRatio);
321 | } else {
322 | maxPixelSize = MAX(thumbnailSize.height * pixelRatio, thumbnailSize.height);
323 | }
324 | } else {
325 | maxPixelSize = MAX(thumbnailSize.width, thumbnailSize.height);
326 | }
327 | decodingOptions[(__bridge NSString *)kCGImageSourceThumbnailMaxPixelSize] = @(maxPixelSize);
328 | decodingOptions[(__bridge NSString *)kCGImageSourceCreateThumbnailFromImageAlways] = @(YES);
329 | imageRef = CGImageSourceCreateThumbnailAtIndex(source, index, (__bridge CFDictionaryRef)[decodingOptions copy]);
330 | }
331 | if (!imageRef) {
332 | return nil;
333 | }
334 | // Thumbnail image post-process
335 | if (!createFullImage) {
336 | if (preserveAspectRatio) {
337 | // kCGImageSourceCreateThumbnailWithTransform will apply EXIF transform as well, we should not apply twice
338 | exifOrientation = kCGImagePropertyOrientationUp;
339 | } else {
340 | // `CGImageSourceCreateThumbnailAtIndex` take only pixel dimension, if not `preserveAspectRatio`, we should manual scale to the target size
341 | CGImageRef scaledImageRef = [SDImageCoderHelper CGImageCreateScaled:imageRef size:thumbnailSize];
342 | CGImageRelease(imageRef);
343 | imageRef = scaledImageRef;
344 | }
345 | }
346 |
347 | #if SD_UIKIT || SD_WATCH
348 | UIImageOrientation imageOrientation = [SDImageCoderHelper imageOrientationFromEXIFOrientation:exifOrientation];
349 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:imageOrientation];
350 | #else
351 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:exifOrientation];
352 | #endif
353 | CGImageRelease(imageRef);
354 | return image;
355 | }
356 |
357 |
358 | @end
359 |
360 | @implementation BkFlutterPixelBufferRef
361 |
362 | + (BkFlutterPixelBufferRef *)pixelBufferRefFromData:(NSData *)data size:(CGSize)size {
363 | NSDictionary *pixelAttributes = @{(NSString*)kCVPixelBufferIOSurfacePropertiesKey:@{}};
364 |
365 | CVPixelBufferRef pixelBuffer = NULL;
366 |
367 | CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,
368 | size.width,
369 | size.height,
370 | kCVPixelFormatType_32BGRA,
371 | (__bridge CFDictionaryRef)(pixelAttributes),
372 | &pixelBuffer);//kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
373 |
374 | CVPixelBufferLockBaseAddress(pixelBuffer,0);
375 | unsigned char *plane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
376 | unsigned char *bytes = (unsigned char *)data.bytes;
377 | memcpy(plane, bytes, size.width * size.height * 4);
378 |
379 | if (result != kCVReturnSuccess) {
380 | return nil;
381 | }
382 | BkFlutterPixelBufferRef *buffer = [[BkFlutterPixelBufferRef alloc] initWithRef:pixelBuffer];
383 | CVPixelBufferRelease(pixelBuffer);
384 | return buffer;
385 | }
386 |
387 | + (NSData *)dataFromPixelBufferRef:(BkFlutterPixelBufferRef *)pixelBufferRef {
388 | CVPixelBufferLockBaseAddress(pixelBufferRef.ref, 0);
389 | unsigned char* address = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(pixelBufferRef.ref, 0);
390 |
391 | int32_t width = (int32_t)CVPixelBufferGetWidth(pixelBufferRef.ref);
392 | int32_t height = (int32_t)CVPixelBufferGetHeight(pixelBufferRef.ref);
393 |
394 | NSMutableData* data = [NSMutableData dataWithBytes:address length:height * width * 4];
395 | CVPixelBufferUnlockBaseAddress(pixelBufferRef.ref, 0);
396 | return data;
397 | }
398 |
399 | + (BkFlutterPixelBufferRef *)pixelBufferRefWithImage:(UIImage *)sImage {
400 | if (sImage) {
401 | CGImageRef image = [sImage CGImage];
402 | GLuint width = (GLuint)CGImageGetWidth(image);
403 | GLuint height = (GLuint)CGImageGetHeight(image);
404 | CGSize size = CGSizeMake(width, height);
405 | BOOL hasAlpha = bkCGImageRefContainsAlpha(image);
406 |
407 | CFDictionaryRef empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
408 | NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
409 | @(YES), kCVPixelBufferCGImageCompatibilityKey,
410 | @(YES), kCVPixelBufferCGBitmapContextCompatibilityKey,
411 | empty, kCVPixelBufferIOSurfacePropertiesKey, nil];
412 |
413 | CVPixelBufferRef pixelBuffer = NULL;
414 | CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef) options, &pixelBuffer);
415 | NSParameterAssert(status == kCVReturnSuccess && pixelBuffer != NULL);
416 | if (status != kCVReturnSuccess)
417 | return nil;
418 |
419 | CVPixelBufferLockBaseAddress(pixelBuffer, 0);
420 | void *pxdata = CVPixelBufferGetBaseAddress(pixelBuffer);
421 | NSParameterAssert(pxdata != NULL);
422 |
423 | CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
424 | uint32_t bitmapInfo = bkBitmapInfoWithPixelFormatType(kCVPixelFormatType_32BGRA, (bool)hasAlpha);
425 | CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, CVPixelBufferGetBytesPerRow(pixelBuffer), rgbColorSpace, bitmapInfo);
426 |
427 | NSParameterAssert(context);
428 | CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
429 | CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
430 | CFRelease(empty);
431 | CGColorSpaceRelease(rgbColorSpace);
432 | CGContextRelease(context);
433 |
434 | BkFlutterPixelBufferRef *buffer = [[BkFlutterPixelBufferRef alloc] initWithRef:pixelBuffer];
435 | CVPixelBufferRelease(pixelBuffer);
436 | return buffer;
437 | } else {
438 | return nil;
439 | }
440 | }
441 |
442 | - (id)initWithRef:(CVPixelBufferRef)ref {
443 | if (self == [super init]) {
444 | self.ref = ref;
445 | CVPixelBufferRetain(ref);
446 |
447 | }
448 | return self;
449 | }
450 |
451 | - (void)dealloc {
452 | CVPixelBufferRelease(self.ref);
453 | }
454 |
455 |
456 | static uint32_t bkBitmapInfoWithPixelFormatType(OSType inputPixelFormat, bool hasAlpha) {
457 | if (inputPixelFormat == kCVPixelFormatType_32BGRA) {
458 | uint32_t bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
459 | if (!hasAlpha)
460 | bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
461 | return bitmapInfo;
462 | } else if (inputPixelFormat == kCVPixelFormatType_32ARGB) {
463 | return kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big;
464 | } else
465 | return 0;
466 | }
467 |
468 | static BOOL bkCGImageRefContainsAlpha(CGImageRef imageRef) {
469 | if (!imageRef) {
470 | return NO;
471 | }
472 | CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
473 | BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || alphaInfo == kCGImageAlphaNoneSkipFirst ||alphaInfo == kCGImageAlphaNoneSkipLast);
474 | return hasAlpha;
475 | }
476 |
477 | @end
478 |
479 |
480 |
--------------------------------------------------------------------------------
/ios/Classes/BkImageRenderWorker.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface BkImageRenderWorker : NSObject
4 |
5 | @property (nonatomic, assign) BOOL glInitDone;
6 | @property (nonatomic, assign) BOOL disposed;
7 |
8 | - (instancetype)initWithParams:(NSDictionary *)params onNewFrame:(void (^)(NSError* error, CGSize size, BOOL isFullPixel))onNewFrame;
9 | - (void)startWork;
10 | @end
11 |
--------------------------------------------------------------------------------
/ios/Classes/BkImageRenderWorker.m:
--------------------------------------------------------------------------------
1 |
2 | #import "BkImageRenderWorker.h"
3 |
4 | #import
5 |
6 | #import "UIImageView+WebCache.h"
7 | #import "SDWebImageDownloader.h"
8 | #import "SDImageCache.h"
9 | #import "BkFlutterPixelBufferCache.h"
10 |
11 | @interface BkImageRenderWorker ()
12 |
13 | @property (nonatomic, copy) NSString *url;
14 | @property (nonatomic, assign) CGFloat aspectRatio;
15 | @property (nonatomic, assign) CGFloat width;
16 | @property (nonatomic, assign) CGFloat height;
17 | @property (nonatomic, assign) CGFloat widthPixel;
18 | @property (nonatomic, assign) CGFloat heightPixel;
19 | @property (nonatomic, assign) BOOL autoResize;
20 | @property (nonatomic, copy) void (^onNewFrame)(NSError* error, CGSize size, BOOL isFullPixel);
21 | @property (nonatomic, strong) BkFlutterPixelBufferRef *pixelBufferRef;
22 |
23 | @end
24 |
25 | @implementation BkImageRenderWorker
26 |
27 | - (instancetype)initWithParams:(NSDictionary *)params onNewFrame:(void (^)(NSError* error, CGSize size, BOOL isFullPixel))onNewFrame {
28 | self = [super init];
29 |
30 | if (self) {
31 | _onNewFrame = onNewFrame;
32 | [self initParams:params];
33 | }
34 |
35 | return self;
36 | }
37 |
38 | - (void)initParams:(NSDictionary *)params {
39 | _url = params[@"url"];
40 | _aspectRatio = [[UIScreen mainScreen] scale];
41 | if (params[@"autoResize"])
42 | _autoResize = [params[@"autoResize"] boolValue];
43 |
44 | if (params[@"width"] && params[@"height"]) {
45 | //图片像素值没有小数
46 | _width = floor([params[@"width"] floatValue] * _aspectRatio);
47 | _height = floor([params[@"height"] floatValue] * _aspectRatio);
48 | } else {
49 | _width = -1;
50 | _height = -1;
51 | }
52 |
53 | }
54 |
55 | - (void)startWork{
56 | __weak typeof(self) weakSelf = self;
57 | [[BkFlutterPixelBufferCache sharedInstance] loadImageAndPixelBuffer:self.url size:_autoResize ? CGSizeMake(_width, _height) : CGSizeMake(-1, -1) completion:^(BkFlutterPixelBufferRef * _Nonnull buffer, NSError* error, BOOL isFullPixel) {
58 | __strong typeof(weakSelf) strongSelf = weakSelf;
59 | [strongSelf callBufferRef:buffer isFullPixel:isFullPixel error:error];
60 | }];
61 | }
62 |
63 | - (void)callBufferRef:(BkFlutterPixelBufferRef *)buffer isFullPixel:(BOOL)isFullPixel error:(NSError *)error{
64 | if (buffer && !error) {
65 | self.pixelBufferRef = buffer;
66 | CGSize size = CGSizeMake(CVPixelBufferGetWidth(buffer.ref) / _aspectRatio, CVPixelBufferGetHeight(buffer.ref) / _aspectRatio);
67 | dispatch_async(dispatch_get_main_queue(), ^{
68 | self.onNewFrame(nil, size,isFullPixel);
69 | });
70 | } else {
71 | dispatch_async(dispatch_get_main_queue(), ^{
72 | self.onNewFrame(error, CGSizeZero, isFullPixel);
73 | });
74 | }
75 | }
76 |
77 |
78 | #pragma mark - FlutterTexture
79 |
80 | - (CVPixelBufferRef)copyPixelBuffer{
81 | if (self.pixelBufferRef) {
82 | CVPixelBufferRetain(self.pixelBufferRef.ref);
83 | return self.pixelBufferRef.ref;
84 | } else {
85 | return NULL;
86 | }
87 | }
88 |
89 | - (void)onTextureUnregistered:(NSObject*)texture {
90 | _pixelBufferRef = nil;
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/ios/bk_flutter_image.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint bk_flutter_image.podspec' to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'bk_flutter_image'
7 | s.version = '0.0.1'
8 | s.summary = 'A new flutter plugin project.'
9 | s.description = <<-DESC
10 | A new flutter plugin project.
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.resource_bundles = {
18 | 'BkFlutterImage' => ['Assets/**/*']
19 | }
20 |
21 |
22 | s.dependency 'Flutter'
23 | s.dependency 'SDWebImage', '>= 5.12.6'
24 |
25 | s.ios.deployment_target = '10.0'
26 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
27 | # s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
28 | end
29 |
30 |
--------------------------------------------------------------------------------
/lib/bk_flutter_image.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | import 'package:bk_flutter_image/bk_flutter_image_native.dart'
4 | if (dart.library.html) 'package:bk_flutter_image/bk_flutter_image_web.dart';
5 |
6 | class BkFlutterImage extends BkFlutterImageImpl {
7 | const BkFlutterImage({
8 | Key? key,
9 | required this.url,
10 | this.placeholder,
11 | this.width,
12 | this.height,
13 | this.fit = BoxFit.none,
14 | this.autoResize = true,
15 | this.cacheWidth,
16 | this.cacheHeight,
17 | this.imageErrorBuilder,
18 | }) : super(
19 | key: key,
20 | url: url,
21 | placeholder: placeholder,
22 | width: width,
23 | height: height,
24 | fit: BoxFit.none,
25 | autoResize: true,
26 | cacheWidth: cacheWidth,
27 | cacheHeight: cacheHeight,
28 | imageErrorBuilder: imageErrorBuilder);
29 |
30 | final String url; // 图片web地址
31 | final double? width; // 组件宽度
32 | final double? height; // 组件高度
33 | final String? placeholder;
34 | final BoxFit fit; // 图片显示模式
35 | final bool autoResize; // 是否下采样图片大小
36 | final double? cacheWidth; // 下采样的宽度
37 | final double? cacheHeight; // 下采样的高度
38 | final ImageErrorWidgetBuilder? imageErrorBuilder;
39 |
40 | static void setCacheMaxSize(double diskMaxSize, double memoryMaxSize) {
41 | BkFlutterImageImpl.setCacheMaxSize(diskMaxSize, memoryMaxSize);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/bk_flutter_image_native.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter/rendering.dart';
7 | import 'package:flutter/services.dart';
8 |
9 | typedef ImageCallback = void Function(ImageViewController controller);
10 | typedef SizeCallBack = void Function(Size size);
11 |
12 | class BkFlutterImageImpl extends StatefulWidget {
13 | const BkFlutterImageImpl({
14 | Key? key,
15 | required this.url,
16 | this.placeholder,
17 | this.width,
18 | this.height,
19 | this.fit = BoxFit.none,
20 | this.autoResize = true,
21 | this.cacheWidth,
22 | this.cacheHeight,
23 | this.imageErrorBuilder,
24 | this.color
25 | }) : super(key: key);
26 |
27 | static void registerWith(dynamic registrar) {}
28 |
29 | final String url; // 图片web地址
30 | final double? width; // 组件宽度
31 | final double? height; // 组件高度
32 | final String? placeholder;
33 | final BoxFit fit; // 图片显示模式
34 | final bool autoResize; // 是否下采样图片大小
35 | final double? cacheWidth; // 下采样的宽度
36 | final double? cacheHeight; // 下采样的高度
37 | final ImageErrorWidgetBuilder? imageErrorBuilder;
38 | final Color? color;
39 |
40 | @override
41 | State createState() => _BkFlutterImageState();
42 |
43 | static void setCacheMaxSize(double diskMaxSize, double memoryMaxSize) {
44 | ImageViewController.setCacheMaxSize(diskMaxSize, memoryMaxSize);
45 | }
46 | }
47 |
48 | class _BkFlutterImageState extends State {
49 | ImageViewController _controller = ImageViewController();
50 | int textureId = -1;
51 | Object? error;
52 |
53 | bool isFullPixel = false;
54 | double textureWidth = -1;
55 | double textureHeight = -1;
56 | String textureUrl = '';
57 | bool textureAutoResize = true;
58 |
59 | Size? realSize;
60 |
61 | @override
62 | Widget build(BuildContext context) {
63 | List idList = List.empty(growable: true);
64 | idList.add( LayoutId( id: _LayoutType.None,
65 | child: Container(color: widget.color ?? Color.fromARGB(0, 0, 0, 0))));
66 | _LayoutType layoutType = _LayoutType.None;
67 | if (textureId != -1) {
68 | idList.add(LayoutId(id: _LayoutType.Image, child: Texture(textureId: textureId)));
69 | layoutType = _LayoutType.Image;
70 | } else if (error != null && widget.imageErrorBuilder != null) {
71 | idList.add(LayoutId(id: _LayoutType.Error, child: widget.imageErrorBuilder!(context, error!, null)));
72 | layoutType = _LayoutType.Error;
73 | } else if (error == null && widget.placeholder != null) {
74 | idList.add(LayoutId(
75 | id: _LayoutType.PlaceHolder,
76 | child: Image.asset(
77 | widget.placeholder!,
78 | fit: widget.fit,
79 | width: widget.width,
80 | height: widget.height,
81 | )));
82 | layoutType = _LayoutType.PlaceHolder;
83 | }
84 | return _BkFlutterImageRenderObjectWidget(
85 | layoutType: layoutType,
86 | width: widget.width,
87 | height: widget.height,
88 | fit: widget.fit,
89 | callBack: sizeChange,
90 | imageSize: Size(textureWidth,textureHeight),
91 | children: idList,);
92 | }
93 |
94 | @override
95 | void initState() {
96 | super.initState();
97 | if (widget.cacheWidth != null && widget.cacheHeight != null) {
98 | updateTexture();
99 | }
100 | }
101 |
102 | @override
103 | void didUpdateWidget(covariant BkFlutterImageImpl oldWidget) {
104 | super.didUpdateWidget(oldWidget);
105 | if (maybeUpdate(widget, null)) {
106 | updateTexture();
107 | }
108 | }
109 |
110 | void sizeChange(Size size) {
111 | Size? tempRealSize = size == Size(double.infinity,double.infinity) ?
112 | null : Size(size.width == double.infinity ? size.height : size.width, size.height == double.infinity ? size.width : size.height);
113 | if (tempRealSize == realSize) {
114 | return;
115 | }
116 | realSize = tempRealSize;
117 | if (maybeUpdate(widget, size)) {
118 | updateTexture();
119 | }
120 | }
121 |
122 | bool maybeUpdate(BkFlutterImageImpl newImage, Size? realSize) {
123 | if (textureUrl != newImage.url || textureId == -1) {
124 | return true;
125 | }
126 | //计算现有size
127 | Size oldSize = isFullPixel
128 | ? Size(double.maxFinite, double.maxFinite)
129 | : Size(textureWidth, textureHeight);
130 |
131 | Size requestSize =
132 | (newImage.cacheWidth != null && newImage.cacheHeight != null)
133 | ? Size(newImage.cacheWidth!, newImage.cacheHeight!)
134 | : realSize != null
135 | ? realSize
136 | : Size(0.1, 0.1);
137 | Size newSize = newImage.autoResize
138 | ? requestSize
139 | : Size(double.maxFinite, double.maxFinite);
140 |
141 | if (sizeMatchSize(oldSize, newSize)) {
142 | return false;
143 | }
144 | return true;
145 | }
146 |
147 | bool sizeMatchSize(Size fromSize, Size toSize) {
148 | double oldImageRatio = fromSize.width / fromSize.height;
149 | double newImageRatio = toSize.width / toSize.height;
150 |
151 | if (oldImageRatio >= newImageRatio && fromSize.width >= toSize.width ||
152 | oldImageRatio < newImageRatio && fromSize.height >= toSize.height) {
153 | return true;
154 | }
155 |
156 | return false;
157 | }
158 |
159 | void updateTexture() {
160 | print("updateTexture");
161 |
162 | _controller.dispose();
163 | _controller = ImageViewController();
164 | _controller
165 | .initialize(widget.url,
166 | width: widget.cacheWidth ?? realSize?.width,
167 | height: widget.cacheHeight ?? realSize?.height,
168 | autoResize: widget.autoResize)
169 | .then((value) {
170 | if (mounted) {
171 | setState(() {
172 | if (value != null) {
173 | textureId = value['textureId'];
174 | textureWidth =
175 | textureId == -1 ? -1 : value['textureWidth'].roundToDouble();
176 | textureHeight =
177 | textureId == -1 ? -1 : value['textureHeight'].roundToDouble();
178 | isFullPixel = value['isFullPixel'];
179 | error = value['error'].toString().isNotEmpty
180 | ? value['error'].toString()
181 | : (textureId == -1 ? '注册纹理失败' : null);
182 | textureAutoResize = widget.autoResize;
183 | textureUrl = widget.url;
184 | }
185 | });
186 | }
187 | });
188 | }
189 |
190 | @override
191 | void dispose() {
192 | //不加上这个会内存泄漏
193 | _controller.dispose();
194 | print("_controller.dispose()");
195 | super.dispose();
196 | }
197 | }
198 |
199 | class ImageViewController {
200 | static MethodChannel _channel = MethodChannel('bk_flutter_image');
201 | bool disposed = false;
202 | int textureId = -1;
203 |
204 | Future initialize(String url,
205 | {double? width, double? height, bool autoResize = true}) async {
206 | String mapString = await _channel.invokeMethod('create', {
207 | 'width': width,
208 | 'height': height,
209 | 'url': url,
210 | 'autoResize': autoResize,
211 | });
212 | Map textureInfo = json.decode(mapString);
213 | textureId = textureInfo['textureId'];
214 | return Future.value(textureInfo);
215 | }
216 |
217 | void dispose() {
218 | logger('dispose $textureId');
219 | _channel.invokeMethod('dispose', {'textureId': textureId});
220 | disposed = true;
221 | }
222 |
223 | static void setCacheMaxSize(double diskMaxSize, double memoryMaxSize) {
224 | _channel.invokeMethod('setCacheSize',
225 | {'memoryMaxSize': memoryMaxSize, 'diskMaxSize': diskMaxSize});
226 | }
227 | }
228 |
229 |
230 | enum _LayoutType { None, PlaceHolder, Image, Error}
231 |
232 | class _BkFlutterImageRenderObject extends RenderBox
233 | with
234 | ContainerRenderObjectMixin,
235 | RenderBoxContainerDefaultsMixin {
236 |
237 | SizeCallBack callBack;
238 | BoxFit _fit;
239 | Size _imageSize;
240 | double? _width;
241 | double? _height;
242 | Offset? imageOffset;
243 |
244 | _LayoutType layoutType = _LayoutType.None;
245 |
246 |
247 | _BkFlutterImageRenderObject({required this.layoutType,
248 | required Size imageSize,
249 | required this.callBack,
250 | required BoxFit fit,
251 | double? width,
252 | double? height,
253 | children,}): _imageSize = imageSize ,
254 | _fit = fit,
255 | _width = width,
256 | _height = height{
257 | addAll(children);
258 | }
259 |
260 |
261 | set fit(BoxFit fit) {
262 | if ( fit == _fit)
263 | return;
264 | _fit = fit;
265 | markNeedsLayout();
266 | }
267 |
268 | set imageSize(Size imageSize) {
269 | if ( imageSize == _imageSize)
270 | return;
271 | _imageSize = imageSize;
272 | markNeedsLayout();
273 | }
274 |
275 | set width(double? width) {
276 | if ( width == _width)
277 | return;
278 | _width = width;
279 | markNeedsLayout();
280 | }
281 |
282 | set height(double? height) {
283 | if ( height == _height)
284 | return;
285 | _height = height;
286 | markNeedsLayout();
287 | }
288 |
289 | Size get imageSize => _imageSize;
290 | BoxFit get fit => _fit;
291 | double? get height => _height;
292 | double? get width => _width;
293 |
294 | @override
295 | void setupParentData(RenderBox child) {
296 | if (child.parentData is! MultiChildLayoutParentData)
297 | child.parentData = MultiChildLayoutParentData();
298 | }
299 |
300 | @override
301 | void paint(PaintingContext context, Offset offset) {
302 | context.pushClipRect(
303 | needsCompositing,
304 | offset,
305 | Offset.zero & size,
306 | defaultPaint,
307 | );
308 | }
309 |
310 | Size calculateImageContainerSize(BoxConstraints c) {
311 | if (imageSize.width > 0 && imageSize.height > 0) {
312 | //1.计算逻辑,确定宽高是否都是范围值
313 | double? realWidth = c.minWidth == c.maxWidth ? c.maxWidth : null;
314 | double? realHeight = c.minHeight == c.maxHeight ? c.maxHeight : null;
315 | if (realHeight != null || realWidth != null) {
316 | //2.宽不是范围值,就采用宽,如果是则通过高计算,同理高也一样。
317 | realWidth =
318 | realWidth ?? imageSize.width / imageSize.height * realHeight!;
319 | realHeight =
320 | realHeight ?? imageSize.height / imageSize.width * realWidth;
321 | } else if (constraints.isSatisfiedBy(imageSize)) {
322 | //3.宽和搞都是范围值,并且image的大小都在这个范围内,那就直接采用image的大小
323 | realWidth = imageSize.width;
324 | realHeight = imageSize.height;
325 | } else if (c.minWidth < imageSize.width &&
326 | imageSize.width <= c.maxWidth) {
327 | //4.宽和搞都是范围值,但是高不在范围内,那么就把高直接换算成范围边界的就近值,然后宽按照比例计算宽。
328 | realHeight = c.constrainHeight(imageSize.height);
329 | realWidth = imageSize.width / imageSize.height * realHeight;
330 | } else if (c.minHeight < imageSize.height &&
331 | imageSize.height <= c.maxHeight) {
332 | //4.宽和搞都是范围值,但是宽不在范围内,那么就把宽直接换算成范围边界的就近值,然后高按照比例计算宽。
333 | realWidth = c.constrainWidth(imageSize.width);
334 | realHeight = imageSize.height / imageSize.width * realWidth;
335 | } else {
336 | //5.如果都不满足,那么就取值各自范围的Max,如果是无穷大,则取最小值。
337 | realWidth = c.maxWidth == double.infinity ? c.minWidth : c.maxWidth;
338 | realHeight = c.maxHeight == double.infinity ? c.minHeight : c.maxHeight;
339 | }
340 | return Size(realWidth,realHeight);
341 | }
342 | return Size.zero;
343 | }
344 |
345 | Size calculateImageContentSize(Size size){
346 | if (imageSize.width > 0 &&
347 | imageSize.height > 0) {
348 | Size realSize = imageSize;
349 | double wRatio = size.width / imageSize.width;
350 | double hRatio = size.height / imageSize.height;
351 | switch (fit) {
352 | case BoxFit.contain:
353 | if (wRatio <= hRatio) {
354 | realSize = Size(size.width, imageSize.height * wRatio);
355 | } else {
356 | realSize = Size(imageSize.width * hRatio, size.height);
357 | }
358 | break;
359 | case BoxFit.cover:
360 | if (wRatio >= hRatio) {
361 | realSize = Size(size.width, imageSize.height * wRatio);
362 | } else {
363 | realSize = Size(imageSize.width * hRatio, size.height);
364 | }
365 | break;
366 | case BoxFit.fitWidth:
367 | realSize = Size(size.width, imageSize.height * wRatio);
368 | break;
369 | case BoxFit.fitHeight:
370 | realSize = Size(imageSize.width * hRatio, size.height);
371 | break;
372 | case BoxFit.scaleDown:
373 | if (wRatio < 1 || hRatio < 1) {
374 | if (wRatio <= hRatio) {
375 | realSize = Size(size.width, imageSize.height * wRatio);
376 | } else {
377 | realSize = Size(imageSize.width * hRatio, size.height);
378 | }
379 | } else {
380 | realSize = imageSize;
381 | }
382 | break;
383 | case BoxFit.fill:
384 | realSize = size;
385 | break;
386 | case BoxFit.none:
387 | realSize = imageSize;
388 | break;
389 | }
390 | return realSize;
391 | }
392 | return Size.zero;
393 | }
394 |
395 | @override
396 | void performLayout() {
397 | Map