extract media list view
This commit is contained in:
@@ -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
@@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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 |
Reference in New Issue
Block a user