Quellcode durchsuchen

Fix issues on iOS

Vishnu Mohandas vor 5 Jahren
Ursprung
Commit
4a892e2ce3

+ 20 - 24
lib/db/db_helper.dart

@@ -14,11 +14,9 @@ class DatabaseHelper {
   static final columnGeneratedId = '_id';
   static final columnUploadedFileId = 'uploaded_file_id';
   static final columnLocalId = 'local_id';
-  static final columnLocalPath = 'local_path';
-  static final columnRelativePath = 'relative_path';
-  static final columnThumbnailPath = 'thumbnail_path';
-  static final columnPath = 'path';
-  static final columnHash = 'hash';
+  static final columnTitle = 'title';
+  static final columnPathName = 'path_name';
+  static final columnRemotePath = 'remote_path';
   static final columnIsDeleted = 'is_deleted';
   static final columnCreateTimestamp = 'create_timestamp';
   static final columnSyncTimestamp = 'sync_timestamp';
@@ -51,11 +49,9 @@ class DatabaseHelper {
             $columnGeneratedId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
             $columnLocalId TEXT,
             $columnUploadedFileId INTEGER NOT NULL,
-            $columnLocalPath TEXT NOT NULL,
-            $columnRelativePath TEXT NOT NULL,
-            $columnThumbnailPath TEXT NOT NULL,
-            $columnPath TEXT,
-            $columnHash TEXT NOT NULL,
+            $columnTitle TEXT NOT NULL,
+            $columnPathName TEXT NOT NULL,
+            $columnRemotePath TEXT,
             $columnIsDeleted INTEGER DEFAULT 0,
             $columnCreateTimestamp TEXT NOT NULL,
             $columnSyncTimestamp TEXT
@@ -103,16 +99,20 @@ class DatabaseHelper {
     return _convertToPhotos(results);
   }
 
-  Future<int> updatePhoto(Photo photo) async {
+  Future<int> updatePhoto(
+      int generatedId, String remotePath, int syncTimestamp) async {
     Database db = await instance.database;
-    return await db.update(table, _getRowForPhoto(photo),
-        where: '$columnGeneratedId = ?', whereArgs: [photo.generatedId]);
+    var values = new Map<String, dynamic>();
+    values[columnRemotePath] = remotePath;
+    values[columnSyncTimestamp] = syncTimestamp;
+    return await db.update(table, values,
+        where: '$columnGeneratedId = ?', whereArgs: [generatedId]);
   }
 
   Future<Photo> getPhotoByPath(String path) async {
     Database db = await instance.database;
     var rows =
-        await db.query(table, where: '$columnPath =?', whereArgs: [path]);
+        await db.query(table, where: '$columnRemotePath =?', whereArgs: [path]);
     if (rows.length > 0) {
       return _getPhotofromRow(rows[0]);
     } else {
@@ -147,11 +147,9 @@ class DatabaseHelper {
     row[columnLocalId] = photo.localId;
     row[columnUploadedFileId] =
         photo.uploadedFileId == null ? -1 : photo.uploadedFileId;
-    row[columnLocalPath] = photo.localPath;
-    row[columnRelativePath] = photo.relativePath;
-    row[columnThumbnailPath] = photo.thumbnailPath;
-    row[columnPath] = photo.path;
-    row[columnHash] = photo.hash;
+    row[columnTitle] = photo.title;
+    row[columnPathName] = photo.pathName;
+    row[columnRemotePath] = photo.remotePath;
     row[columnCreateTimestamp] = photo.createTimestamp;
     row[columnSyncTimestamp] = photo.syncTimestamp;
     return row;
@@ -162,11 +160,9 @@ class DatabaseHelper {
     photo.generatedId = row[columnGeneratedId];
     photo.localId = row[columnLocalId];
     photo.uploadedFileId = row[columnUploadedFileId];
-    photo.localPath = row[columnLocalPath];
-    photo.relativePath = row[columnRelativePath];
-    photo.thumbnailPath = row[columnThumbnailPath];
-    photo.path = row[columnPath];
-    photo.hash = row[columnHash];
+    photo.title = row[columnTitle];
+    photo.pathName = row[columnPathName];
+    photo.remotePath = row[columnRemotePath];
     photo.createTimestamp = int.parse(row[columnCreateTimestamp]);
     photo.syncTimestamp = row[columnSyncTimestamp] == null
         ? -1

+ 1 - 17
lib/main.dart

@@ -1,12 +1,9 @@
-import 'dart:async';
-
 import 'package:flutter/material.dart';
 import 'package:logger/logger.dart';
 import 'package:myapp/photo_loader.dart';
 import 'package:myapp/photo_provider.dart';
 import 'package:myapp/photo_sync_manager.dart';
 import 'package:myapp/ui/home_widget.dart';
-import 'package:photo_manager/photo_manager.dart';
 import 'package:provider/provider.dart';
 
 final provider = PhotoProvider();
@@ -15,20 +12,7 @@ final logger = Logger();
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
   runApp(MyApp());
-  await provider.refreshGalleryList();
-
-  if (provider.list.length > 0) {
-    provider.list[0].assetList.then((assets) {
-      init(assets);
-    });
-  } else {
-    init(List<AssetEntity>());
-  }
-}
-
-Future<void> init(List<AssetEntity> assets) async {
-  var photoSyncManager = PhotoSyncManager(assets);
-  photoSyncManager.init();
+  provider.refreshGalleryList().then((_) => PhotoSyncManager(provider.list));
 }
 
 class MyApp extends StatelessWidget {

+ 50 - 32
lib/models/photo.dart

@@ -1,63 +1,81 @@
-import 'dart:convert';
-import 'dart:io';
+import 'dart:typed_data';
 
-import 'package:crypto/crypto.dart';
-import 'package:logger/logger.dart';
-import 'package:path/path.dart';
+import 'package:flutter_image_compress/flutter_image_compress.dart';
 import 'package:photo_manager/photo_manager.dart';
+import 'package:path/path.dart';
 
 class Photo {
   int generatedId;
   int uploadedFileId;
   String localId;
-  String path;
-  String localPath;
-  String relativePath;
-  String thumbnailPath;
-  String hash;
+  String title;
+  String pathName;
+  String remotePath;
   int createTimestamp;
   int syncTimestamp;
 
   Photo();
-
   Photo.fromJson(Map<String, dynamic> json)
       : uploadedFileId = json["fileId"],
-        path = json["path"],
-        hash = json["hash"],
-        thumbnailPath = json["thumbnailPath"],
+        remotePath = json["path"],
         createTimestamp = json["createTimestamp"],
         syncTimestamp = json["syncTimestamp"];
 
-  static Future<Photo> fromAsset(AssetEntity asset) async {
+  static Future<Photo> fromAsset(
+      AssetPathEntity pathEntity, AssetEntity asset) async {
     Photo photo = Photo();
     photo.uploadedFileId = -1;
     photo.localId = asset.id;
-    var file = await asset.originFile;
-    photo.localPath = file.path;
-    if (Platform.isAndroid) {
-      photo.relativePath = dirname((asset.relativePath.endsWith("/")
-              ? asset.relativePath
-              : asset.relativePath + "/") +
-          asset.title);
-    } else {
-      photo.relativePath = dirname(photo.localPath);
-    }
-    photo.hash = getHash(file);
-    photo.thumbnailPath = photo.localPath;
+    photo.title = asset.title;
+    photo.pathName = pathEntity.name;
     photo.createTimestamp = asset.createDateTime.microsecondsSinceEpoch;
     return photo;
   }
 
-  AssetEntity getAsset() {
-    return AssetEntity(id: localId);
+  Future<Uint8List> getBytes() {
+    final asset = AssetEntity(id: localId);
+    if (extension(title) == ".HEIC") {
+      return asset.originBytes.then((bytes) =>
+          FlutterImageCompress.compressWithList(bytes)
+              .then((result) => Uint8List.fromList(result)));
+    } else {
+      return asset.originBytes;
+    }
   }
 
-  static String getHash(File file) {
-    return sha256.convert(file.readAsBytesSync()).toString();
+  Future<Uint8List> getOriginalBytes() {
+    return AssetEntity(id: localId).originBytes;
   }
 
   @override
   String toString() {
-    return 'Photo(generatedId: $generatedId, uploadedFileId: $uploadedFileId, localId: $localId, path: $path, localPath: $localPath, relativePath: $relativePath, thumbnailPath: $thumbnailPath, hash: $hash, createTimestamp: $createTimestamp, syncTimestamp: $syncTimestamp)';
+    return 'Photo(generatedId: $generatedId, uploadedFileId: $uploadedFileId, localId: $localId, title: $title, pathName: $pathName, remotePath: $remotePath, createTimestamp: $createTimestamp, syncTimestamp: $syncTimestamp)';
+  }
+
+  @override
+  bool operator ==(Object o) {
+    if (identical(this, o)) return true;
+
+    return o is Photo &&
+        o.generatedId == generatedId &&
+        o.uploadedFileId == uploadedFileId &&
+        o.localId == localId &&
+        o.title == title &&
+        o.pathName == pathName &&
+        o.remotePath == remotePath &&
+        o.createTimestamp == createTimestamp &&
+        o.syncTimestamp == syncTimestamp;
+  }
+
+  @override
+  int get hashCode {
+    return generatedId.hashCode ^
+        uploadedFileId.hashCode ^
+        localId.hashCode ^
+        title.hashCode ^
+        pathName.hashCode ^
+        remotePath.hashCode ^
+        createTimestamp.hashCode ^
+        syncTimestamp.hashCode;
   }
 }

+ 4 - 2
lib/photo_provider.dart

@@ -88,8 +88,10 @@ class PhotoProvider extends ChangeNotifier {
     if (!result) {
       print("Did not get permission");
     }
-    var galleryList =
-        await PhotoManager.getAssetPathList(type: RequestType.image);
+    final filterOptionGroup = FilterOptionGroup();
+    filterOptionGroup.setOption(AssetType.image, FilterOption(needTitle: true));
+    var galleryList = await PhotoManager.getAssetPathList(
+        type: RequestType.image, filterOption: filterOptionGroup);
 
     galleryList.sort((s1, s2) {
       return s2.assetCount.compareTo(s1.assetCount);

+ 45 - 40
lib/photo_sync_manager.dart

@@ -1,6 +1,5 @@
 import 'dart:async';
 import 'dart:io';
-import 'dart:math';
 
 import 'package:logger/logger.dart';
 import 'package:myapp/db/db_helper.dart';
@@ -16,51 +15,58 @@ import 'package:myapp/core/constants.dart' as Constants;
 class PhotoSyncManager {
   final _logger = Logger();
   final _dio = Dio();
-  final List<AssetEntity> _assets;
   static final _lastSyncTimestampKey = "last_sync_timestamp_0";
   static final _lastDBUpdateTimestampKey = "last_db_update_timestamp";
 
-  PhotoSyncManager(this._assets) {
-    _logger.i("PhotoSyncManager init");
-    _assets.sort((first, second) => first.createDateTime.microsecondsSinceEpoch
-        .compareTo(second.createDateTime.microsecondsSinceEpoch));
+  PhotoSyncManager(List<AssetPathEntity> pathEntities) {
+    init(pathEntities);
   }
 
-  Future<void> init() async {
-    _updateDatabase().then((_) {
-      _syncPhotos().then((_) {
-        _deletePhotos();
-      });
-    });
-  }
-
-  Future<bool> _updateDatabase() async {
+  Future<void> init(List<AssetPathEntity> pathEntities) async {
     final prefs = await SharedPreferences.getInstance();
     var lastDBUpdateTimestamp = prefs.getInt(_lastDBUpdateTimestampKey);
     if (lastDBUpdateTimestamp == null) {
       lastDBUpdateTimestamp = 0;
       await _initializeDirectories();
     }
-    var photos = List<Photo>();
-    var bufferLimit = 10;
-    final maxBufferLimit = 1000;
-    for (AssetEntity asset in _assets) {
-      if (asset.createDateTime.microsecondsSinceEpoch > lastDBUpdateTimestamp) {
-        try {
-          photos.add(await Photo.fromAsset(asset));
-        } catch (e) {
-          _logger.e(e);
-        }
-        if (photos.length > bufferLimit) {
-          await _insertPhotosToDB(
-              photos, prefs, asset.createDateTime.microsecondsSinceEpoch);
-          photos.clear();
-          bufferLimit = min(maxBufferLimit, bufferLimit * 2);
+
+    final photos = List<Photo>();
+    for (AssetPathEntity pathEntity in pathEntities) {
+      if (Platform.isIOS || pathEntity.name != "Recent") {
+        // "Recents" contain duplicate information on Android
+        var assetList = await pathEntity.assetList;
+        for (AssetEntity entity in assetList) {
+          if (entity.createDateTime.microsecondsSinceEpoch >
+              lastDBUpdateTimestamp) {
+            try {
+              photos.add(await Photo.fromAsset(pathEntity, entity));
+            } catch (e) {
+              _logger.e(e);
+            }
+          }
         }
       }
     }
+    photos.sort((first, second) =>
+        first.createTimestamp.compareTo(second.createTimestamp));
+
+    _updateDatabase(photos, prefs, lastDBUpdateTimestamp).then((_) {
+      _syncPhotos().then((_) {
+        _deletePhotos();
+      });
+    });
+  }
+
+  Future<bool> _updateDatabase(final List<Photo> photos,
+      SharedPreferences prefs, int lastDBUpdateTimestamp) async {
+    var photosToBeAdded = List<Photo>();
+    for (Photo photo in photos) {
+      if (photo.createTimestamp > lastDBUpdateTimestamp) {
+        photosToBeAdded.add(photo);
+      }
+    }
     return await _insertPhotosToDB(
-        photos, prefs, DateTime.now().microsecondsSinceEpoch);
+        photosToBeAdded, prefs, DateTime.now().microsecondsSinceEpoch);
   }
 
   _syncPhotos() async {
@@ -89,7 +95,8 @@ class PhotoSyncManager {
       if (uploadedPhoto == null) {
         return;
       }
-      await DatabaseHelper.instance.updatePhoto(uploadedPhoto);
+      await DatabaseHelper.instance.updatePhoto(photo.generatedId,
+          uploadedPhoto.remotePath, uploadedPhoto.syncTimestamp);
       prefs.setInt(_lastSyncTimestampKey, uploadedPhoto.syncTimestamp);
     }
   }
@@ -98,12 +105,12 @@ class PhotoSyncManager {
     var externalPath = (await getApplicationDocumentsDirectory()).path;
     var path = externalPath + "/photos/";
     for (Photo photo in diff) {
-      var localPath = path + basename(photo.path);
+      var localPath = path + basename(photo.remotePath);
       await _dio
-          .download(Constants.ENDPOINT + "/" + photo.path, localPath)
+          .download(Constants.ENDPOINT + "/" + photo.remotePath, localPath)
           .catchError(_onError);
-      photo.localPath = localPath;
-      photo.thumbnailPath = localPath;
+      // TODO: Save path
+      photo.pathName = localPath;
       await DatabaseHelper.instance.insertPhoto(photo);
       PhotoLoader.instance.reloadPhotos();
       await prefs.setInt(_lastSyncTimestampKey, photo.syncTimestamp);
@@ -128,8 +135,8 @@ class PhotoSyncManager {
 
   Future<Photo> _uploadFile(Photo localPhoto) async {
     var formData = FormData.fromMap({
-      "file": await MultipartFile.fromFile(localPhoto.localPath,
-          filename: basename(localPhoto.localPath)),
+      "file": MultipartFile.fromBytes(await localPhoto.getOriginalBytes()),
+      "filename": localPhoto.title,
       "user": Constants.USER,
     });
     return _dio
@@ -137,8 +144,6 @@ class PhotoSyncManager {
         .then((response) {
       _logger.i(response.toString());
       var photo = Photo.fromJson(response.data);
-      photo.localPath = localPhoto.localPath;
-      photo.localId = localPhoto.localId;
       return photo;
     }).catchError(_onError);
   }

+ 2 - 2
lib/ui/album_list_widget.dart

@@ -42,7 +42,7 @@ class _AlbumListWidgetState extends State<AlbumListWidget> {
   List<Album> _getAlbums(List<Photo> photos) {
     final albumMap = new LinkedHashMap<String, List<Photo>>();
     for (Photo photo in photos) {
-      final folder = path.basename(photo.relativePath);
+      final folder = path.basename(photo.pathName);
       if (!albumMap.containsKey(folder)) {
         albumMap[folder] = new List<Photo>();
       }
@@ -59,7 +59,7 @@ class _AlbumListWidgetState extends State<AlbumListWidget> {
     return GestureDetector(
       child: Column(
         children: <Widget>[
-          ImageWidget(album.photos[0], size: 160),
+          ImageWidget(album.photos[0], size: 140),
           Padding(padding: EdgeInsets.all(2)),
           Expanded(
             child: Text(

+ 6 - 23
lib/ui/detail_page.dart

@@ -4,13 +4,10 @@ import 'package:flutter/material.dart';
 import 'package:logger/logger.dart';
 import 'package:myapp/core/lru_map.dart';
 import 'package:myapp/models/photo.dart';
-import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_view/photo_view.dart';
-import 'package:share_extend/share_extend.dart';
 import 'extents_page_view.dart';
 import 'loading_widget.dart';
-import 'package:path/path.dart' as path;
-import 'package:flutter_image_compress/flutter_image_compress.dart';
+import 'package:myapp/utils/share_util.dart';
 
 class DetailPage extends StatefulWidget {
   final List<Photo> photos;
@@ -27,11 +24,6 @@ class _DetailPageState extends State<DetailPage> {
   int _selectedIndex = 0;
   final _cachedImages = LRUMap<int, ZoomableImage>(5);
 
-  @override
-  void initState() {
-    super.initState();
-  }
-
   @override
   Widget build(BuildContext context) {
     _selectedIndex = widget.selectedIndex;
@@ -77,8 +69,8 @@ class _DetailPageState extends State<DetailPage> {
       actions: <Widget>[
         IconButton(
           icon: Icon(Icons.share),
-          onPressed: () {
-            ShareExtend.share(widget.photos[_selectedIndex].localPath, "image");
+          onPressed: () async {
+            share(widget.photos[_selectedIndex]);
           },
         )
       ],
@@ -99,26 +91,17 @@ class ZoomableImage extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    Logger().i("Building " + photo.generatedId.toString());
+    Logger().i("Building " + photo.toString());
     if (ImageLruCache.getData(photo.generatedId) != null) {
       return _buildPhotoView(ImageLruCache.getData(photo.generatedId));
     }
-    var future;
-    if (path.extension(photo.localPath) == '.HEIC') {
-      Logger().i("Decoding HEIC");
-      future = photo.getAsset().originBytes.then((bytes) =>
-          FlutterImageCompress.compressWithList(bytes)
-              .then((result) => Uint8List.fromList(result)));
-    } else {
-      future = AssetEntity(id: photo.localId).originBytes;
-    }
     return FutureBuilder<Uint8List>(
-      future: future,
+      future: photo.getBytes(),
       builder: (_, snapshot) {
         if (snapshot.hasData) {
           return _buildPhotoView(snapshot.data);
         } else if (snapshot.hasError) {
-          return Text(snapshot.error);
+          return Text(snapshot.error.toString());
         } else {
           return loadWidget;
         }

+ 6 - 11
lib/ui/gallery_app_bar_widget.dart

@@ -1,12 +1,11 @@
-import 'dart:io';
-
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:myapp/db/db_helper.dart';
 import 'package:myapp/models/photo.dart';
 import 'package:myapp/photo_loader.dart';
+import 'package:photo_manager/photo_manager.dart';
 import 'package:provider/provider.dart';
-import 'package:share_extend/share_extend.dart';
+import 'package:myapp/utils/share_util.dart';
 
 class GalleryAppBarWidget extends StatefulWidget
     implements PreferredSizeWidget {
@@ -65,11 +64,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
   }
 
   void _shareSelectedPhotos(BuildContext context) {
-    var photoPaths = List<String>();
-    for (Photo photo in widget.selectedPhotos) {
-      photoPaths.add(photo.localPath);
-    }
-    ShareExtend.shareMultiple(photoPaths, "image");
+    shareMultiple(widget.selectedPhotos.toList());
   }
 
   void _showDeletePhotosSheet(BuildContext context) {
@@ -102,14 +97,14 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
 
   Future _deleteSelectedPhotos(
       BuildContext context, bool deleteEverywhere) async {
+    await PhotoManager.editor
+        .deleteWithIds(widget.selectedPhotos.map((p) => p.localId).toList());
+
     for (Photo photo in widget.selectedPhotos) {
       deleteEverywhere
           ? await DatabaseHelper.instance.markPhotoForDeletion(photo)
           : await DatabaseHelper.instance.deletePhoto(photo);
-      File file = File(photo.localPath);
-      await file.delete();
     }
-
     Navigator.of(context, rootNavigator: true).pop();
     photoLoader.reloadPhotos();
     if (widget.onPhotosDeleted != null) {

+ 7 - 14
lib/ui/image_widget.dart

@@ -1,4 +1,3 @@
-import 'dart:io';
 import 'dart:typed_data';
 
 import 'package:flutter/material.dart';
@@ -33,7 +32,7 @@ class _ImageWidgetState extends State<ImageWidget> {
     Widget image;
 
     if (cachedImageData != null) {
-      image = Image.memory(cachedImageData);
+      image = _buildImage(cachedImageData, size);
     } else {
       if (widget.photo.localId != null) {
         image = FutureBuilder<Uint8List>(
@@ -43,10 +42,7 @@ class _ImageWidgetState extends State<ImageWidget> {
             if (snapshot.hasData) {
               ImageLruCache.setData(
                   widget.photo.generatedId, size, snapshot.data);
-              Image image = Image.memory(snapshot.data,
-                  width: size.toDouble(),
-                  height: size.toDouble(),
-                  fit: BoxFit.cover);
+              Image image = _buildImage(snapshot.data, size);
               return image;
             } else {
               return loadingWidget;
@@ -54,19 +50,16 @@ class _ImageWidgetState extends State<ImageWidget> {
           },
         );
       } else {
-        image = Image.file(File(widget.photo.localPath),
-            width: size.toDouble(), height: size.toDouble(), fit: BoxFit.cover);
+        // TODO
+        return Text("Not Implemented");
       }
     }
 
     return image;
   }
 
-  @override
-  void didUpdateWidget(ImageWidget oldWidget) {
-    super.didUpdateWidget(oldWidget);
-    if (widget.photo.generatedId != oldWidget.photo.generatedId) {
-      setState(() {});
-    }
+  Image _buildImage(Uint8List data, int size) {
+    return Image.memory(data,
+        width: size.toDouble(), height: size.toDouble(), fit: BoxFit.cover);
   }
 }

+ 11 - 6
lib/utils/important_items_filter.dart

@@ -1,3 +1,5 @@
+import 'dart:io';
+
 import 'package:myapp/models/photo.dart';
 import 'package:myapp/utils/gallery_items_filter.dart';
 import 'package:path/path.dart';
@@ -5,11 +7,14 @@ import 'package:path/path.dart';
 class ImportantItemsFilter implements GalleryItemsFilter {
   @override
   bool shouldInclude(Photo photo) {
-    // TODO: Improve logic
-    final String folder = basename(photo.relativePath);
-    return folder == "Camera" ||
-        folder == "DCIM" ||
-        folder == "Download" ||
-        folder == "Screenshot";
+    if (Platform.isAndroid) {
+      final String folder = basename(photo.pathName);
+      return folder == "Camera" ||
+          folder == "DCIM" ||
+          folder == "Download" ||
+          folder == "Screenshot";
+    } else {
+      return true;
+    }
   }
 }

+ 21 - 0
lib/utils/share_util.dart

@@ -0,0 +1,21 @@
+import 'dart:typed_data';
+
+import 'package:esys_flutter_share/esys_flutter_share.dart';
+import 'package:myapp/models/photo.dart';
+import 'package:path/path.dart';
+
+Future<void> share(Photo photo) async {
+  final bytes = await photo.getBytes();
+  final ext = extension(photo.title);
+  final shareExt =
+      ext == ".HEIC" ? "jpeg" : ext.substring(1, ext.length).toLowerCase();
+  return Share.file(photo.title, photo.title, bytes, "image/" + shareExt);
+}
+
+Future<void> shareMultiple(List<Photo> photos) async {
+  final shareContent = Map<String, Uint8List>();
+  for (Photo photo in photos) {
+    shareContent[photo.title] = await photo.getBytes();
+  }
+  return Share.files("images", shareContent, "*/*");
+}

+ 7 - 7
pubspec.lock

@@ -78,6 +78,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.0.4"
+  esys_flutter_share:
+    dependency: "direct main"
+    description:
+      name: esys_flutter_share
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.2"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -233,13 +240,6 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.5"
-  share_extend:
-    dependency: "direct main"
-    description:
-      name: share_extend
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.3"
   shared_preferences:
     dependency: "direct main"
     description:

+ 1 - 1
pubspec.yaml

@@ -34,7 +34,7 @@ dependencies:
   crypto: ^2.1.3
   toast: ^0.1.5
   image: ^2.1.4
-  share_extend: "^1.1.2"
+  esys_flutter_share: ^1.0.2
   draggable_scrollbar: ^0.0.4
   photo_view: ^0.9.2
   flutter_image_compress: ^0.6.5+1