Commit 8b6d4c6
Changed files (3)
lib
components
statistics
test
ui
components
lib/components/statistics/value_distribution.dart
@@ -2,6 +2,7 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
/// A statistic that shows how often values occur in a list of values.
@@ -87,6 +88,8 @@ class _ValueDistributionPainter extends CustomPainter {
///
/// The key is the x order on the bar and it's value indicates it's relative
/// height.
+ ///
+ /// The height of the bar is how often it occurs in a list of values.
final Map<int, double> distribution;
final AppLocalizations localizations;
@@ -173,18 +176,68 @@ class _ValueDistributionPainter extends CustomPainter {
textPainter.paint(canvas, position);
}
- drawLabel(localizations.minOf(distribution.keys.min.toString()),
+ drawLabel(localizations.minOf(_min),
Alignment.centerLeft,);
- drawLabel(localizations.avgOf(distribution.keys.average.round().toString()),
- Alignment.center,); // FIXME: Average doesn't depend on count
- drawLabel(localizations.maxOf(distribution.keys.max.toString()),
+ drawLabel(localizations.avgOf(_average),
+ Alignment.center,);
+ drawLabel(localizations.maxOf(_max),
Alignment.centerRight,);
}
@override
bool shouldRepaint(covariant _ValueDistributionPainter oldDelegate) =>
distribution == oldDelegate.distribution;
-
-}
- // TODO: Consider adding semantics
+ @override
+ bool shouldRebuildSemantics(covariant _ValueDistributionPainter oldDelegate)
+ => distribution == oldDelegate.distribution;
+
+ @override
+ // TODO: test semanticsBuilder
+ SemanticsBuilderCallback? get semanticsBuilder => (Size size) {
+ final oneThird = size.width / 3;
+ return [
+ CustomPainterSemantics(
+ rect: Rect.fromLTRB(0, 0, oneThird, size.height),
+ properties: SemanticsProperties(
+ label: localizations.minOf(_min),
+ textDirection: TextDirection.ltr,
+ ),
+ ),
+ CustomPainterSemantics(
+ rect: Rect.fromLTRB(oneThird, 0, 2 * oneThird, size.height),
+ properties: SemanticsProperties(
+ label: localizations.avgOf(_average),
+ textDirection: TextDirection.ltr,
+ ),
+ ),
+ CustomPainterSemantics(
+ rect: Rect.fromLTRB(2 * oneThird, 0, size.width, size.height),
+ properties: SemanticsProperties(
+ label: localizations.maxOf(_max),
+ textDirection: TextDirection.ltr,
+ ),
+ ),
+ ];
+ };
+
+ /// Max (right end) value in distribution.
+ String get _max => distribution.keys.max.toString();
+
+ /// Min (left end) value in distribution.
+ String get _min => distribution.keys.min.toString();
+
+ /// Average value of distribution.
+ ///
+ ///
+ String get _average {
+ double sum = 0;
+ int count = 0;
+ for (final key in distribution.keys) {
+ sum += key * distribution[key]!;
+ count += distribution[key]!.toInt();
+ }
+ return (sum / count).round().toString();
+ }
+// TODO: test averages
+}
test/ui/components/statistics/blood_pressure_distribution_test.dart
@@ -0,0 +1,5 @@
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ testWidgets('should ', (widgetTester) => null)
+}
test/ui/components/statistics/value_distribution_test.dart
@@ -1,6 +1,7 @@
import 'package:blood_pressure_app/components/statistics/value_distribution.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -49,7 +50,7 @@ void main() {
width: 180,
child: ValueDistribution(
color: Colors.red,
- values: [1,2,3,3,4,5], // min 3, max 10, avg 6 + 2/3
+ values: [1,2,3,3,4,5],
),
),),);
@@ -63,4 +64,31 @@ void main() {
..line(color: Colors.white70) // start drawing decoration
,);
},);
+ testWidgets('should have semantics labels with correct values', (widgetTester) async {
+ await widgetTester.pumpWidget(materialApp(const SizedBox(
+ height: 50,
+ width: 180,
+ child: ValueDistribution(
+ color: Colors.red,
+ values: [5,6,3,8,8,10], // min 3, max 10, avg 6 + 2/3
+ ),
+ ),),);
+
+ final localizations = await AppLocalizations.delegate.load(const Locale('en'));
+ final labels = _getAllLabels(widgetTester.getSemantics(find.byType(ValueDistribution)));
+
+ expect(labels, contains(localizations.minOf('3')));
+ expect(labels, contains(localizations.maxOf('10')));
+ expect(labels, contains(localizations.avgOf('7')));
+ },);
+}
+
+/// Recursively fetches the labels of the semantics node and all its children.
+List<String> _getAllLabels(SemanticsNode node) {
+ final labels = [node.label];
+ node.visitChildren((node) {
+ labels.addAll(_getAllLabels(node));
+ return true;
+ });
+ return labels;
}