use photo manager
This commit is contained in:
@@ -108,8 +108,6 @@ PODS:
|
||||
- GoogleUtilities/Logger
|
||||
- image_picker (0.0.1):
|
||||
- Flutter
|
||||
- media_gallery (0.0.1):
|
||||
- Flutter
|
||||
- nanopb (1.30906.0):
|
||||
- nanopb/decode (= 1.30906.0)
|
||||
- nanopb/encode (= 1.30906.0)
|
||||
@@ -119,6 +117,8 @@ PODS:
|
||||
- Flutter
|
||||
- "permission_handler (5.0.1+1)":
|
||||
- Flutter
|
||||
- photo_manager (0.0.1):
|
||||
- Flutter
|
||||
- PromisesObjC (1.2.11)
|
||||
- Protobuf (3.13.0)
|
||||
- SDWebImage (5.10.0):
|
||||
@@ -171,9 +171,9 @@ DEPENDENCIES:
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- image_picker (from `.symlinks/plugins/image_picker/ios`)
|
||||
- media_gallery (from `.symlinks/plugins/media_gallery/ios`)
|
||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||
@@ -226,12 +226,12 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
image_picker:
|
||||
:path: ".symlinks/plugins/image_picker/ios"
|
||||
media_gallery:
|
||||
:path: ".symlinks/plugins/media_gallery/ios"
|
||||
path_provider:
|
||||
:path: ".symlinks/plugins/path_provider/ios"
|
||||
permission_handler:
|
||||
:path: ".symlinks/plugins/permission_handler/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
shared_preferences:
|
||||
:path: ".symlinks/plugins/shared_preferences/ios"
|
||||
sqflite:
|
||||
@@ -269,10 +269,10 @@ SPEC CHECKSUMS:
|
||||
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
|
||||
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
|
||||
image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09
|
||||
media_gallery: 834a04455a476b975c280aa1f24ed2853dfe36de
|
||||
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6
|
||||
photo_manager: f7c619c2cc8c2adb8d85c63363babac477de9c67
|
||||
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
|
||||
Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
|
||||
SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee
|
||||
|
||||
@@ -327,9 +327,9 @@
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/media_gallery/media_gallery.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework",
|
||||
@@ -359,9 +359,9 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_gallery.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: example
|
||||
description: A new Flutter project.
|
||||
version: 1.0.77+79
|
||||
version: 1.0.78+80
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
|
||||
+140
-106
@@ -1,11 +1,14 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:media_gallery/media_gallery.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
class MediaListView extends StatefulWidget {
|
||||
final List<String> selectedIds;
|
||||
final void Function(Media media) onSelect;
|
||||
final void Function(AssetEntity media) onSelect;
|
||||
|
||||
const MediaListView({
|
||||
Key key,
|
||||
@@ -17,88 +20,94 @@ class MediaListView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MediaListViewState extends State<MediaListView> {
|
||||
final _media = <Media>[];
|
||||
final _media = <AssetEntity>[];
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
int _currentPage = 0;
|
||||
bool _endPagination = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
itemCount: _media.length,
|
||||
controller: _scrollController,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
),
|
||||
cacheExtent: 1000,
|
||||
itemBuilder: (
|
||||
context,
|
||||
position,
|
||||
) {
|
||||
final media = _media.elementAt(position);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0),
|
||||
child: InkWell(
|
||||
child: Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: FadeInImage(
|
||||
fadeInDuration: Duration(milliseconds: 300),
|
||||
placeholder: AssetImage(
|
||||
'images/placeholder.png',
|
||||
package: 'stream_chat_flutter',
|
||||
return LazyLoadScrollView(
|
||||
onEndOfPage: () {
|
||||
_getMedia();
|
||||
},
|
||||
child: GridView.builder(
|
||||
itemCount: _media.length,
|
||||
controller: _scrollController,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
),
|
||||
cacheExtent: 1000,
|
||||
itemBuilder: (
|
||||
context,
|
||||
position,
|
||||
) {
|
||||
final media = _media.elementAt(position);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0),
|
||||
child: InkWell(
|
||||
child: Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: FadeInImage(
|
||||
fadeInDuration: Duration(milliseconds: 300),
|
||||
placeholder: AssetImage(
|
||||
'images/placeholder.png',
|
||||
package: 'stream_chat_flutter',
|
||||
),
|
||||
image: MediaThumbnailProvider(
|
||||
media: media,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
image: MediaThumbnailProvider(
|
||||
media: media,
|
||||
highQuality: true,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: AnimatedOpacity(
|
||||
duration: Duration(milliseconds: 300),
|
||||
opacity: widget.selectedIds.any((id) => id == media.id)
|
||||
? 1.0
|
||||
: 0.0,
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
alignment: Alignment.topRight,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 12,
|
||||
backgroundColor: Colors.white,
|
||||
child: StreamSvgIcon.check(
|
||||
size: 24,
|
||||
color: Colors.black,
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: AnimatedOpacity(
|
||||
duration: Duration(milliseconds: 300),
|
||||
opacity: widget.selectedIds.any((id) => id == media.id)
|
||||
? 1.0
|
||||
: 0.0,
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
alignment: Alignment.topRight,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 12,
|
||||
backgroundColor: Colors.white,
|
||||
child: StreamSvgIcon.check(
|
||||
size: 24,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (media.mediaType == MediaType.video)
|
||||
Positioned(
|
||||
left: 8,
|
||||
bottom: 10,
|
||||
child: SvgPicture.asset(
|
||||
'svgs/video_call_icon.svg',
|
||||
package: 'stream_chat_flutter',
|
||||
if (media.type == AssetType.video)
|
||||
Positioned(
|
||||
left: 8,
|
||||
bottom: 10,
|
||||
child: SvgPicture.asset(
|
||||
'svgs/video_call_icon.svg',
|
||||
package: 'stream_chat_flutter',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.onSelect != null) {
|
||||
widget.onSelect(media);
|
||||
}
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.onSelect != null) {
|
||||
widget.onSelect(media);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,42 +118,67 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
}
|
||||
|
||||
void _getMedia() async {
|
||||
final List<MediaCollection> collections =
|
||||
await MediaGallery.listMediaCollections(
|
||||
mediaTypes: [
|
||||
MediaType.video,
|
||||
MediaType.image,
|
||||
],
|
||||
);
|
||||
final assetList = await PhotoManager.getAssetPathList(
|
||||
hasAll: true,
|
||||
).then((value) => value.singleWhere((element) => element.isAll));
|
||||
|
||||
if (collections.isEmpty) {
|
||||
return;
|
||||
final media = await assetList.getAssetListPaged(_currentPage, 50);
|
||||
|
||||
if (media.isEmpty) {
|
||||
setState(() {
|
||||
_endPagination = true;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_media.addAll(media);
|
||||
});
|
||||
}
|
||||
final collection = collections.firstWhere(
|
||||
(element) => element.isAllCollection,
|
||||
orElse: () => collections.first,
|
||||
);
|
||||
|
||||
final videoPage = await collection.getMedias(
|
||||
mediaType: MediaType.video,
|
||||
take: 500,
|
||||
);
|
||||
final imagePage = await collection.getMedias(
|
||||
mediaType: MediaType.image,
|
||||
take: 500,
|
||||
);
|
||||
|
||||
final allItems = [
|
||||
...videoPage.items,
|
||||
...imagePage.items,
|
||||
]..sort((
|
||||
a,
|
||||
b,
|
||||
) =>
|
||||
b.creationDate.compareTo(a.creationDate));
|
||||
|
||||
setState(() {
|
||||
_media.addAll(allItems);
|
||||
});
|
||||
++_currentPage;
|
||||
}
|
||||
}
|
||||
|
||||
class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
|
||||
const MediaThumbnailProvider({
|
||||
@required this.media,
|
||||
}) : assert(media != null);
|
||||
|
||||
final AssetEntity media;
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(key, decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode),
|
||||
scale: 1.0,
|
||||
informationCollector: () sync* {
|
||||
yield ErrorDescription('Id: ${media?.id}');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(
|
||||
MediaThumbnailProvider key, DecoderCallback decode) async {
|
||||
assert(key == this);
|
||||
final bytes = await media.thumbData;
|
||||
if (bytes.isEmpty) return null;
|
||||
|
||||
return await decode(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MediaThumbnailProvider> obtainKey(ImageConfiguration configuration) {
|
||||
return SynchronousFuture<MediaThumbnailProvider>(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (other.runtimeType != runtimeType) return false;
|
||||
final MediaThumbnailProvider typedOther = other;
|
||||
return media?.id == typedOther.media?.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => media?.id?.hashCode ?? 0;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType("${media?.id}")';
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:http_parser/http_parser.dart' as httpParser;
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:media_gallery/media_gallery.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/compress_video_service.dart';
|
||||
import 'package:stream_chat_flutter/src/media_list_view.dart';
|
||||
@@ -845,7 +845,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
}
|
||||
}
|
||||
|
||||
void _addAttachment(Media medium) async {
|
||||
void _addAttachment(AssetEntity medium) async {
|
||||
final attachment = _SendingAttachment(
|
||||
id: medium.id,
|
||||
);
|
||||
@@ -853,16 +853,16 @@ class MessageInputState extends State<MessageInput> {
|
||||
setState(() {
|
||||
_attachments.add(attachment);
|
||||
});
|
||||
final mediaFile = await medium.getFile();
|
||||
final mediaFile = await medium.file;
|
||||
|
||||
var file = PlatformFile(
|
||||
path: mediaFile.path,
|
||||
size: (await mediaFile.length()) ~/ 1024,
|
||||
size: ((await mediaFile.length()) / 1024).ceil(),
|
||||
bytes: mediaFile.readAsBytesSync(),
|
||||
);
|
||||
|
||||
if (file.size > _kMaxAttachmentSize) {
|
||||
if (medium.mediaType == MediaType.video) {
|
||||
if (medium.type == AssetType.video) {
|
||||
final mediaInfo = await CompressVideoService.compressVideo(file.path);
|
||||
|
||||
if (mediaInfo.filesize / (1024 * 1024) > _kMaxAttachmentSize) {
|
||||
@@ -902,18 +902,18 @@ class MessageInputState extends State<MessageInput> {
|
||||
..file = file
|
||||
..attachment = Attachment(
|
||||
localUri: file.path != null ? Uri.parse(file.path) : null,
|
||||
type: medium.mediaType == MediaType.image ? 'image' : 'video',
|
||||
type: medium.type == AssetType.image ? 'image' : 'video',
|
||||
);
|
||||
});
|
||||
|
||||
final url = await _uploadAttachment(
|
||||
file,
|
||||
medium.mediaType == MediaType.image
|
||||
medium.type == AssetType.image
|
||||
? DefaultAttachmentTypes.image
|
||||
: DefaultAttachmentTypes.video,
|
||||
channel);
|
||||
|
||||
final fileType = medium.mediaType == MediaType.image
|
||||
final fileType = medium.type == AssetType.image
|
||||
? DefaultAttachmentTypes.image
|
||||
: DefaultAttachmentTypes.video;
|
||||
|
||||
@@ -936,6 +936,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
setState(() {
|
||||
_attachments.remove(attachment);
|
||||
});
|
||||
print(e);
|
||||
print(s);
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
|
||||
+1
-2
@@ -38,8 +38,7 @@ dependencies:
|
||||
flutter_slidable: ^0.5.4
|
||||
carousel_slider: ^2.2.1
|
||||
clipboard: ^0.1.2+8
|
||||
media_gallery:
|
||||
git: https://github.com/imtoori/media_gallery.git
|
||||
photo_manager: ^0.5.8
|
||||
permission_handler: ^5.0.1+1
|
||||
transparent_image: ^1.0.0
|
||||
ezanimation: ^0.4.1
|
||||
|
||||
Reference in New Issue
Block a user