fullscreen: work with uri/path & flutter image widget
This commit is contained in:
parent
1313d0c845
commit
10be8b1f2e
5 changed files with 282 additions and 69 deletions
|
@ -93,7 +93,8 @@ public class MainActivity extends FlutterActivity {
|
||||||
case "cancelGetImageBytes": {
|
case "cancelGetImageBytes": {
|
||||||
String uri = call.argument("uri");
|
String uri = call.argument("uri");
|
||||||
thumbnailFetcher.cancel(uri);
|
thumbnailFetcher.cancel(uri);
|
||||||
result.success(null);
|
// do not send `null`, as it closes the channel
|
||||||
|
result.success("");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -214,12 +215,14 @@ class BitmapWorkerTask extends AsyncTask<BitmapWorkerTask.MyTaskParams, Void, Bi
|
||||||
bmp.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
bmp.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||||
data = stream.toByteArray();
|
data = stream.toByteArray();
|
||||||
}
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.d(LOG_TAG, "getImageBytes with uri=" + entry.getUri() + " interrupted");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
Glide.with(activity).clear(target);
|
Glide.with(activity).clear(target);
|
||||||
} else {
|
} else {
|
||||||
Log.d(LOG_TAG, "getImageBytes with uri=" + entry.getUri() + " (cancelled)");
|
Log.d(LOG_TAG, "getImageBytes with uri=" + entry.getUri() + " cancelled");
|
||||||
}
|
}
|
||||||
return new MyTaskResult(p, data);
|
return new MyTaskResult(p, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,16 @@
|
||||||
package deckers.thibault.aves.model;
|
package deckers.thibault.aves.model;
|
||||||
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.MediaStore;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import deckers.thibault.aves.utils.Constants;
|
import deckers.thibault.aves.utils.Constants;
|
||||||
|
|
||||||
public class ImageEntry {
|
public class ImageEntry {
|
||||||
private static final Uri mediaStoreContentUri = MediaStore.Files.getContentUri("external");
|
|
||||||
|
|
||||||
// from source
|
// from source
|
||||||
private String path; // best effort to get local path from content providers
|
private String path; // best effort to get local path from content providers
|
||||||
private long contentId; // should be defined for mediastore, use full URI otherwise
|
private long contentId; // should be defined for mediastore, use full URI otherwise
|
||||||
|
@ -39,11 +31,13 @@ public class ImageEntry {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageEntry(String path, long id, String mimeType, int width, int height, int orientationDegrees, long sizeBytes,
|
// uri: content provider uri
|
||||||
|
// path: FileUtils.getPathFromUri(activity, itemUri) is useful (for Download, File, etc.) but is slower than directly using `MediaStore.MediaColumns.DATA` from the MediaStore query
|
||||||
|
public ImageEntry(Uri uri, String path, long id, String mimeType, int width, int height, int orientationDegrees, long sizeBytes,
|
||||||
String title, long dateModifiedSecs, long dateTakenMillis, String bucketDisplayName, long durationMillis) {
|
String title, long dateModifiedSecs, long dateTakenMillis, String bucketDisplayName, long durationMillis) {
|
||||||
|
this.uri = uri;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.contentId = id;
|
this.contentId = id;
|
||||||
this.uri = null;
|
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
@ -59,6 +53,7 @@ public class ImageEntry {
|
||||||
|
|
||||||
public ImageEntry(Map map) {
|
public ImageEntry(Map map) {
|
||||||
this(
|
this(
|
||||||
|
Uri.parse((String) map.get("uri")),
|
||||||
(String) map.get("path"),
|
(String) map.get("path"),
|
||||||
toLong(map.get("contentId")),
|
toLong(map.get("contentId")),
|
||||||
(String) map.get("mimeType"),
|
(String) map.get("mimeType"),
|
||||||
|
@ -76,6 +71,7 @@ public class ImageEntry {
|
||||||
|
|
||||||
public static Map toMap(ImageEntry entry) {
|
public static Map toMap(ImageEntry entry) {
|
||||||
return new HashMap<String, Object>() {{
|
return new HashMap<String, Object>() {{
|
||||||
|
put("uri", entry.uri.toString());
|
||||||
put("path", entry.path);
|
put("path", entry.path);
|
||||||
put("contentId", entry.contentId);
|
put("contentId", entry.contentId);
|
||||||
put("mimeType", entry.mimeType);
|
put("mimeType", entry.mimeType);
|
||||||
|
@ -88,8 +84,6 @@ public class ImageEntry {
|
||||||
put("sourceDateTakenMillis", entry.sourceDateTakenMillis);
|
put("sourceDateTakenMillis", entry.sourceDateTakenMillis);
|
||||||
put("bucketDisplayName", entry.bucketDisplayName);
|
put("bucketDisplayName", entry.bucketDisplayName);
|
||||||
put("durationMillis", entry.durationMillis);
|
put("durationMillis", entry.durationMillis);
|
||||||
//
|
|
||||||
put("uri", entry.getUri().toString());
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +91,10 @@ public class ImageEntry {
|
||||||
isVideo = mimeType.startsWith(Constants.MIME_VIDEO);
|
isVideo = mimeType.startsWith(Constants.MIME_VIDEO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uri getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return path;
|
return path;
|
||||||
|
@ -106,18 +104,6 @@ public class ImageEntry {
|
||||||
return path == null ? null : new File(path).getName();
|
return path == null ? null : new File(path).getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream(Context context) throws FileNotFoundException {
|
|
||||||
// FileInputStream is faster than input stream from ContentResolver
|
|
||||||
return path != null ? new FileInputStream(path) : context.getContentResolver().openInputStream(getUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uri getUri() {
|
|
||||||
if (uri != null) {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
return ContentUris.withAppendedId(mediaStoreContentUri, contentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVideo() {
|
public boolean isVideo() {
|
||||||
return isVideo;
|
return isVideo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package deckers.thibault.aves.model.provider;
|
package deckers.thibault.aves.model.provider;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.ContentUris;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
@ -39,22 +40,24 @@ public class MediaStoreImageProvider {
|
||||||
|
|
||||||
|
|
||||||
public List<ImageEntry> fetchAll(Activity activity) {
|
public List<ImageEntry> fetchAll(Activity activity) {
|
||||||
return fetch(activity, FILES_URI, null);
|
return fetch(activity, FILES_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImageEntry> fetch(final Activity activity, final Uri uri, String mimeType) {
|
private List<ImageEntry> fetch(final Activity activity, final Uri queryUri) {
|
||||||
ArrayList<ImageEntry> entries = new ArrayList<>();
|
ArrayList<ImageEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
// URI should refer to the "files" table, not to the "images" or "videos" one,
|
// URI should refer to the "files" table, not to the "images" or "videos" one,
|
||||||
// as our projection includes a mix of columns from both
|
// as our projection includes a mix of columns from both
|
||||||
Uri filesUri = uri;
|
Uri filesUri = queryUri;
|
||||||
if (!FILES_URI.equals(uri)) {
|
if (!FILES_URI.equals(queryUri)) {
|
||||||
String id = uri.getLastPathSegment();
|
String id = queryUri.getLastPathSegment();
|
||||||
filesUri = Uri.withAppendedPath(FILES_URI, id);
|
filesUri = Uri.withAppendedPath(FILES_URI, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String orderBy = MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Cursor cursor = activity.getContentResolver().query(filesUri, PROJECTION, SELECTION, null, null);
|
Cursor cursor = activity.getContentResolver().query(filesUri, PROJECTION, SELECTION, null, orderBy);
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
Log.d(LOG_TAG, "fetch query returned " + cursor.getCount() + " entries");
|
Log.d(LOG_TAG, "fetch query returned " + cursor.getCount() + " entries");
|
||||||
|
|
||||||
|
@ -72,9 +75,12 @@ public class MediaStoreImageProvider {
|
||||||
int durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
|
int durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
|
long contentId = cursor.getLong(idColumn);
|
||||||
|
Uri itemUri = ContentUris.withAppendedId(FILES_URI, contentId);
|
||||||
ImageEntry imageEntry = new ImageEntry(
|
ImageEntry imageEntry = new ImageEntry(
|
||||||
|
itemUri,
|
||||||
cursor.getString(pathColumn),
|
cursor.getString(pathColumn),
|
||||||
cursor.getLong(idColumn),
|
contentId,
|
||||||
cursor.getString(mimeTypeColumn),
|
cursor.getString(mimeTypeColumn),
|
||||||
cursor.getInt(widthColumn),
|
cursor.getInt(widthColumn),
|
||||||
cursor.getInt(heightColumn),
|
cursor.getInt(heightColumn),
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007-2008 OpenIntents.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* This file was modified by the Flutter authors from the following original file:
|
||||||
|
* https://raw.githubusercontent.com/iPaulPro/aFileChooser/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TLAD: copied from https://raw.githubusercontent.com/flutter/plugins/master/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java
|
||||||
|
|
||||||
|
package deckers.thibault.aves.utils;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
public String getPathFromUri(final Context context, final Uri uri) {
|
||||||
|
String path = getPathFromLocalUri(context, uri);
|
||||||
|
if (path == null) {
|
||||||
|
path = getPathFromRemoteUri(context, uri);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private String getPathFromLocalUri(final Context context, final Uri uri) {
|
||||||
|
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
if (isExternalStorageDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
}
|
||||||
|
} else if (isDownloadsDocument(uri)) {
|
||||||
|
final String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(id)) {
|
||||||
|
try {
|
||||||
|
final Uri contentUri =
|
||||||
|
ContentUris.withAppendedId(
|
||||||
|
Uri.parse(Environment.DIRECTORY_DOWNLOADS), Long.valueOf(id));
|
||||||
|
return getDataColumn(context, contentUri, null, null);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (isMediaDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
Uri contentUri = null;
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selection = "_id=?";
|
||||||
|
final String[] selectionArgs = new String[] {split[1]};
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
|
||||||
|
// Return the remote address
|
||||||
|
if (isGooglePhotosUri(uri)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDataColumn(
|
||||||
|
Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
|
||||||
|
final String column = "_data";
|
||||||
|
final String[] projection = {column};
|
||||||
|
try (Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int column_index = cursor.getColumnIndex(column);
|
||||||
|
|
||||||
|
//yandex.disk and dropbox do not have _data column
|
||||||
|
if (column_index == -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor.getString(column_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPathFromRemoteUri(final Context context, final Uri uri) {
|
||||||
|
// The code below is why Java now has try-with-resources and the Files utility.
|
||||||
|
File file = null;
|
||||||
|
InputStream inputStream = null;
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
String extension = getImageExtension(context, uri);
|
||||||
|
inputStream = context.getContentResolver().openInputStream(uri);
|
||||||
|
file = File.createTempFile("image_picker", extension, context.getCacheDir());
|
||||||
|
outputStream = new FileOutputStream(file);
|
||||||
|
if (inputStream != null) {
|
||||||
|
copy(inputStream, outputStream);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// If closing the output stream fails, we cannot be sure that the
|
||||||
|
// target file was written in full. Flushing the stream merely moves
|
||||||
|
// the bytes into the OS, not necessarily to the file.
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success ? file.getPath() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return extension of image with dot, or default .jpg if it none. */
|
||||||
|
private static String getImageExtension(Context context, Uri uriImage) {
|
||||||
|
String extension = null;
|
||||||
|
|
||||||
|
try (Cursor cursor = context
|
||||||
|
.getContentResolver()
|
||||||
|
.query(uriImage, new String[]{MediaStore.MediaColumns.MIME_TYPE}, null, null, null)) {
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
|
String mimeType = cursor.getString(0);
|
||||||
|
|
||||||
|
extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extension == null) {
|
||||||
|
//default extension for matches the previous behavior of the plugin
|
||||||
|
extension = "jpg";
|
||||||
|
}
|
||||||
|
return "." + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copy(InputStream in, OutputStream out) throws IOException {
|
||||||
|
final byte[] buffer = new byte[4 * 1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMediaDocument(Uri uri) {
|
||||||
|
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isGooglePhotosUri(Uri uri) {
|
||||||
|
return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:aves/model/image_fetcher.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:transparent_image/transparent_image.dart';
|
||||||
|
|
||||||
class ImageFullscreenPage extends StatefulWidget {
|
class ImageFullscreenPage extends StatefulWidget {
|
||||||
final Map entry;
|
final Map entry;
|
||||||
|
@ -15,14 +16,16 @@ class ImageFullscreenPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageFullscreenPageState extends State<ImageFullscreenPage> {
|
class ImageFullscreenPageState extends State<ImageFullscreenPage> {
|
||||||
Future<Uint8List> loader;
|
|
||||||
|
|
||||||
int get imageWidth => widget.entry['width'];
|
int get imageWidth => widget.entry['width'];
|
||||||
|
|
||||||
int get imageHeight => widget.entry['height'];
|
int get imageHeight => widget.entry['height'];
|
||||||
|
|
||||||
String get uri => widget.entry['uri'];
|
String get uri => widget.entry['uri'];
|
||||||
|
|
||||||
|
String get path => widget.entry['path'];
|
||||||
|
|
||||||
|
double requestWidth, requestHeight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -30,30 +33,29 @@ class ImageFullscreenPageState extends State<ImageFullscreenPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
ImageFetcher.cancelGetImageBytes(uri);
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (loader == null) {
|
if (requestWidth == null || requestHeight == null) {
|
||||||
var mediaQuery = MediaQuery.of(context);
|
var mediaQuery = MediaQuery.of(context);
|
||||||
var screenSize = mediaQuery.size;
|
var screenSize = mediaQuery.size;
|
||||||
var dpr = mediaQuery.devicePixelRatio;
|
var dpr = mediaQuery.devicePixelRatio;
|
||||||
var requestWidth = imageWidth * dpr;
|
requestWidth = imageWidth * dpr;
|
||||||
var requestHeight = imageHeight * dpr;
|
requestHeight = imageHeight * dpr;
|
||||||
if (imageWidth > screenSize.width || imageHeight > screenSize.height) {
|
if (imageWidth > screenSize.width || imageHeight > screenSize.height) {
|
||||||
var ratio = max(imageWidth / screenSize.width, imageHeight / screenSize.height);
|
var ratio = max(imageWidth / screenSize.width, imageHeight / screenSize.height);
|
||||||
requestWidth /= ratio;
|
requestWidth /= ratio;
|
||||||
requestHeight /= ratio;
|
requestHeight /= ratio;
|
||||||
}
|
}
|
||||||
loader = ImageFetcher.getImageBytes(widget.entry, requestWidth.round(), requestHeight.round());
|
|
||||||
}
|
}
|
||||||
return FutureBuilder(
|
return MediaQuery.removeViewInsets(
|
||||||
future: loader,
|
context: context,
|
||||||
builder: (futureContext, AsyncSnapshot<Uint8List> snapshot) {
|
// remove bottom view insets to paint underneath the translucent navigation bar
|
||||||
var ready = snapshot.connectionState == ConnectionState.done && !snapshot.hasError;
|
removeBottom: true,
|
||||||
return Hero(
|
child: Scaffold(
|
||||||
|
body: Hero(
|
||||||
tag: uri,
|
tag: uri,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -62,21 +64,26 @@ class ImageFullscreenPageState extends State<ImageFullscreenPage> {
|
||||||
? CircularProgressIndicator()
|
? CircularProgressIndicator()
|
||||||
: Image.memory(
|
: Image.memory(
|
||||||
widget.thumbnail,
|
widget.thumbnail,
|
||||||
width: imageWidth.toDouble(),
|
width: requestWidth,
|
||||||
height: imageHeight.toDouble(),
|
height: requestHeight,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (ready)
|
Center(
|
||||||
Image.memory(
|
child: FadeInImage(
|
||||||
snapshot.data,
|
placeholder: MemoryImage(kTransparentImage),
|
||||||
width: imageWidth.toDouble(),
|
image: FileImage(File(path)),
|
||||||
height: imageHeight.toDouble(),
|
fadeOutDuration: Duration(milliseconds: 1),
|
||||||
|
fadeInDuration: Duration(milliseconds: 200),
|
||||||
|
width: requestWidth,
|
||||||
|
height: requestHeight,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue