refactor(ui): added MultiPortal and customPortalOptions

This commit is contained in:
Salvatore Giordano
2021-09-21 16:44:03 +02:00
parent e0b23653b7
commit 152adfc8b0
3 changed files with 96 additions and 24 deletions
@@ -12,6 +12,10 @@
- Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls. - Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls.
✅ Added
- Added `MessageInput.customPortalOptions` property to add custom overlays to the `MessageInput`.
## 2.2.1 ## 2.2.1
⚠️ Deprecated ⚠️ Deprecated
@@ -6,7 +6,6 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
@@ -18,6 +17,7 @@ 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';
import 'package:stream_chat_flutter/src/mentions_overlay.dart'; import 'package:stream_chat_flutter/src/mentions_overlay.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/overlays.dart';
import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
@@ -192,12 +192,16 @@ class MessageInput extends StatefulWidget {
this.onAttachmentLimitExceed, this.onAttachmentLimitExceed,
this.attachmentButtonBuilder, this.attachmentButtonBuilder,
this.commandButtonBuilder, this.commandButtonBuilder,
this.customPortalOptions = const [],
}) : assert( }) : assert(
initialMessage == null || editMessage == null, initialMessage == null || editMessage == null,
"Can't provide both `initialMessage` and `editMessage`", "Can't provide both `initialMessage` and `editMessage`",
), ),
super(key: key); super(key: key);
/// List of portal options for showing overlays
final List<PortalOptions> customPortalOptions;
/// Message to edit /// Message to edit
final Message? editMessage; final Message? editMessage;
@@ -462,32 +466,33 @@ class MessageInputState extends State<MessageInput> {
); );
} }
return PortalEntry( return MultiPortal(
portal: _buildCommandsOverlayEntry(),
visible: _showCommandsOverlay,
childAnchor: Alignment.topCenter, childAnchor: Alignment.topCenter,
portalAnchor: Alignment.bottomCenter, portalAnchor: Alignment.bottomCenter,
child: PortalEntry( portalOptions: [
portal: _buildEmojiOverlay(), PortalOptions(
visible: _focusNode.hasFocus && visible: _showCommandsOverlay,
textEditingController.text.isNotEmpty && widget: _buildCommandsOverlayEntry(),
textEditingController.selection.baseOffset > 0 &&
textEditingController.text
.substring(
0,
textEditingController.selection.baseOffset,
)
.contains(':'),
childAnchor: Alignment.topCenter,
portalAnchor: Alignment.bottomCenter,
child: PortalEntry(
portal: _buildMentionsOverlayEntry(),
visible: _showMentionsOverlay,
childAnchor: Alignment.topCenter,
portalAnchor: Alignment.bottomCenter,
child: child,
), ),
), PortalOptions(
visible: _focusNode.hasFocus &&
textEditingController.text.isNotEmpty &&
textEditingController.selection.baseOffset > 0 &&
textEditingController.text
.substring(
0,
textEditingController.selection.baseOffset,
)
.contains(':'),
widget: _buildEmojiOverlay(),
),
PortalOptions(
visible: _showMentionsOverlay,
widget: _buildMentionsOverlayEntry(),
),
...widget.customPortalOptions,
],
child: child,
); );
} }
@@ -0,0 +1,63 @@
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_portal/flutter_portal.dart';
/// Class that contains the parameters for building a portal entry
class PortalOptions {
/// Constructs a new portal options object
/// [visible] - the visibility of the portal
/// [widget] - the widget to be displayed
PortalOptions({
required this.visible,
required this.widget,
});
/// the visibility of the portal
final bool visible;
/// the widget to be displayed
final Widget widget;
}
/// Widget that renders a single portal widget from a list of [portalOptions]
/// It shows the first one that is visible
class MultiPortal extends StatelessWidget {
/// Constructs a new MultiPortal widget
/// [portalOptions] - the list of portal options
/// [portalAnchor] - the anchor relative to the portal
/// [childAnchor] - the anchor relative to the child
/// [child] - the child widget
const MultiPortal({
Key? key,
required this.portalOptions,
required this.child,
required this.portalAnchor,
required this.childAnchor,
}) : super(key: key);
/// The list of portal options
final List<PortalOptions> portalOptions;
/// The child widget
final Widget child;
/// The anchor relative to the portal
final Alignment? portalAnchor;
/// The anchor relative to the child
final Alignment? childAnchor;
@override
Widget build(BuildContext context) {
final visibleOverlay =
portalOptions.firstWhereOrNull((element) => element.visible);
return PortalEntry(
childAnchor: childAnchor,
portalAnchor: portalAnchor,
visible: visibleOverlay != null,
portal: visibleOverlay?.widget,
child: child,
);
}
}