diff --git a/lib/views/chat/chat_input_field.dart b/lib/views/chat/chat_input_field.dart new file mode 100644 index 00000000..9f6ee817 --- /dev/null +++ b/lib/views/chat/chat_input_field.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +class ChatInputField extends StatefulWidget { + // Callback to be triggered when the send button is pressed + final VoidCallback onSendPressed; + + const ChatInputField({ + Key? key, + required this.onSendPressed, + }) : super(key: key); + + @override + _ChatInputFieldState createState() => _ChatInputFieldState(); +} + +class _ChatInputFieldState extends State { + // Controller for the TextField to manage its content + final TextEditingController _controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + // Using LayoutBuilder to provide the current constraints of the widget, + // ensuring it rebuilds when the window size changes + return LayoutBuilder( + builder: (context, constraints) { + // Calculate the width of the chat view based on the constraints provided + double chatViewWidth = constraints.maxWidth; + + // Determine the width of the input field based on the chat view width. + // If the chat view width is 1000 or more, the input width will be 900. + // Otherwise, the input width will be the chat view width minus 40. + double inputWidth = (chatViewWidth >= 1000) ? 900 : chatViewWidth - 40; + + return Row( + // Centering the children of the Row + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: Container( + width: inputWidth, + // Defining the minimum and maximum height for the TextField container + constraints: BoxConstraints( + minHeight: 50, + maxHeight: 400, + ), + // Styling the container with a border and rounded corners + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black, width: 0.5), + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(horizontal: 8), + // Using SingleChildScrollView to ensure the TextField can scroll + // when the content exceeds its maximum height + child: SingleChildScrollView( + reverse: true, + child: TextField( + controller: _controller, + // Allowing the TextField to expand vertically and accommodate multiple lines + maxLines: null, + decoration: InputDecoration( + hintText: 'Type a message...', + border: InputBorder.none, + ), + ), + ), + ), + ), + // Send button to trigger the provided onSendPressed callback + IconButton( + icon: Icon(Icons.send), + onPressed: widget.onSendPressed, + ), + ], + ); + }, + ); + } +} diff --git a/lib/views/new_task_button.dart b/lib/views/task/new_task_button.dart similarity index 100% rename from lib/views/new_task_button.dart rename to lib/views/task/new_task_button.dart diff --git a/lib/views/task_list_tile.dart b/lib/views/task/task_list_tile.dart similarity index 100% rename from lib/views/task_list_tile.dart rename to lib/views/task/task_list_tile.dart diff --git a/lib/views/task_view.dart b/lib/views/task/task_view.dart similarity index 90% rename from lib/views/task_view.dart rename to lib/views/task/task_view.dart index 9295e9a2..3d865d51 100644 --- a/lib/views/task_view.dart +++ b/lib/views/task/task_view.dart @@ -1,5 +1,5 @@ -import 'package:auto_gpt_flutter_client/views/new_task_button.dart'; -import 'package:auto_gpt_flutter_client/views/task_list_tile.dart'; +import 'package:auto_gpt_flutter_client/views/task/new_task_button.dart'; +import 'package:auto_gpt_flutter_client/views/task/task_list_tile.dart'; import 'package:flutter/material.dart'; import 'package:auto_gpt_flutter_client/viewmodels/task_viewmodel.dart'; @@ -34,11 +34,11 @@ class _TaskViewState extends State { padding: const EdgeInsets.all(8.0), child: Column( children: [ - Text( + const Text( 'Tasks', style: TextStyle(fontSize: 20, fontWeight: FontWeight.normal), ), - SizedBox(height: 8), + const SizedBox(height: 8), NewTaskButton( onPressed: () { // TODO: Implement add new task action diff --git a/test/chat_input_field_test.dart b/test/chat_input_field_test.dart new file mode 100644 index 00000000..2aa8eade --- /dev/null +++ b/test/chat_input_field_test.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:auto_gpt_flutter_client/views/chat/chat_input_field.dart'; + +void main() { + // Test if the ChatInputField widget renders correctly + testWidgets('ChatInputField renders correctly', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ChatInputField( + onSendPressed: () {}, + ), + ), + ), + ); + + // Find the TextField widget + expect(find.byType(TextField), findsOneWidget); + // Find the send IconButton + expect(find.byIcon(Icons.send), findsOneWidget); + }); + + // Test if the TextField inside ChatInputField can accept and display input + testWidgets('ChatInputField text field accepts input', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ChatInputField( + onSendPressed: () {}, + ), + ), + ), + ); + + // Type 'Hello' into the TextField + await tester.enterText(find.byType(TextField), 'Hello'); + // Rebuild the widget with the new text + await tester.pump(); + + // Expect to find 'Hello' in the TextField + expect(find.text('Hello'), findsOneWidget); + }); + + // Test if the send button triggers the provided onSendPressed callback + testWidgets('ChatInputField send button triggers callback', + (WidgetTester tester) async { + bool onPressedCalled = false; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ChatInputField( + onSendPressed: () { + onPressedCalled = true; + }, + ), + ), + ), + ); + + // Tap the send IconButton + await tester.tap(find.byIcon(Icons.send)); + // Rebuild the widget after the tap + await tester.pump(); + + // Check if the callback was called + expect(onPressedCalled, isTrue); + }); +} diff --git a/test/new_task_button_test.dart b/test/new_task_button_test.dart index f0d5078a..107a28eb 100644 --- a/test/new_task_button_test.dart +++ b/test/new_task_button_test.dart @@ -1,4 +1,4 @@ -import 'package:auto_gpt_flutter_client/views/new_task_button.dart'; +import 'package:auto_gpt_flutter_client/views/task/new_task_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/test/task_list_tile_test.dart b/test/task_list_tile_test.dart index 39706cda..e9b0c21d 100644 --- a/test/task_list_tile_test.dart +++ b/test/task_list_tile_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; -import 'package:auto_gpt_flutter_client/views/task_list_tile.dart'; +import 'package:auto_gpt_flutter_client/views/task/task_list_tile.dart'; import 'package:auto_gpt_flutter_client/models/task.dart'; void main() {