Commit 241c7c1

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-11-06 06:30:00
test DropDownListTile
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 01afcb9
Changed files (3)
lib
components
test
lib/components/settings/dropdown_list_tile.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 
 /// A ListTile that allows choosing from a dropdown.
-class DropDownListTile<T> extends StatelessWidget {
+class DropDownListTile<T> extends StatefulWidget {
   /// Creates a list tile that allows choosing an item from a dropdown.
   ///
   /// Using this is equivalent to using a [ListTile] with a trailing [DropdownButton]. Please refer to those classes for
@@ -15,24 +15,52 @@ class DropDownListTile<T> extends StatelessWidget {
     this.subtitle,
     super.key});
 
+  /// Primary description of the tile.
   final Widget title;
+
+  /// Secondary description below the title.
   final Widget? subtitle;
+
+  /// A widget to display before the title.
   final Widget? leading;
 
-  final T value;
+  /// The value of the currently selected [DropdownMenuItem].
+  final T? value;
+
+  /// A list of items the user can select.
   final List<DropdownMenuItem<T>> items;
+
+  /// Called when the selection changes.
   final void Function(T? value) onChanged;
 
+  @override
+  State<DropDownListTile<T>> createState() => _DropDownListTileState<T>();
+}
+
+class _DropDownListTileState<T> extends State<DropDownListTile<T>> {
+  final focusNode = FocusNode();
+
+
+  @override
+  void dispose() {
+    focusNode.dispose();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
     return ListTile(
-      title: title,
-      subtitle: subtitle,
-      leading: leading,
+      title: widget.title,
+      subtitle: widget.subtitle,
+      leading: widget.leading,
+      onTap: () {
+        focusNode.requestFocus();
+      },
       trailing: DropdownButton<T>(
-        value: value,
-        items: items,
-        onChanged: onChanged,
+        focusNode: focusNode,
+        value: widget.value,
+        items: widget.items,
+        onChanged: widget.onChanged,
       ),
     );
   }
test/ui/components/settings/dropdown_list_tile_test.dart
@@ -0,0 +1,73 @@
+import 'package:blood_pressure_app/components/settings/dropdown_list_tile.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  group('DropDownListTile', () {
+    testWidgets('should not throw errors', (widgetTester) async {
+      await widgetTester.pumpWidget(_materialApp(DropDownListTile<int>(
+        title: const Text('test title'),
+        onChanged: (int? newValue) {},
+        items: [
+          for (int i = 0; i < 10; i++)
+            DropdownMenuItem(value: i, child: Text('option $i'))
+        ],
+        value: 3,
+      )));
+      await widgetTester.pumpWidget(_materialApp(DropDownListTile<int>(
+        title: const Text('This is a very long test title.'),
+        subtitle: const Text('This is a very long test subtitle that should go over multiple lines.'),
+        leading: const Icon(Icons.add),
+        onChanged: (int? newValue) {},
+        items: [
+          for (int i = 0; i < 1000; i++)
+            DropdownMenuItem(value: i, child: Text('option $i'))
+        ],
+        value: 527,
+      )));
+    });
+    testWidgets('should display selected option', (widgetTester) async {
+      await widgetTester.pumpWidget(_materialApp(DropDownListTile<int>(
+        title: const Text('test title'),
+        onChanged: (int? newValue) {},
+        items: [
+          for (int i = 0; i < 10; i++)
+            DropdownMenuItem(value: i, child: Text('option $i'))
+        ],
+        value: 3,
+      )));
+      expect(find.text('option 3'), findsOneWidget);
+      expect(find.text('option 4'), findsNothing);
+    });
+    testWidgets('should call onChanged on option selected', (widgetTester) async {
+      int callCount = 0;
+      await widgetTester.pumpWidget(_materialApp(DropDownListTile<int>(
+        title: const Text('test title'),
+        onChanged: (int? newValue) {
+          callCount += 1;
+          expect(newValue, 5);
+        },
+        items: [
+          for (int i = 0; i < 10; i++)
+            DropdownMenuItem(value: i, child: Text('option $i'))
+        ],
+        value: 3,
+      )));
+
+      await widgetTester.tap(find.text('option 3'));
+      await widgetTester.pumpAndSettle();
+
+      expect(find.text('option 5'), findsOneWidget);
+      await widgetTester.tap(find.text('option 5'));
+      await widgetTester.pumpAndSettle();
+
+      expect(callCount, 1);
+    });
+  });
+}
+
+Widget _materialApp(Widget child) {
+  return MaterialApp(
+    home: Scaffold(body: child),
+  );
+}
\ No newline at end of file
test/ui/components/settings/slider_list_tile_test.dart
@@ -4,7 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
 
 void main() {
   group('SliderListTile', () {
-    testWidgets('should not throw without errors', (widgetTester) async {
+    testWidgets('should not throw errors', (widgetTester) async {
       await widgetTester.pumpWidget(_materialApp(SliderListTile(
         title: const Text('test title'),
         onChanged: (double newValue) {  },