extract media list view

This commit is contained in:
Salvatore Giordano
2020-11-11 17:29:45 +01:00
parent 42e746fed7
commit f756e6e567
3 changed files with 189 additions and 100 deletions
+161
View File
@@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:media_gallery/media_gallery.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:transparent_image/transparent_image.dart';
class MediaListView extends StatefulWidget {
final List<String> selectedIds;
final void Function(Media media) onSelect;
const MediaListView({
Key key,
this.selectedIds = const [],
this.onSelect,
}) : super(key: key);
@override
_MediaListViewState createState() => _MediaListViewState();
}
class _MediaListViewState extends State<MediaListView> {
final _media = <Media>[];
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: _media.length,
controller: _scrollController,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
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(
placeholder: MemoryImage(kTransparentImage),
image: MediaThumbnailProvider(
media: media,
),
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(
maxRadius: 12.0,
backgroundColor: Colors.white,
child: Icon(
StreamIcons.check,
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',
),
),
],
),
onTap: () {
if (widget.onSelect != null) {
widget.onSelect(media);
}
},
),
);
},
);
}
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.offset >
_scrollController.position.maxScrollExtent - 100) {
_getMedia();
}
});
_getMedia();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _getMedia() async {
final List<MediaCollection> collections =
await MediaGallery.listMediaCollections(
mediaTypes: [
MediaType.video,
MediaType.image,
],
);
if (collections.isEmpty) {
return;
}
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);
});
print(
'_media.where((element) => element.mediaType == MediaType.video).length: ${_media.where((element) => element.mediaType == MediaType.video).length}');
}
}
+25 -100
View File
@@ -14,11 +14,11 @@ import 'package:media_gallery/media_gallery.dart';
import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:substring_highlight/substring_highlight.dart';
import 'package:transparent_image/transparent_image.dart';
import '../stream_chat_flutter.dart';
import 'stream_channel.dart';
@@ -183,8 +183,6 @@ class MessageInputState extends State<MessageInput> {
int _filePickerIndex = 0;
double _filePickerSize = 250.0;
Iterable<Media> _media = [];
/// The editing controller passed to the input TextField
TextEditingController textEditingController;
@@ -699,15 +697,16 @@ class MessageInputState extends State<MessageInput> {
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
if (_openFilePickerSection)
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
),
child: _buildPickerSection(),
),
child: _buildPickerSection(),
),
),
],
),
),
@@ -717,68 +716,15 @@ class MessageInputState extends State<MessageInput> {
Widget _buildPickerSection() {
switch (_filePickerIndex) {
case 0:
return GridView.builder(
itemCount: _media.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (context, position) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0),
child: InkWell(
child: Stack(
children: [
AspectRatio(
aspectRatio: 1.0,
child: FadeInImage(
placeholder: MemoryImage(kTransparentImage),
image: MediaThumbnailProvider(
media: _media.elementAt(position),
),
fit: BoxFit.fill,
),
),
Positioned.fill(
child: IgnorePointer(
child: AnimatedOpacity(
duration: Duration(milliseconds: 300),
opacity: _attachments.any((element) =>
element.id == _media.elementAt(position).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(
maxRadius: 12.0,
backgroundColor: Colors.white,
child: Icon(
StreamIcons.check,
color: Colors.black,
),
),
),
),
),
),
],
),
onTap: () {
if (!_attachments.any((element) =>
element.id == _media.elementAt(position).id)) {
_addAttachment(_media.elementAt(position));
} else {
_attachments.removeWhere((element) =>
element.id == _media.elementAt(position).id);
setState(() {});
}
},
),
);
return MediaListView(
selectedIds: _attachments.map((e) => e.id).toList(),
onSelect: (media) {
if (!_attachments.any((element) => element.id == media.id)) {
_addAttachment(media);
} else {
_attachments.removeWhere((element) => element.id == media.id);
setState(() {});
}
},
);
break;
@@ -845,32 +791,6 @@ class MessageInputState extends State<MessageInput> {
});
}
void _getAllMedia() async {
final List<MediaCollection> collections =
await MediaGallery.listMediaCollections(mediaTypes: [
MediaType.image,
MediaType.video,
]);
if (collections.isEmpty) {
return;
}
final page = await collections
.firstWhere(
(element) => element.name == 'All',
orElse: () => collections.first,
)
.getMedias(
take: 50,
);
print('page.items.length: ${page.items.length}');
setState(() {
_media = page.items;
});
}
CircleAvatar _buildGiphyIcon() {
if (kIsWeb) {
return CircleAvatar(
@@ -1275,6 +1195,13 @@ class MessageInputState extends State<MessageInput> {
),
),
onTap: () async {
_emojiOverlay?.remove();
_emojiOverlay = null;
_commandsOverlay?.remove();
_commandsOverlay = null;
_mentionsOverlay?.remove();
_mentionsOverlay = null;
if (_openFilePickerSection) {
setState(() {
_openFilePickerSection = false;
@@ -1282,14 +1209,12 @@ class MessageInputState extends State<MessageInput> {
});
} else {
final status = await Permission.storage.request();
print('status: ${status}');
if (status.isDenied || status.isPermanentlyDenied) {
final res = await openAppSettings();
if (!res) {
return;
}
}
_getAllMedia();
showAttachmentModal();
}
},
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.525 8.15C20.23 7.967 19.862 7.951 19.552 8.106L17 9.382V8C17 6.896 16.104 6 15 6H6C4.896 6 4 6.896 4 8V16C4 17.104 4.896 18 6 18H15C16.104 18 17 17.104 17 16V14.619L19.553 15.894C19.693 15.965 19.848 16 20 16C20.183 16 20.365 15.949 20.525 15.851C20.82 15.668 21 15.347 21 15V9C21 8.653 20.82 8.332 20.525 8.15Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 444 B