Commit 5c22026
Changed files (3)
app
lib
components
bluetooth_input
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);
+ });
+}