Commit 5c22026

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-05-13 07:36:24
extract input card
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 66c1ae7
Changed files (3)
app
lib
components
test
ui
components
bluetooth_input
app/lib/components/bluetooth_input/input_card.dart
@@ -0,0 +1,70 @@
+import 'package:flutter/material.dart';
+
+/// Card to place a complex opened input on.
+class InputCard extends StatelessWidget {
+  /// Create a card to host a complex input.
+  const InputCard({super.key,
+    required this.child,
+    this.title,
+    this.onClosed
+  });
+
+  /// Main content of the card
+  final Widget child;
+
+  /// Description of the card or the state of the card.
+  final Widget? title;
+
+  /// When provided a close icon at the top left corner is shown.
+  final void Function()? onClosed;
+
+  Widget _buildCloseIcon() => Align(
+    alignment: Alignment.topRight,
+    child: IconButton(
+      icon: const Icon(Icons.close),
+      onPressed: onClosed!,
+    ),
+  );
+
+  Widget _buildTitle(BuildContext context) => Align(
+    alignment: Alignment.topLeft,
+    child: Padding(
+      padding: const EdgeInsets.only(
+        top: 8.0,
+        left: 16.0,
+      ),
+      child: DefaultTextStyle(
+        style: Theme.of(context).textTheme.titleLarge ?? const TextStyle(),
+        child: title!,
+      ),
+    ),
+  );
+
+  Widget _buildBody() => Padding( // content
+    padding: const EdgeInsets.only(
+      top: 42,
+      bottom: 20,
+      left: 20,
+      right: 20,
+    ),
+    child: Center(
+      child: child,
+    ),
+  );
+
+  @override
+  Widget build(BuildContext context) => Card(
+    color: Theme.of(context).cardColor,
+    margin: const EdgeInsets.all(8),
+    child: Stack(
+      children: [
+        _buildBody(),
+        if (title != null)
+          _buildTitle(context),
+        if (onClosed != null)
+          _buildCloseIcon(),
+      ],
+    ),
+  );
+
+}
app/lib/components/bluetooth_input.dart
@@ -3,6 +3,7 @@ import 'dart:async';
 import 'package:blood_pressure_app/bluetooth/ble_read_cubit.dart';
 import 'package:blood_pressure_app/bluetooth/bluetooth_cubit.dart';
 import 'package:blood_pressure_app/bluetooth/device_scan_cubit.dart';
+import 'package:blood_pressure_app/components/bluetooth_input/input_card.dart';
 import 'package:blood_pressure_app/model/storage/storage.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -20,6 +21,7 @@ class BluetoothInput extends StatefulWidget {
   @override
   State<BluetoothInput> createState() => _BluetoothInputState();
 }
+// TODO: more info
 
 class _BluetoothInputState extends State<BluetoothInput> {
   /// Whether the user expanded bluetooth input
@@ -154,34 +156,9 @@ class _BluetoothInputState extends State<BluetoothInput> {
   Widget _buildMainCard(BuildContext context, {
     required Widget child,
     Widget? title,
-  }) => Card(
-    color: Theme.of(context).cardColor,
-    // borderRadius: BorderRadius.circular(24),
-    // width: MediaQuery.of(context).size.width,
-    // height: MediaQuery.of(context).size.width,
-    // padding: const EdgeInsets.all(24),
-    margin: const EdgeInsets.all(8),
-    child: Stack(
-      children: [
-        Padding( // content
-          padding: const EdgeInsets.all(24),
-          child: Center(
-            child: child,
-          ),
-        ),
-        if (title != null)
-          Align(
-            alignment: Alignment.topLeft,
-            child: title,
-          ),
-        Align(
-          alignment: Alignment.topRight,
-          child: IconButton(
-            icon: const Icon(Icons.close),
-            onPressed: _returnToIdle,
-          ),
-        ),
-      ],
-    ),
+  }) => InputCard(
+    onClosed: _returnToIdle,
+    title: title,
+    child: child,
   );
 }
app/test/ui/components/bluetooth_input/input_card.test.dart
@@ -0,0 +1,57 @@
+import 'package:blood_pressure_app/components/bluetooth_input/input_card.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('shows all elements', (WidgetTester tester) async {
+    await tester.pumpWidget(MaterialApp(
+      home: InputCard(
+        onClosed: () {},
+        title: const Text('Some title'),
+        child: const CircularProgressIndicator(),
+      ),
+    ));
+
+    expect(find.text('Some title'), findsOneWidget);
+    expect(find.byType(CircularProgressIndicator), findsOneWidget);
+    expect(find.byIcon(Icons.close), findsOneWidget);
+
+    expect(
+      tester.getBottomLeft(find.text('Some title')).dy,
+      lessThan(tester.getTopLeft(find.byType(CircularProgressIndicator)).dy),
+    );
+    expect(
+      tester.getBottomLeft(find.byIcon(Icons.close)).dy,
+      lessThan(tester.getTopLeft(find.byType(CircularProgressIndicator)).dy),
+    );
+  });
+
+  testWidgets('hides correct elements', (WidgetTester tester) async {
+    await tester.pumpWidget(const MaterialApp(
+      home: InputCard(
+        child: Text('content'),
+      ),
+    ));
+
+    expect(find.text('content'), findsOneWidget);
+    expect(find.byType(CircularProgressIndicator), findsNothing);
+    expect(find.byIcon(Icons.close), findsNothing);
+  });
+
+  testWidgets('triggers close listener', (WidgetTester tester) async {
+    int closeCount = 0;
+    await tester.pumpWidget(MaterialApp(
+      home: InputCard(
+        child: const SizedBox.shrink(),
+        onClosed: () {
+          closeCount++;
+        },
+      ),
+    ));
+
+    expect(find.byIcon(Icons.close), findsOneWidget);
+    await tester.tap(find.byIcon(Icons.close));
+    await tester.pumpAndSettle();
+    expect(closeCount, 1);
+  });
+}