From e72cd812401f75bf5288023209f0c331ac1ca98c Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 4 Nov 2021 17:35:21 +0530 Subject: [PATCH] feat: Added ability to add custom attachment types --- .../lib/src/mip/stream_attachment_picker.dart | 102 +++++++++++++----- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/mip/stream_attachment_picker.dart b/packages/stream_chat_flutter/lib/src/mip/stream_attachment_picker.dart index e39efc1c..ea7d54fb 100644 --- a/packages/stream_chat_flutter/lib/src/mip/stream_attachment_picker.dart +++ b/packages/stream_chat_flutter/lib/src/mip/stream_attachment_picker.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; @@ -15,6 +14,11 @@ typedef FilePickerCallback = void Function( bool camera, }); +typedef CustomAttachmentIconBuilder = Widget Function( + BuildContext context, + bool active, +); + class StreamAttachmentPicker extends StatefulWidget { final bool isOpen; final double pickerSize; @@ -38,6 +42,8 @@ class StreamAttachmentPicker extends StatefulWidget { final List allowedAttachmentTypes; + final List customAttachmentTypes; + const StreamAttachmentPicker({ Key? key, required this.messageInputController, @@ -56,6 +62,7 @@ class StreamAttachmentPicker extends StatefulWidget { DefaultAttachmentTypes.file, DefaultAttachmentTypes.video, ], + this.customAttachmentTypes = const [], }) : super(key: key); @override @@ -70,9 +77,15 @@ class _StreamAttachmentPickerState extends State { var _streamChatTheme = StreamChatTheme.of(context); var messageInputController = widget.messageInputController; + final _attachmentContainsImage = + messageInputController.attachments.any((it) => it.type == 'image'); + final _attachmentContainsFile = messageInputController.attachments.any((it) => it.type == 'file'); + final _attachmentContainsVideo = + messageInputController.attachments.any((it) => it.type == 'video'); + final attachmentLimitCrossed = messageInputController.attachments.length >= widget.attachmentLimit; @@ -80,12 +93,13 @@ class _StreamAttachmentPickerState extends State { final streamChatThemeData = _streamChatTheme; switch (index) { case 0: - return messageInputController.attachments.isEmpty + return _filePickerIndex == 0 || _attachmentContainsImage ? streamChatThemeData.colorTheme.accentPrimary - : (!_attachmentContainsFile + : (_attachmentContainsImage ? streamChatThemeData.colorTheme.accentPrimary - : streamChatThemeData.colorTheme.textHighEmphasis - .withOpacity(0.2)); + : streamChatThemeData.colorTheme.textHighEmphasis.withOpacity( + messageInputController.attachments.isEmpty ? 0.5 : 0.2, + )); case 1: return _attachmentContainsFile ? streamChatThemeData.colorTheme.accentPrimary @@ -95,7 +109,8 @@ class _StreamAttachmentPickerState extends State { : streamChatThemeData.colorTheme.textHighEmphasis .withOpacity(0.2)); case 2: - return attachmentLimitCrossed + return widget.messageInputController.attachments.isNotEmpty && + (!_attachmentContainsImage || attachmentLimitCrossed) ? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2) : _attachmentContainsFile && messageInputController.attachments.isNotEmpty @@ -104,7 +119,8 @@ class _StreamAttachmentPickerState extends State { : streamChatThemeData.colorTheme.textHighEmphasis .withOpacity(0.5); case 3: - return attachmentLimitCrossed + return widget.messageInputController.attachments.isNotEmpty && + (!_attachmentContainsVideo || attachmentLimitCrossed) ? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2) : _attachmentContainsFile && messageInputController.attachments.isNotEmpty @@ -138,14 +154,15 @@ class _StreamAttachmentPickerState extends State { icon: StreamSvgIcon.pictures( color: _getIconColor(0), ), - onPressed: _attachmentContainsFile && - messageInputController.attachments.isNotEmpty - ? null - : () { - setState(() { - _filePickerIndex = 0; - }); - }, + onPressed: + messageInputController.attachments.isNotEmpty && + !_attachmentContainsImage + ? null + : () { + setState(() { + _filePickerIndex = 0; + }); + }, ), if (widget.allowedAttachmentTypes .contains(DefaultAttachmentTypes.file)) @@ -154,8 +171,9 @@ class _StreamAttachmentPickerState extends State { icon: StreamSvgIcon.files( color: _getIconColor(1), ), - onPressed: !_attachmentContainsFile && - messageInputController.attachments.isNotEmpty + onPressed: messageInputController + .attachments.isNotEmpty && + !_attachmentContainsFile ? null : () { widget @@ -169,9 +187,9 @@ class _StreamAttachmentPickerState extends State { color: _getIconColor(2), ), onPressed: attachmentLimitCrossed || - (_attachmentContainsFile && - messageInputController - .attachments.isNotEmpty) + (messageInputController + .attachments.isNotEmpty && + !_attachmentContainsVideo) ? null : () { widget.onFilePicked( @@ -188,9 +206,9 @@ class _StreamAttachmentPickerState extends State { color: _getIconColor(3), ), onPressed: attachmentLimitCrossed || - (_attachmentContainsFile && - messageInputController - .attachments.isNotEmpty) + (messageInputController + .attachments.isNotEmpty && + !_attachmentContainsVideo) ? null : () { widget.onFilePicked( @@ -199,6 +217,26 @@ class _StreamAttachmentPickerState extends State { ); }, ), + for (int i = 0; + i < widget.customAttachmentTypes.length; + i++) + IconButton( + onPressed: () { + if (messageInputController.attachments.isNotEmpty) { + if (!messageInputController.attachments.any((e) => + e.type == + widget.customAttachmentTypes[i].type)) { + return; + } + } + + setState(() { + _filePickerIndex = i + 1; + }); + }, + icon: widget.customAttachmentTypes[i] + .iconBuilder(context, _filePickerIndex == i + 1), + ), ], ), DecoratedBox( @@ -252,6 +290,7 @@ class _StreamAttachmentPickerState extends State { } }, allowedAttachmentTypes: widget.allowedAttachmentTypes, + customAttachmentTypes: widget.customAttachmentTypes, ), ), ), @@ -350,6 +389,7 @@ class _PickerWidget extends StatefulWidget { required this.onMediaSelected, required this.streamChatTheme, required this.allowedAttachmentTypes, + required this.customAttachmentTypes, }) : super(key: key); final int filePickerIndex; @@ -359,6 +399,7 @@ class _PickerWidget extends StatefulWidget { final void Function(AssetEntity) onMediaSelected; final StreamChatThemeData streamChatTheme; final List allowedAttachmentTypes; + final List customAttachmentTypes; @override _PickerWidgetState createState() => _PickerWidgetState(); @@ -376,7 +417,8 @@ class _PickerWidgetState extends State<_PickerWidget> { @override Widget build(BuildContext context) { if (widget.filePickerIndex != 0) { - return const Offstage(); + return widget.customAttachmentTypes[widget.filePickerIndex - 1] + .pickerBuilder(context); } return FutureBuilder( future: requestPermission, @@ -453,3 +495,15 @@ class _PickerWidgetState extends State<_PickerWidget> { ); } } + +class CustomAttachmentType { + String type; + CustomAttachmentIconBuilder iconBuilder; + WidgetBuilder pickerBuilder; + + CustomAttachmentType({ + required this.type, + required this.iconBuilder, + required this.pickerBuilder, + }); +}