feat: Added ability to add custom attachment types

This commit is contained in:
Deven Joshi
2021-11-04 17:35:21 +05:30
parent e973f98308
commit e72cd81240
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart';
@@ -15,6 +14,11 @@ typedef FilePickerCallback = void Function(
bool camera, bool camera,
}); });
typedef CustomAttachmentIconBuilder = Widget Function(
BuildContext context,
bool active,
);
class StreamAttachmentPicker extends StatefulWidget { class StreamAttachmentPicker extends StatefulWidget {
final bool isOpen; final bool isOpen;
final double pickerSize; final double pickerSize;
@@ -38,6 +42,8 @@ class StreamAttachmentPicker extends StatefulWidget {
final List<DefaultAttachmentTypes> allowedAttachmentTypes; final List<DefaultAttachmentTypes> allowedAttachmentTypes;
final List<CustomAttachmentType> customAttachmentTypes;
const StreamAttachmentPicker({ const StreamAttachmentPicker({
Key? key, Key? key,
required this.messageInputController, required this.messageInputController,
@@ -56,6 +62,7 @@ class StreamAttachmentPicker extends StatefulWidget {
DefaultAttachmentTypes.file, DefaultAttachmentTypes.file,
DefaultAttachmentTypes.video, DefaultAttachmentTypes.video,
], ],
this.customAttachmentTypes = const [],
}) : super(key: key); }) : super(key: key);
@override @override
@@ -70,9 +77,15 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
var _streamChatTheme = StreamChatTheme.of(context); var _streamChatTheme = StreamChatTheme.of(context);
var messageInputController = widget.messageInputController; var messageInputController = widget.messageInputController;
final _attachmentContainsImage =
messageInputController.attachments.any((it) => it.type == 'image');
final _attachmentContainsFile = final _attachmentContainsFile =
messageInputController.attachments.any((it) => it.type == 'file'); messageInputController.attachments.any((it) => it.type == 'file');
final _attachmentContainsVideo =
messageInputController.attachments.any((it) => it.type == 'video');
final attachmentLimitCrossed = final attachmentLimitCrossed =
messageInputController.attachments.length >= widget.attachmentLimit; messageInputController.attachments.length >= widget.attachmentLimit;
@@ -80,12 +93,13 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
final streamChatThemeData = _streamChatTheme; final streamChatThemeData = _streamChatTheme;
switch (index) { switch (index) {
case 0: case 0:
return messageInputController.attachments.isEmpty return _filePickerIndex == 0 || _attachmentContainsImage
? streamChatThemeData.colorTheme.accentPrimary ? streamChatThemeData.colorTheme.accentPrimary
: (!_attachmentContainsFile : (_attachmentContainsImage
? streamChatThemeData.colorTheme.accentPrimary ? streamChatThemeData.colorTheme.accentPrimary
: streamChatThemeData.colorTheme.textHighEmphasis : streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(
.withOpacity(0.2)); messageInputController.attachments.isEmpty ? 0.5 : 0.2,
));
case 1: case 1:
return _attachmentContainsFile return _attachmentContainsFile
? streamChatThemeData.colorTheme.accentPrimary ? streamChatThemeData.colorTheme.accentPrimary
@@ -95,7 +109,8 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
: streamChatThemeData.colorTheme.textHighEmphasis : streamChatThemeData.colorTheme.textHighEmphasis
.withOpacity(0.2)); .withOpacity(0.2));
case 2: case 2:
return attachmentLimitCrossed return widget.messageInputController.attachments.isNotEmpty &&
(!_attachmentContainsImage || attachmentLimitCrossed)
? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2) ? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2)
: _attachmentContainsFile && : _attachmentContainsFile &&
messageInputController.attachments.isNotEmpty messageInputController.attachments.isNotEmpty
@@ -104,7 +119,8 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
: streamChatThemeData.colorTheme.textHighEmphasis : streamChatThemeData.colorTheme.textHighEmphasis
.withOpacity(0.5); .withOpacity(0.5);
case 3: case 3:
return attachmentLimitCrossed return widget.messageInputController.attachments.isNotEmpty &&
(!_attachmentContainsVideo || attachmentLimitCrossed)
? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2) ? streamChatThemeData.colorTheme.textHighEmphasis.withOpacity(0.2)
: _attachmentContainsFile && : _attachmentContainsFile &&
messageInputController.attachments.isNotEmpty messageInputController.attachments.isNotEmpty
@@ -138,14 +154,15 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
icon: StreamSvgIcon.pictures( icon: StreamSvgIcon.pictures(
color: _getIconColor(0), color: _getIconColor(0),
), ),
onPressed: _attachmentContainsFile && onPressed:
messageInputController.attachments.isNotEmpty messageInputController.attachments.isNotEmpty &&
? null !_attachmentContainsImage
: () { ? null
setState(() { : () {
_filePickerIndex = 0; setState(() {
}); _filePickerIndex = 0;
}, });
},
), ),
if (widget.allowedAttachmentTypes if (widget.allowedAttachmentTypes
.contains(DefaultAttachmentTypes.file)) .contains(DefaultAttachmentTypes.file))
@@ -154,8 +171,9 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
icon: StreamSvgIcon.files( icon: StreamSvgIcon.files(
color: _getIconColor(1), color: _getIconColor(1),
), ),
onPressed: !_attachmentContainsFile && onPressed: messageInputController
messageInputController.attachments.isNotEmpty .attachments.isNotEmpty &&
!_attachmentContainsFile
? null ? null
: () { : () {
widget widget
@@ -169,9 +187,9 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
color: _getIconColor(2), color: _getIconColor(2),
), ),
onPressed: attachmentLimitCrossed || onPressed: attachmentLimitCrossed ||
(_attachmentContainsFile && (messageInputController
messageInputController .attachments.isNotEmpty &&
.attachments.isNotEmpty) !_attachmentContainsVideo)
? null ? null
: () { : () {
widget.onFilePicked( widget.onFilePicked(
@@ -188,9 +206,9 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
color: _getIconColor(3), color: _getIconColor(3),
), ),
onPressed: attachmentLimitCrossed || onPressed: attachmentLimitCrossed ||
(_attachmentContainsFile && (messageInputController
messageInputController .attachments.isNotEmpty &&
.attachments.isNotEmpty) !_attachmentContainsVideo)
? null ? null
: () { : () {
widget.onFilePicked( widget.onFilePicked(
@@ -199,6 +217,26 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
); );
}, },
), ),
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( DecoratedBox(
@@ -252,6 +290,7 @@ class _StreamAttachmentPickerState extends State<StreamAttachmentPicker> {
} }
}, },
allowedAttachmentTypes: widget.allowedAttachmentTypes, allowedAttachmentTypes: widget.allowedAttachmentTypes,
customAttachmentTypes: widget.customAttachmentTypes,
), ),
), ),
), ),
@@ -350,6 +389,7 @@ class _PickerWidget extends StatefulWidget {
required this.onMediaSelected, required this.onMediaSelected,
required this.streamChatTheme, required this.streamChatTheme,
required this.allowedAttachmentTypes, required this.allowedAttachmentTypes,
required this.customAttachmentTypes,
}) : super(key: key); }) : super(key: key);
final int filePickerIndex; final int filePickerIndex;
@@ -359,6 +399,7 @@ class _PickerWidget extends StatefulWidget {
final void Function(AssetEntity) onMediaSelected; final void Function(AssetEntity) onMediaSelected;
final StreamChatThemeData streamChatTheme; final StreamChatThemeData streamChatTheme;
final List<DefaultAttachmentTypes> allowedAttachmentTypes; final List<DefaultAttachmentTypes> allowedAttachmentTypes;
final List<CustomAttachmentType> customAttachmentTypes;
@override @override
_PickerWidgetState createState() => _PickerWidgetState(); _PickerWidgetState createState() => _PickerWidgetState();
@@ -376,7 +417,8 @@ class _PickerWidgetState extends State<_PickerWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.filePickerIndex != 0) { if (widget.filePickerIndex != 0) {
return const Offstage(); return widget.customAttachmentTypes[widget.filePickerIndex - 1]
.pickerBuilder(context);
} }
return FutureBuilder<bool>( return FutureBuilder<bool>(
future: requestPermission, 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,
});
}