Commit 241c7c1
Changed files (3)
lib
components
settings
test
ui
components
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) { },