diff --git a/lib/src/chart/base/axis_chart/side_titles/side_titles_flex.dart b/lib/src/chart/base/axis_chart/side_titles/side_titles_flex.dart index a1ca5ed9dd..06ec81d3bc 100644 --- a/lib/src/chart/base/axis_chart/side_titles/side_titles_flex.dart +++ b/lib/src/chart/base/axis_chart/side_titles/side_titles_flex.dart @@ -94,72 +94,8 @@ class AxisSideTitlesRenderFlex extends RenderBox } } - bool get _canComputeIntrinsics => false; - - double _getIntrinsicSize({ - required Axis sizingDirection, - required double extent, - required _ChildSizingFunction childSize, - }) { - if (!_canComputeIntrinsics) { - assert( - RenderObject.debugCheckingIntrinsics, - 'Intrinsics are not available for CrossAxisAlignment.baseline.', - ); - return 0.0; - } - double totalFlex = 0.0; - double inflexibleSpace = 0.0; - double maxFlexFractionSoFar = 0.0; - RenderBox? child = firstChild; - while (child != null) { - inflexibleSpace += childSize(child, extent); - final FlexParentData childParentData = - child.parentData! as FlexParentData; - child = childParentData.nextSibling; - } - return maxFlexFractionSoFar * totalFlex + inflexibleSpace; - } - - @override - double computeMinIntrinsicWidth(double height) { - return _getIntrinsicSize( - sizingDirection: Axis.horizontal, - extent: height, - childSize: (RenderBox child, double extent) => - child.getMinIntrinsicWidth(extent), - ); - } - @override - double computeMaxIntrinsicWidth(double height) { - return _getIntrinsicSize( - sizingDirection: Axis.horizontal, - extent: height, - childSize: (RenderBox child, double extent) => - child.getMaxIntrinsicWidth(extent), - ); - } - - @override - double computeMinIntrinsicHeight(double width) { - return _getIntrinsicSize( - sizingDirection: Axis.vertical, - extent: width, - childSize: (RenderBox child, double extent) => - child.getMinIntrinsicHeight(extent), - ); - } - - @override - double computeMaxIntrinsicHeight(double width) { - return _getIntrinsicSize( - sizingDirection: Axis.vertical, - extent: width, - childSize: (RenderBox child, double extent) => - child.getMaxIntrinsicHeight(extent), - ); - } + bool get debugNeedsLayout => false; @override double? computeDistanceToActualBaseline(TextBaseline baseline) { @@ -189,14 +125,6 @@ class AxisSideTitlesRenderFlex extends RenderBox @override Size computeDryLayout(BoxConstraints constraints) { - if (!_canComputeIntrinsics) { - assert(debugCannotComputeDryLayout( - reason: - 'Dry layout cannot be computed for CrossAxisAlignment.baseline, which requires a full layout.', - )); - return Size.zero; - } - final _LayoutSizes sizes = _computeSizes( layoutChild: ChildLayoutHelper.dryLayoutChild, constraints: constraints, @@ -340,8 +268,6 @@ class AxisSideTitlesRenderFlex extends RenderBox } } -typedef _ChildSizingFunction = double Function(RenderBox child, double extent); - class _LayoutSizes { const _LayoutSizes({ required this.mainSize, diff --git a/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart b/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart index 592a5320a5..94fb7f1151 100644 --- a/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart +++ b/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart @@ -46,14 +46,6 @@ class SideTitlesWidget extends StatelessWidget { FlTitlesData get titlesData => axisChartData.titlesData; - AxisTitles get leftTitles => titlesData.leftTitles; - - AxisTitles get topTitles => titlesData.topTitles; - - AxisTitles get rightTitles => titlesData.rightTitles; - - AxisTitles get bottomTitles => titlesData.bottomTitles; - bool get isLeftOrTop => side == TitlesSide.left || side == TitlesSide.top; bool get isRightOrBottom => @@ -69,8 +61,6 @@ class SideTitlesWidget extends StatelessWidget { return titlesData.rightTitles; case TitlesSide.bottom: return titlesData.bottomTitles; - default: - throw StateError("Side is not valid $side"); } } @@ -90,8 +80,6 @@ class SideTitlesWidget extends StatelessWidget { return Alignment.centerRight; case TitlesSide.bottom: return Alignment.bottomCenter; - default: - throw StateError("Side is not valid $side"); } } @@ -103,8 +91,6 @@ class SideTitlesWidget extends StatelessWidget { case TitlesSide.top: case TitlesSide.bottom: return titlesData.allSidesPadding.onlyLeftRight; - default: - throw StateError("Side is not valid $side"); } } @@ -116,8 +102,6 @@ class SideTitlesWidget extends StatelessWidget { case TitlesSide.top: case TitlesSide.bottom: return titlesData.allSidesPadding.horizontal; - default: - throw StateError("Side is not valid $side"); } } @@ -179,6 +163,9 @@ class SideTitlesWidget extends StatelessWidget { @override Widget build(BuildContext context) { + if (!axisTitles.showAxisTitles && !axisTitles.showSideTitles) { + return Container(); + } final axisViewSize = isHorizontal ? parentSize.width : parentSize.height; return Align( alignment: alignment, @@ -245,8 +232,6 @@ class _AxisTitleWidget extends StatelessWidget { return 0; case TitlesSide.bottom: return 0; - default: - throw StateError("Side is not valid $side"); } } diff --git a/test/chart/base/axis_chart/axis_chart_scaffold_widget_test.dart b/test/chart/base/axis_chart/axis_chart_scaffold_widget_test.dart new file mode 100644 index 0000000000..f5f5b41997 --- /dev/null +++ b/test/chart/base/axis_chart/axis_chart_scaffold_widget_test.dart @@ -0,0 +1,293 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:fl_chart/src/chart/base/axis_chart/axis_chart_scaffold_widget.dart'; +import 'package:fl_chart/src/chart/base/axis_chart/side_titles/side_titles_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + const viewSize = Size(400, 400); + + final lineChartDataBase = LineChartData( + minX: 0, + maxX: 10, + minY: 0, + maxY: 10, + ); + + final lineChartDataWithNoTitles = lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: false, + leftTitles: AxisTitles(), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles(), + ), + ); + + final lineChartDataWithAllTitles = lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles( + axisName: const Icon(Icons.arrow_left), + axisNameSize: 10, + sideTitles: SideTitles( + showTitles: true, + reservedSize: 10, + getTitles: (double value, TitleMeta meta) { + return Text('L-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + topTitles: AxisTitles( + axisName: const Icon(Icons.arrow_drop_up), + axisNameSize: 20, + sideTitles: SideTitles( + showTitles: true, + reservedSize: 20, + getTitles: (double value, TitleMeta meta) { + return Text('T-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + rightTitles: AxisTitles( + axisName: const Icon(Icons.arrow_right), + axisNameSize: 30, + sideTitles: SideTitles( + showTitles: true, + reservedSize: 30, + getTitles: (double value, TitleMeta meta) { + return Text('R-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + bottomTitles: AxisTitles( + axisName: const Icon(Icons.arrow_drop_down), + axisNameSize: 40, + sideTitles: SideTitles( + showTitles: true, + reservedSize: 40, + getTitles: (double value, TitleMeta meta) { + return Text('B-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + ), + ); + + final lineChartDataWithOnlyLeftTitles = lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles( + axisName: const Icon(Icons.arrow_left), + axisNameSize: 10, + sideTitles: SideTitles( + showTitles: true, + reservedSize: 10, + getTitles: (double value, TitleMeta meta) { + return Text('L-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles(), + ), + ); + + final lineChartDataWithOnlyLeftTitlesWithoutAxisName = + lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles( + axisNameSize: 10, + sideTitles: SideTitles( + showTitles: true, + reservedSize: 10, + getTitles: (double value, TitleMeta meta) { + return Text('L-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles(), + ), + ); + + testWidgets( + 'LineChart with no titles', + (WidgetTester tester) async { + Size? chartDrawingSize; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: AxisChartScaffoldWidget( + chart: LayoutBuilder(builder: (context, constraints) { + chartDrawingSize = constraints.biggest; + return Container( + color: Colors.red, + ); + }), + data: lineChartDataWithNoTitles, + ), + ), + ), + ), + ), + ); + expect(chartDrawingSize, viewSize); + expect(find.byType(Text), findsNothing); + expect(find.byType(Icon), findsNothing); + }, + ); + + testWidgets( + 'LineChart with all titles', + (WidgetTester tester) async { + Size? chartDrawingSize; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: AxisChartScaffoldWidget( + chart: LayoutBuilder(builder: (context, constraints) { + chartDrawingSize = constraints.biggest; + return Container( + color: Colors.red, + ); + }), + data: lineChartDataWithAllTitles, + ), + ), + ), + ), + ), + ); + + Future checkSide(TitlesSide side) async { + String axisChar; + switch (side) { + case TitlesSide.left: + axisChar = 'L'; + break; + case TitlesSide.top: + axisChar = 'T'; + break; + case TitlesSide.right: + axisChar = 'R'; + break; + case TitlesSide.bottom: + axisChar = 'B'; + break; + default: + throw StateError('Invalid'); + } + for (int i = 0; i <= 10; i++) { + expect(find.text('$axisChar-$i'), findsOneWidget); + } + } + + expect(chartDrawingSize, const Size(320, 280)); + expect(find.byIcon(Icons.arrow_left), findsOneWidget); + checkSide(TitlesSide.left); + + expect(find.byIcon(Icons.arrow_drop_up), findsOneWidget); + checkSide(TitlesSide.top); + + expect(find.byIcon(Icons.arrow_right), findsOneWidget); + checkSide(TitlesSide.right); + + expect(find.byIcon(Icons.arrow_drop_down), findsOneWidget); + checkSide(TitlesSide.bottom); + + expect(find.byType(Text), findsNWidgets(44)); + expect(find.byType(Icon), findsNWidgets(4)); + }, + ); + + testWidgets( + 'LineChart with only left titles', + (WidgetTester tester) async { + Size? chartDrawingSize; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: AxisChartScaffoldWidget( + chart: LayoutBuilder(builder: (context, constraints) { + chartDrawingSize = constraints.biggest; + return Container( + color: Colors.red, + ); + }), + data: lineChartDataWithOnlyLeftTitles, + ), + ), + ), + ), + ), + ); + + expect(chartDrawingSize, const Size(380, 400)); + expect(find.byIcon(Icons.arrow_left), findsOneWidget); + for (int i = 0; i <= 10; i++) { + expect(find.text('L-$i'), findsOneWidget); + } + + expect(find.byType(Text), findsNWidgets(11)); + expect(find.byType(Icon), findsNWidgets(1)); + }, + ); + + testWidgets( + 'LineChart with only left titles without axis name', + (WidgetTester tester) async { + Size? chartDrawingSize; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: AxisChartScaffoldWidget( + chart: LayoutBuilder(builder: (context, constraints) { + chartDrawingSize = constraints.biggest; + return Container( + color: Colors.red, + ); + }), + data: lineChartDataWithOnlyLeftTitlesWithoutAxisName, + ), + ), + ), + ), + ), + ); + + expect(chartDrawingSize, const Size(390, 400)); + for (int i = 0; i <= 10; i++) { + expect(find.text('L-$i'), findsOneWidget); + } + + expect(find.byType(Text), findsNWidgets(11)); + expect(find.byType(Icon), findsNothing); + }, + ); +} diff --git a/test/chart/base/axis_chart/side_titles/side_titles_flex_test.dart b/test/chart/base/axis_chart/side_titles/side_titles_flex_test.dart new file mode 100644 index 0000000000..a9f4de8da0 --- /dev/null +++ b/test/chart/base/axis_chart/side_titles/side_titles_flex_test.dart @@ -0,0 +1,251 @@ +import 'package:fl_chart/src/chart/base/axis_chart/side_titles/side_titles_flex.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('SideTitlesFlex test', () { + testWidgets( + 'Test vertical mode', + (WidgetTester tester) async { + const viewHeight = 400.0; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: 40, + height: viewHeight, + child: SideTitlesFlex( + direction: Axis.vertical, + axisSideMetaData: AxisSideMetaData( + 0, + 10, + viewHeight, + ), + widgetHolders: oneToNineWidgetHolders(viewHeight), + ), + ), + ), + ), + ), + ); + + for (int i = 1; i <= 9; i++) { + expect(find.text(i.toDouble().toString()), findsOneWidget); + } + expect(find.text('10.0'), findsNothing); + expect(find.text('11.0'), findsNothing); + expect(find.text('0.0'), findsNothing); + }, + ); + + testWidgets( + 'Test horizontal mode', + (WidgetTester tester) async { + const viewWidth = 400.0; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewWidth, + height: 40, + child: SideTitlesFlex( + direction: Axis.horizontal, + axisSideMetaData: AxisSideMetaData( + 0, + 10, + viewWidth, + ), + widgetHolders: List.generate(9, (index) => index.toDouble()) + .map( + (value) => AxisSideTitleWidgetHolder( + AxisSideTitleMetaData( + value + 1, (value / 10) * viewWidth), + Text((value + 1).toString()), + ), + ) + .toList(), + ), + ), + ), + ), + ), + ); + + for (int i = 1; i <= 9; i++) { + expect(find.text(i.toDouble().toString()), findsOneWidget); + } + expect(find.text('10.0'), findsNothing); + expect(find.text('11.0'), findsNothing); + expect(find.text('0.0'), findsNothing); + }, + ); + + testWidgets( + 'Test update from horizontal to vertical', + (WidgetTester tester) async { + const valueKey = ValueKey("asdf"); + + const viewSize = 400.0; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize, + height: 40, + child: SideTitlesFlex( + key: valueKey, + direction: Axis.horizontal, + axisSideMetaData: AxisSideMetaData( + 0, + 10, + viewSize, + ), + widgetHolders: oneToNineWidgetHolders(viewSize), + ), + ), + ), + ), + ), + ); + + for (int i = 1; i <= 9; i++) { + expect(find.text(i.toDouble().toString()), findsOneWidget); + } + expect(find.text('10.0'), findsNothing); + expect(find.text('11.0'), findsNothing); + expect(find.text('0.0'), findsNothing); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: 40, + height: viewSize, + child: SideTitlesFlex( + key: valueKey, + direction: Axis.vertical, + axisSideMetaData: AxisSideMetaData( + 0, + 10, + viewSize, + ), + widgetHolders: oneToNineWidgetHolders(viewSize), + ), + ), + ), + ), + ), + ); + for (int i = 1; i <= 9; i++) { + expect(find.text(i.toDouble().toString()), findsOneWidget); + } + expect(find.text('10.0'), findsNothing); + expect(find.text('11.0'), findsNothing); + expect(find.text('0.0'), findsNothing); + }, + ); + }); + + test('AxisSideTitlesRenderFlex horizontal', () { + const viewSize = 400.0; + final axisSideMetaData = AxisSideMetaData( + 0, + 10, + viewSize, + ); + final sideTitlesMetaData = oneToNineSideTitleMetaData(viewSize); + final renderFlex = AxisSideTitlesRenderFlex( + direction: Axis.horizontal, + axisSideMetaData: axisSideMetaData, + axisSideTitlesMetaData: sideTitlesMetaData, + ); + + DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + renderFlex.debugFillProperties(builder); + expect(builder.properties.length > 1, true); + expect( + (builder.properties.last as EnumProperty).value, Axis.horizontal); + expect(renderFlex.direction, Axis.horizontal); + expect(renderFlex.axisSideMetaData, axisSideMetaData); + expect(renderFlex.axisSideTitlesMetaData, sideTitlesMetaData); + expect(renderFlex.debugNeedsLayout, false); + expect( + renderFlex.hitTestChildren(BoxHitTestResult(), position: Offset.zero), + false, + ); + expect( + renderFlex + .computeDryLayout(BoxConstraints.tight(const Size(viewSize, 40))), + const Size(viewSize, 40), + ); + expect( + // ignore: invalid_use_of_protected_member + renderFlex.computeDistanceToActualBaseline(TextBaseline.alphabetic), + null, + ); + }); + + test('AxisSideTitlesRenderFlex vertical', () { + const viewSize = 400.0; + final axisSideMetaData = AxisSideMetaData( + 0, + 10, + viewSize, + ); + final sideTitlesMetaData = oneToNineSideTitleMetaData(viewSize); + final renderFlex = AxisSideTitlesRenderFlex( + direction: Axis.vertical, + axisSideMetaData: axisSideMetaData, + axisSideTitlesMetaData: sideTitlesMetaData, + ); + expect( + renderFlex + .computeDryLayout(BoxConstraints.tight(const Size(viewSize, 40))), + const Size(viewSize, 40), + ); + expect( + // ignore: invalid_use_of_protected_member + renderFlex.computeDistanceToActualBaseline(TextBaseline.alphabetic), + null, + ); + }); + + test('AxisSideTitleMetaData equality test', () { + final data1 = AxisSideTitleMetaData(0, 0); + final data1Clone = AxisSideTitleMetaData(0, 0); + final data2 = AxisSideTitleMetaData(2, 0); + expect(data1 == data1Clone, true); + expect(data1 == data2, false); + }); + + test('AxisSideMetaData diff', () { + expect(AxisSideMetaData(5, 10, 100).diff, 5); + expect(AxisSideMetaData(5, 10, 0).diff, 5); + expect(AxisSideMetaData(9, 10, 0).diff, 1); + }); +} + +List oneToNineWidgetHolders(double viewSize) { + return oneToNineSideTitleMetaData(viewSize).asMap().entries.map((e) { + final index = e.key; + final sideTitlesMetaData = e.value; + return AxisSideTitleWidgetHolder( + sideTitlesMetaData, + Text((index + 1).toDouble().toString()), + ); + }).toList(); +} + +List oneToNineSideTitleMetaData(double viewSize) { + return List.generate(9, (index) => index.toDouble()) + .map( + (value) => AxisSideTitleMetaData(value + 1, (value / 10) * viewSize), + ) + .toList(); +} diff --git a/test/chart/base/axis_chart/side_titles/side_titles_widget_test.dart b/test/chart/base/axis_chart/side_titles/side_titles_widget_test.dart new file mode 100644 index 0000000000..9231eaaaf2 --- /dev/null +++ b/test/chart/base/axis_chart/side_titles/side_titles_widget_test.dart @@ -0,0 +1,388 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:fl_chart/src/chart/base/axis_chart/side_titles/side_titles_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + const viewSize = Size(400, 400); + + final lineChartDataBase = LineChartData( + minX: 0, + maxX: 10, + minY: 0, + maxY: 10, + ); + + final lineChartDataWithNoTitles = lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: false, + leftTitles: AxisTitles(), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles(), + ), + ); + + final lineChartDataWithAllTitles = lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles( + axisName: const Text('Left Titles'), + sideTitles: SideTitles( + showTitles: true, + getTitles: (double value, TitleMeta meta) { + return Text('L-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + topTitles: AxisTitles( + axisName: const Text('Top Titles'), + sideTitles: SideTitles( + showTitles: true, + getTitles: (double value, TitleMeta meta) { + return Text('T-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + rightTitles: AxisTitles( + axisName: const Text('Right Titles'), + sideTitles: SideTitles( + showTitles: true, + getTitles: (double value, TitleMeta meta) { + return Text('R-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + bottomTitles: AxisTitles( + axisName: const Text('Bottom Titles'), + sideTitles: SideTitles( + showTitles: true, + getTitles: (double value, TitleMeta meta) { + return Text('B-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + ), + ); + + final lineChartDataWithOnlyLeftTitles = lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles( + axisName: const Text('Left Titles'), + sideTitles: SideTitles( + showTitles: true, + getTitles: (double value, TitleMeta meta) { + return Text('L-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles(), + ), + ); + + final lineChartDataWithOnlyLeftTitlesWithoutAxisName = + lineChartDataBase.copyWith( + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitles: (double value, TitleMeta meta) { + return Text('L-${value.toInt().toString()}'); + }, + interval: 1, + ), + ), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles(), + ), + ); + + final barChartDataWithOnlyBottomTitles = BarChartData( + barGroups: [ + BarChartGroupData( + x: 0, + barRods: [ + BarChartRodData(toY: 10), + ], + ), + BarChartGroupData( + x: 1, + barRods: [ + BarChartRodData(toY: 10), + ], + ), + BarChartGroupData( + x: 2, + barRods: [ + BarChartRodData(toY: 10), + ], + ), + ], + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles(), + topTitles: AxisTitles(), + rightTitles: AxisTitles(), + bottomTitles: AxisTitles( + axisName: const Icon(Icons.check), + sideTitles: SideTitles( + showTitles: true, + getTitles: (value, meta) { + return TextButton( + onPressed: () {}, + child: Text( + value.toInt().toString(), + ), + ); + }, + ), + ), + ), + ); + + final barChartDataWithOnlyRightTitles = BarChartData( + barGroups: [ + BarChartGroupData( + x: 0, + barRods: [ + BarChartRodData(toY: 10), + ], + ), + BarChartGroupData( + x: 1, + barRods: [ + BarChartRodData(toY: 10), + ], + ), + BarChartGroupData( + x: 2, + barRods: [ + BarChartRodData(toY: 10), + ], + ), + ], + titlesData: FlTitlesData( + show: true, + leftTitles: AxisTitles(), + topTitles: AxisTitles(), + rightTitles: AxisTitles( + axisName: const Icon(Icons.arrow_right), + sideTitles: SideTitles( + showTitles: true, + interval: 1, + getTitles: (value, meta) { + return TextButton( + onPressed: () {}, + child: Text( + value.toInt().toString(), + ), + ); + }, + ), + ), + bottomTitles: AxisTitles(), + ), + ); + + testWidgets( + 'LineChart with no titles', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: SideTitlesWidget( + side: TitlesSide.left, + axisChartData: lineChartDataWithNoTitles, + parentSize: viewSize, + ), + ), + ), + ), + ), + ); + + expect(find.byType(Text), findsNothing); + }, + ); + + testWidgets( + 'LineChart with all titles', + (WidgetTester tester) async { + Future checkSide(TitlesSide side) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: SideTitlesWidget( + side: side, + axisChartData: lineChartDataWithAllTitles, + parentSize: viewSize, + ), + ), + ), + ), + ), + ); + + String axisName; + switch (side) { + case TitlesSide.left: + axisName = 'Left'; + break; + case TitlesSide.top: + axisName = 'Top'; + break; + case TitlesSide.right: + axisName = 'Right'; + break; + case TitlesSide.bottom: + axisName = 'Bottom'; + break; + default: + throw StateError('Invalid'); + } + expect(find.text('$axisName Titles'), findsOneWidget); + for (int i = 0; i <= 10; i++) { + expect(find.text('${axisName.characters.first}-$i'), findsOneWidget); + } + } + + await checkSide(TitlesSide.left); + await checkSide(TitlesSide.top); + await checkSide(TitlesSide.right); + await checkSide(TitlesSide.bottom); + }, + ); + + testWidgets( + 'LineChart with Only left titles', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: SideTitlesWidget( + side: TitlesSide.left, + axisChartData: lineChartDataWithOnlyLeftTitles, + parentSize: viewSize, + ), + ), + ), + ), + ), + ); + expect(find.text('Left Titles'), findsOneWidget); + for (int i = 0; i <= 10; i++) { + expect(find.text('L-$i'), findsOneWidget); + } + + expect(find.byType(Text), findsNWidgets(12)); + }, + ); + + testWidgets( + 'LineChart with Only left titles without axis name', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: SideTitlesWidget( + side: TitlesSide.left, + axisChartData: lineChartDataWithOnlyLeftTitlesWithoutAxisName, + parentSize: viewSize, + ), + ), + ), + ), + ), + ); + for (int i = 0; i <= 10; i++) { + expect(find.text('L-$i'), findsOneWidget); + } + + expect(find.byType(Text), findsNWidgets(11)); + }, + ); + + testWidgets( + 'BarChart with Only bottom titles', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: SideTitlesWidget( + side: TitlesSide.bottom, + axisChartData: barChartDataWithOnlyBottomTitles, + parentSize: viewSize, + ), + ), + ), + ), + ), + ); + + expect(find.byIcon(Icons.check), findsOneWidget); + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsOneWidget); + expect(find.text('2'), findsOneWidget); + expect(find.byType(TextButton), findsNWidgets(3)); + }, + ); + + testWidgets( + 'BarChart with Only right titles', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: viewSize.width, + height: viewSize.height, + child: SideTitlesWidget( + side: TitlesSide.right, + axisChartData: barChartDataWithOnlyRightTitles, + parentSize: viewSize, + ), + ), + ), + ), + ), + ); + + expect(find.byIcon(Icons.arrow_right), findsOneWidget); + for (int i = 0; i <= 10; i++) { + expect(find.text('$i'), findsOneWidget); + } + expect(find.byType(TextButton), findsNWidgets(11)); + }, + ); +}