refactor(ui): added MultiPortal and customPortalOptions
This commit is contained in:
@@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user