Implement TaskListTile with tests

This commit introduces the TaskListTile, a custom widget designed to display individual tasks in the TaskView. The tile offers a user-friendly interface with interactive features, such as selection and deletion.

Key Features:
- Responsive design that adapts its width based on the TaskView's constraints.
- Interactive tile that changes its background color upon selection.
- A delete icon that appears only when the tile is selected.
- Comprehensive widget tests to ensure the TaskListTile behaves as expected.

By splitting this into its own widget, the codebase remains modular, making it easier to maintain and update in the future.
This commit is contained in:
hunteraraujo
2023-08-23 08:25:19 -07:00
parent b71b7d1f26
commit 3a0db45b3b
3 changed files with 152 additions and 30 deletions

View File

@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:auto_gpt_flutter_client/models/task.dart';
class TaskListTile extends StatefulWidget {
final Task task;
final VoidCallback onTap;
final VoidCallback onDelete;
const TaskListTile({
Key? key,
required this.task,
required this.onTap,
required this.onDelete,
}) : super(key: key);
@override
_TaskListTileState createState() => _TaskListTileState();
}
class _TaskListTileState extends State<TaskListTile> {
bool _isSelected = false;
@override
Widget build(BuildContext context) {
// Determine the width of the TaskView
double taskViewWidth = MediaQuery.of(context).size.width;
double tileWidth = taskViewWidth - 20;
if (tileWidth > 260) {
tileWidth = 260;
}
return GestureDetector(
onTap: () {
setState(() {
_isSelected = !_isSelected;
});
widget.onTap();
},
child: Material(
// Use a transparent color to avoid any unnecessary color overlay
color: Colors.transparent,
child: Padding(
// Provide a horizontal padding to ensure the tile does not touch the edges
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
// Width and height specifications for the tile
width: tileWidth,
height: 50,
decoration: BoxDecoration(
// Use conditional operator to determine background color based on selection
color: _isSelected ? Colors.grey[300] : Colors.white,
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
children: [
// Space from the left edge of the tile
const SizedBox(width: 8),
// Message bubble icon indicating a task
const Icon(Icons.messenger_outline, color: Colors.black),
const SizedBox(width: 8),
// Task title
Expanded(
child: Text(
widget.task.title,
style: const TextStyle(color: Colors.black),
),
),
// If the task is selected, show a delete icon
if (_isSelected)
IconButton(
icon: const Icon(Icons.close, color: Colors.black),
onPressed: widget.onDelete,
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,71 @@
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/models/task.dart';
void main() {
final Task testTask = Task(id: 1, title: "Sample Task");
testWidgets('TaskListTile displays the task title',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: TaskListTile(task: testTask, onTap: () {}, onDelete: () {})));
expect(find.text('Sample Task'), findsOneWidget);
});
testWidgets('TaskListTile toggles isSelected state on tap',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: TaskListTile(task: testTask, onTap: () {}, onDelete: () {})));
// Initially, the delete icon should not be present
expect(find.byIcon(Icons.close), findsNothing);
// Tap the tile
await tester.tap(find.text('Sample Task'));
await tester.pump();
// The delete icon should appear
expect(find.byIcon(Icons.close), findsOneWidget);
});
testWidgets('TaskListTile triggers onDelete when delete icon is tapped',
(WidgetTester tester) async {
bool wasDeleteCalled = false;
await tester.pumpWidget(MaterialApp(
home: TaskListTile(
task: testTask,
onTap: () {},
onDelete: () {
wasDeleteCalled = true;
})));
// Tap the tile to make the delete icon appear
await tester.tap(find.text('Sample Task'));
await tester.pump();
// Tap the delete icon
await tester.tap(find.byIcon(Icons.close));
await tester.pump();
expect(wasDeleteCalled, true);
});
testWidgets('TaskListTile triggers onTap when tapped',
(WidgetTester tester) async {
bool wasTapped = false;
await tester.pumpWidget(MaterialApp(
home: TaskListTile(
task: testTask,
onTap: () {
wasTapped = true;
},
onDelete: () {})));
// Tap the tile
await tester.tap(find.text('Sample Task'));
await tester.pump();
expect(wasTapped, true);
});
}

View File

@@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:auto_gpt_flutter_client/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}