From c79a27709880c58fbca9323e9fa961a1a78a4c3c Mon Sep 17 00:00:00 2001 From: Chiziaruhoma Ogbonda Date: Thu, 11 Sep 2025 09:07:48 +0100 Subject: [PATCH 1/3] feat: enable multiline text in starguide_chat_input.dart --- .../lib/chat/starguide_chat_input.dart | 73 ++++++++++++------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/starguide_flutter/lib/chat/starguide_chat_input.dart b/starguide_flutter/lib/chat/starguide_chat_input.dart index f894640..1255f95 100644 --- a/starguide_flutter/lib/chat/starguide_chat_input.dart +++ b/starguide_flutter/lib/chat/starguide_chat_input.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:starguide_flutter/config/constants.dart'; @@ -47,30 +48,43 @@ class _StarguideChatInputState extends State { Row( children: [ Expanded( - child: TextField( - focusNode: widget.focusNode, - autofocus: true, - enabled: widget.numChatRequests < kMaxChatRequests, - buildCounter: ( - context, { - required currentLength, - required isFocused, - required maxLength, - }) { - return const SizedBox(); + child: KeyboardListener( + focusNode: FocusNode(), + onKeyEvent: (KeyEvent event) { + if (event is KeyDownEvent) { + final isEnterPressed = + event.logicalKey == LogicalKeyboardKey.enter; + final isShiftPressed = + HardwareKeyboard.instance.isShiftPressed; + + if (isEnterPressed && !isShiftPressed && widget.enabled) { + _handleSubmit(); + } + } }, - maxLength: kMaxChatRequestLength, - maxLines: 1, - decoration: InputDecoration.collapsed( - hintText: hintText, - hintStyle: TextStyle(color: theme.disabledColor), + child: TextField( + focusNode: widget.focusNode, + autofocus: true, + enabled: widget.numChatRequests < kMaxChatRequests, + buildCounter: ( + context, { + required currentLength, + required isFocused, + required maxLength, + }) { + return const SizedBox(); + }, + maxLength: kMaxChatRequestLength, + maxLines: null, + minLines: 1, + keyboardType: TextInputType.multiline, + textInputAction: TextInputAction.newline, + decoration: InputDecoration.collapsed( + hintText: hintText, + hintStyle: TextStyle(color: theme.disabledColor), + ), + controller: widget.textController, ), - controller: widget.textController, - onSubmitted: (value) { - widget.onSend(widget.textController.text); - widget.textController.clear(); - widget.focusNode.requestFocus(); - }, ), ), if (widget.isGeneratingResponse) @@ -94,12 +108,7 @@ class _StarguideChatInputState extends State { padding: const EdgeInsets.all(4), minimumSize: const Size(48, 48), ), - onPressed: widget.enabled - ? () { - widget.onSend(widget.textController.text); - widget.textController.clear(); - } - : null, + onPressed: widget.enabled ? _handleSubmit : null, child: const Icon( LucideIcons.rocket300, size: 20, @@ -112,4 +121,12 @@ class _StarguideChatInputState extends State { ), ); } + + void _handleSubmit() { + if (widget.textController.text.trim().isNotEmpty) { + widget.onSend(widget.textController.text); + widget.textController.clear(); + widget.focusNode.requestFocus(); + } + } } From 4ab3139861c10d29a8a2addc71098e826b70909a Mon Sep 17 00:00:00 2001 From: Chiziaruhoma Ogbonda Date: Mon, 15 Sep 2025 10:00:36 +0100 Subject: [PATCH 2/3] chore: clean up fixes to chat input --- .../lib/chat/starguide_chat_input.dart | 79 ++++++++++++------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/starguide_flutter/lib/chat/starguide_chat_input.dart b/starguide_flutter/lib/chat/starguide_chat_input.dart index f894640..8c4e3a7 100644 --- a/starguide_flutter/lib/chat/starguide_chat_input.dart +++ b/starguide_flutter/lib/chat/starguide_chat_input.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:starguide_flutter/config/constants.dart'; @@ -40,37 +41,42 @@ class _StarguideChatInputState extends State { } return Container( - padding: - const EdgeInsets.only(left: 12.0, right: 8.0, top: 8.0, bottom: 8.0), + padding: const EdgeInsets.only( + left: 12.0, + right: 8.0, + top: 8.0, + bottom: 8.0, + ), child: Column( children: [ Row( children: [ Expanded( - child: TextField( + child: KeyboardListener( focusNode: widget.focusNode, - autofocus: true, - enabled: widget.numChatRequests < kMaxChatRequests, - buildCounter: ( - context, { - required currentLength, - required isFocused, - required maxLength, - }) { - return const SizedBox(); - }, - maxLength: kMaxChatRequestLength, - maxLines: 1, - decoration: InputDecoration.collapsed( - hintText: hintText, - hintStyle: TextStyle(color: theme.disabledColor), + onKeyEvent: (event) => _handleKeyboardEvents(event), + child: TextField( + autofocus: true, + enabled: widget.numChatRequests < kMaxChatRequests, + buildCounter: ( + context, { + required currentLength, + required isFocused, + required maxLength, + }) { + return const SizedBox(); + }, + maxLength: kMaxChatRequestLength, + maxLines: null, + minLines: 1, + keyboardType: TextInputType.multiline, + textInputAction: TextInputAction.newline, + decoration: InputDecoration.collapsed( + hintText: hintText, + hintStyle: TextStyle(color: theme.disabledColor), + ), + controller: widget.textController, ), - controller: widget.textController, - onSubmitted: (value) { - widget.onSend(widget.textController.text); - widget.textController.clear(); - widget.focusNode.requestFocus(); - }, ), ), if (widget.isGeneratingResponse) @@ -94,12 +100,7 @@ class _StarguideChatInputState extends State { padding: const EdgeInsets.all(4), minimumSize: const Size(48, 48), ), - onPressed: widget.enabled - ? () { - widget.onSend(widget.textController.text); - widget.textController.clear(); - } - : null, + onPressed: widget.enabled ? _handleSubmit : null, child: const Icon( LucideIcons.rocket300, size: 20, @@ -112,4 +113,22 @@ class _StarguideChatInputState extends State { ), ); } + + void _handleKeyboardEvents(KeyEvent event) { + if (event is KeyDownEvent) { + final isEnterPressed = event.logicalKey == LogicalKeyboardKey.enter; + final isShiftPressed = HardwareKeyboard.instance.isShiftPressed; + + if (isEnterPressed && !isShiftPressed && widget.enabled) { + _handleSubmit(); + } + } + } + + void _handleSubmit() { + if (widget.textController.text.trim().isNotEmpty) { + widget.onSend(widget.textController.text); + widget.textController.clear(); + } + } } From 8d5ae095a429e8a0b3f0e887082fb4c7cb42755f Mon Sep 17 00:00:00 2001 From: Chiziaruhoma Ogbonda Date: Mon, 15 Sep 2025 11:08:20 +0100 Subject: [PATCH 3/3] chore: bug fixes to multiline support --- .../lib/chat/starguide_chat_input.dart | 127 +++++++++--------- 1 file changed, 67 insertions(+), 60 deletions(-) diff --git a/starguide_flutter/lib/chat/starguide_chat_input.dart b/starguide_flutter/lib/chat/starguide_chat_input.dart index 8c4e3a7..4ae1e70 100644 --- a/starguide_flutter/lib/chat/starguide_chat_input.dart +++ b/starguide_flutter/lib/chat/starguide_chat_input.dart @@ -33,7 +33,7 @@ class _StarguideChatInputState extends State { final String hintText; if (widget.numChatRequests >= kMaxChatRequests) { - hintText = 'Clear the chat to start a new conversation.'; + hintText = 'Clear the chat to start aR new conversation.'; } else if (widget.numChatRequests == 0) { hintText = 'Ask me anything about Serverpod...'; } else { @@ -44,71 +44,78 @@ class _StarguideChatInputState extends State { padding: const EdgeInsets.only( left: 12.0, right: 8.0, - top: 8.0, - bottom: 8.0, ), - child: Column( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, children: [ - Row( - children: [ - Expanded( - child: KeyboardListener( - focusNode: widget.focusNode, - onKeyEvent: (event) => _handleKeyboardEvents(event), - child: TextField( - autofocus: true, - enabled: widget.numChatRequests < kMaxChatRequests, - buildCounter: ( - context, { - required currentLength, - required isFocused, - required maxLength, - }) { - return const SizedBox(); - }, - maxLength: kMaxChatRequestLength, - maxLines: null, - minLines: 1, - keyboardType: TextInputType.multiline, - textInputAction: TextInputAction.newline, - decoration: InputDecoration.collapsed( - hintText: hintText, - hintStyle: TextStyle(color: theme.disabledColor), - ), - controller: widget.textController, + Expanded( + child: KeyboardListener( + focusNode: widget.focusNode, + onKeyEvent: (event) => _handleKeyboardEvents(event), + child: Container( + constraints: BoxConstraints(maxHeight: 400.0), + child: TextField( + autofocus: true, + enabled: widget.numChatRequests < kMaxChatRequests, + buildCounter: ( + context, { + required currentLength, + required isFocused, + required maxLength, + }) { + return const SizedBox(); + }, + maxLength: kMaxChatRequestLength, + maxLines: null, + minLines: 1, + keyboardType: TextInputType.multiline, + textInputAction: TextInputAction.newline, + textAlignVertical: TextAlignVertical.top, + decoration: InputDecoration( + hintText: hintText, + hintStyle: TextStyle(color: theme.disabledColor), + border: InputBorder.none, ), + controller: widget.textController, ), ), - if (widget.isGeneratingResponse) - Container( - padding: const EdgeInsets.all(10), - width: 40, - height: 40, - child: CircularProgressIndicator( - color: theme.colorScheme.primary, - ), - ) - else - TextButton( - style: TextButton.styleFrom( - backgroundColor: widget.focusNode.hasFocus - ? theme.colorScheme.primary - : theme.dividerColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + if (widget.isGeneratingResponse) + Container( + padding: const EdgeInsets.all(10), + width: 40, + height: 40, + child: CircularProgressIndicator( + color: theme.colorScheme.primary, + ), + ) + else + TextButton( + style: TextButton.styleFrom( + backgroundColor: widget.focusNode.hasFocus + ? theme.colorScheme.primary + : theme.dividerColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + padding: const EdgeInsets.all(4), + minimumSize: const Size(48, 48), + ), + onPressed: widget.enabled ? _handleSubmit : null, + child: const Icon( + LucideIcons.rocket300, + size: 20, + color: Colors.white, ), - padding: const EdgeInsets.all(4), - minimumSize: const Size(48, 48), - ), - onPressed: widget.enabled ? _handleSubmit : null, - child: const Icon( - LucideIcons.rocket300, - size: 20, - color: Colors.white, ), - ), - ], - ), + ], + ), + ) ], ), ); @@ -131,4 +138,4 @@ class _StarguideChatInputState extends State { widget.textController.clear(); } } -} +} \ No newline at end of file