using System.Collections.Generic; using ChatComponents; using uiwidgets; using Unity.UIWidgets.animation; using Unity.UIWidgets.foundation; using Unity.UIWidgets.material; using Unity.UIWidgets.painting; using Unity.UIWidgets.service; using Unity.UIWidgets.services; using Unity.UIWidgets.ui; using Unity.UIWidgets.widgets; using System; namespace UIWidgetsSample { public class NewLineIntent : Intent { public NewLineIntent(LocalKey key ) : base(key) { } } public class SendMessageIntent : Intent { public SendMessageIntent(LocalKey key ) : base(key) { } } /// A class that represents bottom bar widget with a text field, attachment and /// send buttons inside. Hides send button when text field is empty. public class Input : StatefulWidget { /// Whether attachment is uploading. Will replace attachment button with a /// [CircularProgressIndicator]. Since we don't have libraries for /// managing media in dependencies we have no way of knowing if /// something is uploading so you need to set this manually. public readonly bool? isAttachmentUploading; /// See [AttachmentButton.onPressed] public readonly OnAttachmentPressed onAttachmentPressed; /// Will be called on [SendButton] tap. Has [types.PartialText] which can /// be transformed to [types.TextMessage] and added to the messages list. public readonly OnSendPressed onSendPressed; /// Will be called whenever the text inside [TextField] changes public readonly OnTextChanged onTextChanged; /// Creates [Input] widget public Input( OnSendPressed onSendPressed, Key key = null, bool? isAttachmentUploading = null, OnAttachmentPressed onAttachmentPressed = null, OnTextChanged onTextChanged = null ) : base(key) { this.onAttachmentPressed = onAttachmentPressed; this.isAttachmentUploading = isAttachmentUploading; this.onSendPressed = onSendPressed; this.onTextChanged = onTextChanged; } public override State createState() { return new _InputState(); } } /// [Input] widget state internal class _InputState : State { public readonly FocusNode _inputFocusNode = new FocusNode(); public readonly TextEditingController _textController = new TextEditingController(); private bool _sendButtonVisible; public override void initState() { base.initState(); _textController.addListener(_handleTextControllerChange); } public override void dispose() { _inputFocusNode.dispose(); _textController.dispose(); base.dispose(); } private void _handleSendPressed() { var _partialText = new PartialText(_textController.text.TrimEnd()); widget.onSendPressed(_partialText); _textController.clear(); } private void _handleTextControllerChange() { setState(() => { _sendButtonVisible = _textController.text != ""; }); } private Widget _leftWidget() { if (widget.isAttachmentUploading == true) return new SizedBox( height: 14, width: 24, child: new CircularProgressIndicator( backgroundColor: Colors.transparent, strokeWidth: 2, valueColor: new AlwaysStoppedAnimation( InheritedChatTheme.of(context).theme.inputTextColor ) ) ); else { return new AttachmentButton(onPressed: ()=> { widget.onAttachmentPressed(); }); } } public CallbackAction sendPress(){ return new CallbackAction( intentKey: DoNothingAction.key, onInvoke: (FocusNode node, Intent intent) => _handleSendPressed() ); } public CallbackAction sendSelectionPress() { return new CallbackAction( intentKey: DoNothingAction.key, onInvoke: (FocusNode node, Intent intent) => { var _newValue = $"{_textController.text}\r\n"; _textController.value = new TextEditingValue( _newValue, TextSelection.fromPosition( new TextPosition(_newValue.Length) ) ); } ); } public override Widget build(BuildContext context) { var _query = MediaQuery.of(context); var results = new List(); if (widget.onAttachmentPressed != null) results.Add(_leftWidget()); var hintStyle = InheritedChatTheme.of(context)? .theme .inputTextStyle .copyWith( color: InheritedChatTheme.of(context)? .theme .inputTextColor .withOpacity(0.5f) ); var hintText = InheritedL10n.of(context)?.l10n?.inputPlaceholder; var decoration = InputDecoration.collapsed( hintStyle: hintStyle, hintText: hintText ); var style = InheritedChatTheme.of(context) .theme .inputTextStyle .copyWith( color: InheritedChatTheme.of(context) .theme .inputTextColor ); var item = new Expanded( child: new TextField( controller: _textController, decoration: decoration , focusNode: _inputFocusNode, keyboardType: TextInputType.multiline, maxLines: 5, minLines: 1, onChanged: (_str) => { widget.onTextChanged(_str); }, style: style, textCapitalization: TextCapitalization.sentences ) ); results.Add(item); results.Add(new Visibility( visible: _sendButtonVisible, child: new SendButton( onPressed: _handleSendPressed ) )); var shortCutsKey = new Dictionary { {new LogicalKeySet(LogicalKeyboardKey.enter), new SendMessageIntent(DoNothingAction.key)}, { new LogicalKeySet(LogicalKeyboardKey.enter, LogicalKeyboardKey.alt), new NewLineIntent(DoNothingAction.key) }, { new LogicalKeySet(LogicalKeyboardKey.enter, LogicalKeyboardKey.shift), new NewLineIntent(DoNothingAction.key) } }; var actionKeys = new Dictionary { {new ObjectKey(typeof(SendMessageIntent)), sendPress}, {new ObjectKey(typeof(NewLineIntent)), sendSelectionPress} }; var test = _query; return new GestureDetector( onTap: () => _inputFocusNode.requestFocus(), child: new Shortcuts( shortcuts: shortCutsKey , child: new Actions( actions: actionKeys, child: new Focus( autofocus: true, child: new Material( borderRadius: InheritedChatTheme.of(context).theme.inputBorderRadius, color: InheritedChatTheme.of(context).theme.inputBackgroundColor, child: new Container( /*padding: EdgeInsets.fromLTRB( left:24 + _query.padding.left, top:0, right:24 + _query.padding.right, bottom:0//10 + _query.viewInsets.bottom + _query.padding.bottom ),*/ child: new Row( children: results ) ) ) ) ) ) ); } } }