Repository: imaNNeo/fl_chart

Issue:
enhancement to support  horizontal bar chart.
enhancement to support  horizontal bar and stacked bar charts.


Discussion: I'm working on horizontal bar charts:
https://github.com/juumixx/fl_chart/commits/horizontal-bar-chart

Things that are still not behaving nicely are backdraw bars and stack items. The rest should be production ready.
Any update for horizontal bar chart?
Any update on this?
I'm busy as a bee  😢😢
I appreciate your help and your PRs
We added StackedBarChart feature. I remove it from the title. Now we just need the horizontal bar chart.
Thanks!
There is a temporary workaround mentioned [here](https://github.com/imaNNeoFighT/fl_chart/issues/552#issuecomment-775812719)

Any update on this?
Not yet!
> There is a temporary workaround mentioned [here](https://github.com/imaNNeoFighT/fl_chart/issues/552#issuecomment-775812719)
@imaNNeoFighT 
Okay, do you have any idea about rotating TooltipIndicators,
Yes, it is on the way [here](https://github.com/imaNNeoFighT/fl_chart/pull/679).
Hi, @imaNNeoFighT any updates on this? Thank you
Hi @sawirricardo. Not yet sir!
Hi @imaNNeoFighT , is there any update about the horizontal bar chart?
Hi @spoeck. 
Unfortunately, not yet!
Hi @imaNNeoFighT i have recently started working with BarCharts in flutter and came across the fl_chart library as it had the highest popularity score. The current requirement for the app i am developing requires us to create a horizontal bar chart, would love to see it added to the library, is there any update on when can we expect it to be added?
Hi @akshatj427 
Unfortunately, I'm not working full-time on this project (I hope I could do)
I work here in my free and leisure time. That's why everything is a little bit slow.

At the moment you can give a thumb up to make it a high-priority issue. 
And also [donation](https://www.buymeacoffee.com/fl_chart) helps me a lot to put more time into it.
I forgot to tell you that you can also contribute to developing this feature! You can start by reading our [guidelines](https://github.com/imaNNeoFighT/fl_chart/blob/master/CONTRIBUTING.md).

Hi @imaNNeoFighT, is there any update on this?
any update on this?
Any updates?

Have any updates for this?
Any updates are welcome
Hi, are there any updates except workaround with rotation? Thanks
Any updates?
Does this plugin support horizontal bar chart now? If yes, where can find an example?
Any updates?
hi guys, this might be a workaround not for everyone but i currently using the line chart as a horizontal bar  and the result like this


![image](https://github.com/imaNNeo/fl_chart/assets/52267648/9fe57237-7ceb-4593-a960-a620313a1b6a)

Is there any updates for Horizontal Bar Chart ?
Hi, unfortunately, I couldn't find enough free time to do that yet.
Please stay tuned, you can also [be my sponsor](https://github.com/sponsors/imaNNeo) to motivate me, then I can put more of time into this project.
any update on this?
y'all can wrap a RotatedBox outside and set quarterTurns: 1 to make it horizontal. That can be a temporary solve for this.

![Screenshot 2024-02-19 at 14 49 33](https://github.com/imaNNeo/fl_chart/assets/88186817/47ef0b26-1b3d-4392-8f22-2944ad8ab1d0)




![simulator_screenshot_9294622F-8F0A-4637-BFA5-6B8087443F73](https://github.com/imaNNeo/fl_chart/assets/88186817/7a5fe0e3-f887-4d25-b15f-326585a1e4e6)


hi all, I tried to solve this problem with the existing api, so you can refer to it.
I use the 0.67.0 version

`
RotatedBox(quarterTurns: -3, 
   child: BarChart(
      BarChartData(
...
    ));

BarTouchTooltipData(
rotateAngle: 270,
);

SideTitleWidget(
      angle: 4.7,
 );
`
<img width="354" alt="截屏2024-04-11 20 35 12" src="https://github.com/imaNNeo/fl_chart/assets/68675046/f83c4c9f-d8f5-4a80-b236-96ec8a5f3ac4">

Hi, I am having problems with aligning bottom titles (left titles after rotating) to the left, everything i tried it just looks like its centered. 

First i rotated chart with 
`RotatedBox(quarterTurns: 1,`

This is my code for titles:
```
Widget bottomTitlesLanguages(double value, TitleMeta meta) {
    const style = TextStyle(fontSize: 10);
    if (value.toInt() < 0 || value.toInt() >= languageData.length) {
      return Container();
    }
    return SideTitleWidget(
      axisSide: meta.axisSide,
      space: 10, // Add space to separate the labels from the axis
      child: Transform.rotate(
        angle: -1.5708, // Rotate 90 degrees counter-clockwise (in radians)
        child: Container(
          alignment: Alignment.centerLeft, // Align text to the left
          child: Text(
            languageData[value.toInt()].x,
            style: style,
          ),
        ),
      ),
    );
  }
  ```
  
  
![image](https://github.com/user-attachments/assets/3521b66c-cae5-44ac-9fc5-40b77d886a40)

A basic version of this could be easily implemented using [FractionallySizedBox](https://api.flutter.dev/flutter/widgets/FractionallySizedBox-class.html).
> Hi, I am having problems with aligning bottom titles (left titles after rotating) to the left, everything i tried it just looks like its centered.
> 
> First i rotated chart with `RotatedBox(quarterTurns: 1,`
> 
> This is my code for titles:
> 
> ```
> Widget bottomTitlesLanguages(double value, TitleMeta meta) {
>     const style = TextStyle(fontSize: 10);
>     if (value.toInt() < 0 || value.toInt() >= languageData.length) {
>       return Container();
>     }
>     return SideTitleWidget(
>       axisSide: meta.axisSide,
>       space: 10, // Add space to separate the labels from the axis
>       child: Transform.rotate(
>         angle: -1.5708, // Rotate 90 degrees counter-clockwise (in radians)
>         child: Container(
>           alignment: Alignment.centerLeft, // Align text to the left
>           child: Text(
>             languageData[value.toInt()].x,
>             style: style,
>           ),
>         ),
>       ),
>     );
>   }
> ```
> 
> ![image](https://private-user-images.githubusercontent.com/74815117/348528258-3521b66c-cae5-44ac-9fc5-40b77d886a40.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mjc5NjY1MzYsIm5iZiI6MTcyNzk2NjIzNiwicGF0aCI6Ii83NDgxNTExNy8zNDg1MjgyNTgtMzUyMWI2NmMtY2FlNS00NGFjLTlmYzUtNDBiNzdkODg2YTQwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDEwMDMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQxMDAzVDE0MzcxNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU4OWJmNGFiODZhN2U5MzZhM2UwZTVhYWU3MDliMmIyNGZlNThlNGEwZGUzMTI5YTY0NzExMGIyNjE1N2JjYzEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.jP5guqG3T_h94rQ2o4eSUmvvJzanfCRfEQYrnJl0ab0)

In your Container add the width
![image](https://github.com/user-attachments/assets/533a89b4-f034-4078-931d-34cf5f778794)
![image](https://github.com/user-attachments/assets/44289662-0a80-4478-9607-479628bee506)

Meanwhile, if you just need bar-chart

```
import 'package:flutter/material.dart';

class BarChartWidget extends StatelessWidget {
  final List<double> barData; // Data for bar lengths
  final List<String> yLabels; // Labels for the Y-axis
  final double maxValue; // Maximum value to scale the bars

  const BarChartWidget({super.key, 
    required this.barData,
    required this.yLabels,
    required this.maxValue,
  }) : assert(barData.length == yLabels.length,
            "Bar data and labels must have the same length");

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: List.generate(
          barData.length,
          (index) {
            double barWidth = (barData[index] / maxValue) * 300; // Bar scaling
            return Padding(
              padding: const EdgeInsets.symmetric(vertical: 4.0),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  // Y-axis label
                  SizedBox(
                    width: 150,
                    child: Text(
                      yLabels[index],
                      style: const TextStyle(fontSize: 14),
                    ),
                  ),
                  // Horizontal bar
                  Container(
                    height: 20,
                    width: barWidth,
                    color: Colors.blue,
                  ),
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}
```

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c5e1dba4..ff369f6df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## newVersion
 * **FEATURE** (by @Peetee06) Add `panEnabled` and `scaleEnabled` properties in the TransformationController, #1818
 * **FEATURE** (by @mitulagr2) Add `renderPriority` feature in our [ScatterSpot](https://github.com/imaNNeo/fl_chart/blob/main/repo_files/documentations/scatter_chart.md#scatterspot), #1545
+* **FEATURE** (by @imaNNeo) Add `rotationQuarterTurns` property in our Axis-Based charts (such as [LineChart](https://github.com/imaNNeo/fl_chart/blob/main/repo_files/documentations/line_chart.md), [BarChart](https://github.com/imaNNeo/fl_chart/blob/main/repo_files/documentations/bar_chart.md) and [ScatterChart](https://github.com/imaNNeo/fl_chart/blob/main/repo_files/documentations/scatter_chart.md)). It allows you to rotate the chart 90 degrees (clockwise) in each turn. For example you can have Horizontal Bar Charts by setting `rotationQuarterTurns` to 1 (which rotates the chart 90 degrees clockwise). It works exactly like [RotatesBox](https://api.flutter.dev/flutter/widgets/RotatedBox-class.html) widget, #113
 
 ## 0.70.0
 * **FEATURE** (by @Peetee06) Implemented a 5 years-old feature request about scroll and zoom support in our axis-based charts. Special thanks to @Peetee06 who made it happen, #71
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 1c2e0777e..1b1ad3e30 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -2,7 +2,6 @@ include: package:very_good_analysis/analysis_options.yaml
 analyzer:
   exclude:
     - "**.mocks.dart"
-    - "test/**"
 linter:
   rules:
     always_put_required_named_parameters_first: false
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample1.dart b/example/lib/presentation/samples/bar/bar_chart_sample1.dart
index 53a351b8c..9e404a565 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample1.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample1.dart
@@ -287,7 +287,7 @@ class BarChartSample1State extends State<BarChartSample1> {
         break;
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 16,
       child: text,
     );
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample2.dart b/example/lib/presentation/samples/bar/bar_chart_sample2.dart
index 272f9d61a..4edfd36d1 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample2.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample2.dart
@@ -190,7 +190,7 @@ class BarChartSample2State extends State<BarChartSample2> {
       return Container();
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 0,
       child: Text(text, style: style),
     );
@@ -209,7 +209,7 @@ class BarChartSample2State extends State<BarChartSample2> {
     );
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 16, //margin top
       child: text,
     );
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample3.dart b/example/lib/presentation/samples/bar/bar_chart_sample3.dart
index 5883a8f08..b06296e0c 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample3.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample3.dart
@@ -78,7 +78,7 @@ class _BarChart extends StatelessWidget {
         break;
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 4,
       child: Text(text, style: style),
     );
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample4.dart b/example/lib/presentation/samples/bar/bar_chart_sample4.dart
index ce2eaa9ba..56eda44b1 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample4.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample4.dart
@@ -39,7 +39,7 @@ class BarChartSample4State extends State<BarChartSample4> {
         break;
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(text, style: style),
     );
   }
@@ -52,7 +52,7 @@ class BarChartSample4State extends State<BarChartSample4> {
       fontSize: 10,
     );
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(
         meta.formattedValue,
         style: style,
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample5.dart b/example/lib/presentation/samples/bar/bar_chart_sample5.dart
index ece8c9e68..8603c546b 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample5.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample5.dart
@@ -59,7 +59,7 @@ class BarChartSample5State extends State<BarChartSample5> {
         break;
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(text, style: style),
     );
   }
@@ -93,7 +93,7 @@ class BarChartSample5State extends State<BarChartSample5> {
         return Container();
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(text, style: style),
     );
   }
@@ -108,7 +108,7 @@ class BarChartSample5State extends State<BarChartSample5> {
     }
     return SideTitleWidget(
       angle: AppUtils().degreeToRadian(value < 0 ? -45 : 45),
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 4,
       child: Text(
         text,
@@ -128,7 +128,7 @@ class BarChartSample5State extends State<BarChartSample5> {
     }
     return SideTitleWidget(
       angle: AppUtils().degreeToRadian(90),
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 0,
       child: Text(
         text,
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample6.dart b/example/lib/presentation/samples/bar/bar_chart_sample6.dart
index 7600b2d1b..3850f6177 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample6.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample6.dart
@@ -87,7 +87,7 @@ class BarChartSample6 extends StatelessWidget {
         text = '';
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(text, style: style),
     );
   }
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample7.dart b/example/lib/presentation/samples/bar/bar_chart_sample7.dart
index 1f5e090d5..bd92ba587 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample7.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample7.dart
@@ -48,120 +48,154 @@ class _BarChartSample7State extends State<BarChartSample7> {
 
   int touchedGroupIndex = -1;
 
+  int rotationTurns = 1;
+
   @override
   Widget build(BuildContext context) {
     return Padding(
       padding: const EdgeInsets.all(24),
-      child: AspectRatio(
-        aspectRatio: 1.4,
-        child: BarChart(
-          BarChartData(
-            alignment: BarChartAlignment.spaceBetween,
-            borderData: FlBorderData(
-              show: true,
-              border: Border.symmetric(
-                horizontal: BorderSide(
-                  color: AppColors.borderColor.withValues(alpha: 0.2),
-                ),
-              ),
-            ),
-            titlesData: FlTitlesData(
-              show: true,
-              leftTitles: AxisTitles(
-                drawBelowEverything: true,
-                sideTitles: SideTitles(
-                  showTitles: true,
-                  reservedSize: 30,
-                  getTitlesWidget: (value, meta) {
-                    return Text(
-                      value.toInt().toString(),
-                      textAlign: TextAlign.left,
-                    );
-                  },
+      child: Column(
+        children: [
+          Row(
+            children: [
+              Expanded(child: Container()),
+              const Text(
+                'Horizontal Bar Chart',
+                style: TextStyle(
+                  color: AppColors.mainTextColor1,
+                  fontSize: 20,
                 ),
               ),
-              bottomTitles: AxisTitles(
-                sideTitles: SideTitles(
-                  showTitles: true,
-                  reservedSize: 36,
-                  getTitlesWidget: (value, meta) {
-                    final index = value.toInt();
-                    return SideTitleWidget(
-                      axisSide: meta.axisSide,
-                      child: _IconWidget(
-                        color: widget.dataList[index].color,
-                        isSelected: touchedGroupIndex == index,
+              Expanded(
+                  child: Align(
+                alignment: Alignment.centerRight,
+                child: Tooltip(
+                  message: 'Rotate the chart 90 degrees (cw)',
+                  child: IconButton(
+                    onPressed: () {
+                      setState(() {
+                        rotationTurns += 1;
+                      });
+                    },
+                    icon: RotatedBox(
+                      quarterTurns: rotationTurns - 1,
+                      child: const Icon(
+                        Icons.rotate_90_degrees_cw,
                       ),
-                    );
-                  },
+                    ),
+                  ),
                 ),
-              ),
-              rightTitles: const AxisTitles(),
-              topTitles: const AxisTitles(),
-            ),
-            gridData: FlGridData(
-              show: true,
-              drawVerticalLine: false,
-              getDrawingHorizontalLine: (value) => FlLine(
-                color: AppColors.borderColor.withValues(alpha: 0.2),
-                strokeWidth: 1,
-              ),
-            ),
-            barGroups: widget.dataList.asMap().entries.map((e) {
-              final index = e.key;
-              final data = e.value;
-              return generateBarGroup(
-                index,
-                data.color,
-                data.value,
-                data.shadowValue,
-              );
-            }).toList(),
-            maxY: 20,
-            barTouchData: BarTouchData(
-              enabled: true,
-              handleBuiltInTouches: false,
-              touchTooltipData: BarTouchTooltipData(
-                getTooltipColor: (group) => Colors.transparent,
-                tooltipMargin: 0,
-                getTooltipItem: (
-                  BarChartGroupData group,
-                  int groupIndex,
-                  BarChartRodData rod,
-                  int rodIndex,
-                ) {
-                  return BarTooltipItem(
-                    rod.toY.toString(),
-                    TextStyle(
-                      fontWeight: FontWeight.bold,
-                      color: rod.color,
-                      fontSize: 18,
-                      shadows: const [
-                        Shadow(
-                          color: Colors.black26,
-                          blurRadius: 12,
-                        )
-                      ],
+              )),
+            ],
+          ),
+          const SizedBox(height: 18),
+          AspectRatio(
+            aspectRatio: 1.4,
+            child: BarChart(
+              BarChartData(
+                alignment: BarChartAlignment.spaceBetween,
+                rotationQuarterTurns: rotationTurns,
+                borderData: FlBorderData(
+                  show: true,
+                  border: Border.symmetric(
+                    horizontal: BorderSide(
+                      color: AppColors.borderColor.withValues(alpha: 0.2),
+                    ),
+                  ),
+                ),
+                titlesData: FlTitlesData(
+                  show: true,
+                  leftTitles: const AxisTitles(
+                    drawBelowEverything: true,
+                    sideTitles: SideTitles(
+                      showTitles: true,
+                      reservedSize: 30,
                     ),
+                  ),
+                  bottomTitles: AxisTitles(
+                    sideTitles: SideTitles(
+                      showTitles: true,
+                      reservedSize: 36,
+                      getTitlesWidget: (value, meta) {
+                        final index = value.toInt();
+                        return SideTitleWidget(
+                          meta: meta,
+                          child: _IconWidget(
+                            color: widget.dataList[index].color,
+                            isSelected: touchedGroupIndex == index,
+                          ),
+                        );
+                      },
+                    ),
+                  ),
+                  rightTitles: const AxisTitles(),
+                  topTitles: const AxisTitles(),
+                ),
+                gridData: FlGridData(
+                  show: true,
+                  drawVerticalLine: false,
+                  getDrawingHorizontalLine: (value) => FlLine(
+                    color: AppColors.borderColor.withValues(alpha: 0.2),
+                    strokeWidth: 1,
+                  ),
+                ),
+                barGroups: widget.dataList.asMap().entries.map((e) {
+                  final index = e.key;
+                  final data = e.value;
+                  return generateBarGroup(
+                    index,
+                    data.color,
+                    data.value,
+                    data.shadowValue,
                   );
-                },
+                }).toList(),
+                maxY: 20,
+                barTouchData: BarTouchData(
+                  enabled: true,
+                  handleBuiltInTouches: false,
+                  touchTooltipData: BarTouchTooltipData(
+                    getTooltipColor: (group) => Colors.transparent,
+                    tooltipMargin: 0,
+                    getTooltipItem: (
+                      BarChartGroupData group,
+                      int groupIndex,
+                      BarChartRodData rod,
+                      int rodIndex,
+                    ) {
+                      return BarTooltipItem(
+                        rod.toY.toString(),
+                        TextStyle(
+                          fontWeight: FontWeight.bold,
+                          color: rod.color,
+                          fontSize: 18,
+                          shadows: const [
+                            Shadow(
+                              color: Colors.black26,
+                              blurRadius: 12,
+                            )
+                          ],
+                        ),
+                      );
+                    },
+                  ),
+                  touchCallback: (event, response) {
+                    if (event.isInterestedForInteractions &&
+                        response != null &&
+                        response.spot != null) {
+                      setState(() {
+                        touchedGroupIndex = response.spot!.touchedBarGroupIndex;
+                      });
+                    } else {
+                      setState(() {
+                        touchedGroupIndex = -1;
+                      });
+                    }
+                  },
+                ),
               ),
-              touchCallback: (event, response) {
-                if (event.isInterestedForInteractions &&
-                    response != null &&
-                    response.spot != null) {
-                  setState(() {
-                    touchedGroupIndex = response.spot!.touchedBarGroupIndex;
-                  });
-                } else {
-                  setState(() {
-                    touchedGroupIndex = -1;
-                  });
-                }
-              },
             ),
           ),
-        ),
+        ],
       ),
     );
   }
@@ -169,6 +203,7 @@ class _BarChartSample7State extends State<BarChartSample7> {
 
 class _BarData {
   const _BarData(this.color, this.value, this.shadowValue);
+
   final Color color;
   final double value;
   final double shadowValue;
diff --git a/example/lib/presentation/samples/bar/bar_chart_sample8.dart b/example/lib/presentation/samples/bar/bar_chart_sample8.dart
index fc5e2a012..044b68070 100644
--- a/example/lib/presentation/samples/bar/bar_chart_sample8.dart
+++ b/example/lib/presentation/samples/bar/bar_chart_sample8.dart
@@ -91,7 +91,7 @@ class BarChartSample1State extends State<BarChartSample8> {
     );
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 16,
       child: text,
     );
diff --git a/example/lib/presentation/samples/line/line_chart_sample1.dart b/example/lib/presentation/samples/line/line_chart_sample1.dart
index ffb895bf2..f9ad583df 100644
--- a/example/lib/presentation/samples/line/line_chart_sample1.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample1.dart
@@ -119,7 +119,14 @@ class _LineChart extends StatelessWidget {
         return Container();
     }
 
-    return Text(text, style: style, textAlign: TextAlign.center);
+    return SideTitleWidget(
+      meta: meta,
+      child: Text(
+        text,
+        style: style,
+        textAlign: TextAlign.center,
+      ),
+    );
   }
 
   SideTitles leftTitles() => SideTitles(
@@ -151,7 +158,7 @@ class _LineChart extends StatelessWidget {
     }
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 10,
       child: text,
     );
diff --git a/example/lib/presentation/samples/line/line_chart_sample12.dart b/example/lib/presentation/samples/line/line_chart_sample12.dart
index fc9ae0fad..a87052e0f 100644
--- a/example/lib/presentation/samples/line/line_chart_sample12.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample12.dart
@@ -241,7 +241,7 @@ class _LineChartSample12State extends State<LineChartSample12> {
                       getTitlesWidget: (double value, TitleMeta meta) {
                         final date = _bitcoinPriceHistory![value.toInt()].$1;
                         return SideTitleWidget(
-                          axisSide: meta.axisSide,
+                          meta: meta,
                           child: Transform.rotate(
                             angle: -45 * 3.14 / 180,
                             child: Text(
diff --git a/example/lib/presentation/samples/line/line_chart_sample2.dart b/example/lib/presentation/samples/line/line_chart_sample2.dart
index 5c100e6be..a22e2b3de 100644
--- a/example/lib/presentation/samples/line/line_chart_sample2.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample2.dart
@@ -81,7 +81,7 @@ class _LineChartSample2State extends State<LineChartSample2> {
     }
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: text,
     );
   }
diff --git a/example/lib/presentation/samples/line/line_chart_sample3.dart b/example/lib/presentation/samples/line/line_chart_sample3.dart
index 9adc24c0b..f79dbf915 100644
--- a/example/lib/presentation/samples/line/line_chart_sample3.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample3.dart
@@ -92,7 +92,7 @@ class _LineChartSample3State extends State<LineChartSample3> {
     }
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 6,
       fitInside: fitInsideLeftTitle
           ? SideTitleFitInsideData.fromTitleMeta(meta)
@@ -113,7 +113,7 @@ class _LineChartSample3State extends State<LineChartSample3> {
     }
     return SideTitleWidget(
       space: 4,
-      axisSide: meta.axisSide,
+      meta: meta,
       fitInside: fitInsideBottomTitle
           ? SideTitleFitInsideData.fromTitleMeta(meta, distanceFromEdge: 0)
           : SideTitleFitInsideData.disable(),
diff --git a/example/lib/presentation/samples/line/line_chart_sample4.dart b/example/lib/presentation/samples/line/line_chart_sample4.dart
index 71d55f265..b03ddd5e0 100644
--- a/example/lib/presentation/samples/line/line_chart_sample4.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample4.dart
@@ -63,7 +63,7 @@ class LineChartSample4 extends StatelessWidget {
     }
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 4,
       child: Text(
         text,
@@ -82,7 +82,7 @@ class LineChartSample4 extends StatelessWidget {
       fontSize: 12,
     );
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text('\$ ${value + 0.5}', style: style),
     );
   }
diff --git a/example/lib/presentation/samples/line/line_chart_sample5.dart b/example/lib/presentation/samples/line/line_chart_sample5.dart
index 26ee32c21..3385f59c5 100644
--- a/example/lib/presentation/samples/line/line_chart_sample5.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample5.dart
@@ -71,7 +71,7 @@ class _LineChartSample5State extends State<LineChartSample5> {
     }
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(text, style: style),
     );
   }
diff --git a/example/lib/presentation/samples/line/line_chart_sample6.dart b/example/lib/presentation/samples/line/line_chart_sample6.dart
index 149f3b4ba..ebd028509 100644
--- a/example/lib/presentation/samples/line/line_chart_sample6.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample6.dart
@@ -110,7 +110,7 @@ class LineChartSample6 extends StatelessWidget {
       color: AppColors.mainTextColor2,
     );
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(value.toInt().toString(), style: style),
     );
   }
diff --git a/example/lib/presentation/samples/line/line_chart_sample7.dart b/example/lib/presentation/samples/line/line_chart_sample7.dart
index 81a3e53fe..fa10b884e 100644
--- a/example/lib/presentation/samples/line/line_chart_sample7.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample7.dart
@@ -65,7 +65,7 @@ class LineChartSample7 extends StatelessWidget {
     }
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 4,
       child: Text(text, style: style),
     );
@@ -75,7 +75,7 @@ class LineChartSample7 extends StatelessWidget {
     const style = TextStyle(fontSize: 10);
 
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(
         '\$ ${value + 0.5}',
         style: style,
diff --git a/example/lib/presentation/samples/line/line_chart_sample8.dart b/example/lib/presentation/samples/line/line_chart_sample8.dart
index 9d08288b2..ee383a5d1 100644
--- a/example/lib/presentation/samples/line/line_chart_sample8.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample8.dart
@@ -77,7 +77,7 @@ class _LineChartSample8State extends State<LineChartSample8> {
       color: AppColors.mainTextColor1,
     );
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Text(meta.formattedValue, style: style),
     );
   }
@@ -106,7 +106,7 @@ class _LineChartSample8State extends State<LineChartSample8> {
         throw StateError('Invalid');
     }
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       child: Icon(
         icon,
         color: color,
diff --git a/example/lib/presentation/samples/line/line_chart_sample9.dart b/example/lib/presentation/samples/line/line_chart_sample9.dart
index d1c68f704..7143fbd28 100644
--- a/example/lib/presentation/samples/line/line_chart_sample9.dart
+++ b/example/lib/presentation/samples/line/line_chart_sample9.dart
@@ -22,7 +22,7 @@ class LineChartSample9 extends StatelessWidget {
       fontSize: min(18, 18 * chartWidth / 300),
     );
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 16,
       child: Text(meta.formattedValue, style: style),
     );
@@ -35,7 +35,7 @@ class LineChartSample9 extends StatelessWidget {
       fontSize: min(18, 18 * chartWidth / 300),
     );
     return SideTitleWidget(
-      axisSide: meta.axisSide,
+      meta: meta,
       space: 16,
       child: Text(meta.formattedValue, style: style),
     );
diff --git a/lib/src/chart/bar_chart/bar_chart_data.dart b/lib/src/chart/bar_chart/bar_chart_data.dart
index aefafb2dc..eadf35ae4 100644
--- a/lib/src/chart/bar_chart/bar_chart_data.dart
+++ b/lib/src/chart/bar_chart/bar_chart_data.dart
@@ -47,6 +47,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
     RangeAnnotations? rangeAnnotations,
     super.backgroundColor,
     ExtraLinesData? extraLinesData,
+    super.rotationQuarterTurns,
   })  : barGroups = barGroups ?? const [],
         groupsSpace = groupsSpace ?? 16,
         alignment = alignment ?? BarChartAlignment.spaceEvenly,
@@ -94,6 +95,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
     double? baselineY,
     Color? backgroundColor,
     ExtraLinesData? extraLinesData,
+    int? rotationQuarterTurns,
   }) =>
       BarChartData(
         barGroups: barGroups ?? this.barGroups,
@@ -109,6 +111,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
         baselineY: baselineY ?? this.baselineY,
         backgroundColor: backgroundColor ?? this.backgroundColor,
         extraLinesData: extraLinesData ?? this.extraLinesData,
+        rotationQuarterTurns: rotationQuarterTurns ?? this.rotationQuarterTurns,
       );
 
   /// Lerps a [BaseChartData] based on [t] value, check [Tween.lerp].
@@ -131,6 +134,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
         backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
         extraLinesData:
             ExtraLinesData.lerp(a.extraLinesData, b.extraLinesData, t),
+        rotationQuarterTurns: b.rotationQuarterTurns,
       );
     } else {
       throw Exception('Illegal State');
@@ -153,6 +157,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
         rangeAnnotations,
         backgroundColor,
         extraLinesData,
+        rotationQuarterTurns,
       ];
 }
 
@@ -750,7 +755,7 @@ class BarTouchTooltipData with EquatableMixin {
   /// Controls showing tooltip on top or bottom, default is auto.
   final TooltipDirection direction;
 
-  /// Controls the rotation of the tooltip.
+  /// Controls the rotation of the tooltip (in degrees)
   final double rotateAngle;
 
   /// The tooltip border color.
diff --git a/lib/src/chart/bar_chart/bar_chart_painter.dart b/lib/src/chart/bar_chart/bar_chart_painter.dart
index 355abd43d..1a2e8dbe8 100644
--- a/lib/src/chart/bar_chart/bar_chart_painter.dart
+++ b/lib/src/chart/bar_chart/bar_chart_painter.dart
@@ -536,11 +536,12 @@ class BarChartPainter extends AxisChartPainter<BarChartData> {
         ..strokeWidth = tooltipData.tooltipBorder.width;
     }
 
+    final reverseQuarterTurnsAngle = -holder.data.rotationQuarterTurns * 90;
     canvasWrapper.drawRotated(
       size: rect.size,
       rotationOffset: rectRotationOffset,
       drawOffset: rectDrawOffset,
-      angle: rotateAngle,
+      angle: reverseQuarterTurnsAngle + rotateAngle,
       drawCallback: () {
         canvasWrapper
           ..drawRRect(roundedRect, _bgTouchTooltipPaint)
diff --git a/lib/src/chart/base/axis_chart/axis_chart_data.dart b/lib/src/chart/base/axis_chart/axis_chart_data.dart
index 32b05f74e..07be42036 100644
--- a/lib/src/chart/base/axis_chart/axis_chart_data.dart
+++ b/lib/src/chart/base/axis_chart/axis_chart_data.dart
@@ -29,6 +29,7 @@ abstract class AxisChartData extends BaseChartData with EquatableMixin {
     super.borderData,
     required super.touchData,
     ExtraLinesData? extraLinesData,
+    this.rotationQuarterTurns = 0,
   })  : gridData = gridData ?? const FlGridData(),
         rangeAnnotations = rangeAnnotations ?? const RangeAnnotations(),
         baselineX = baselineX ?? 0,
@@ -62,6 +63,9 @@ abstract class AxisChartData extends BaseChartData with EquatableMixin {
   /// Extra horizontal or vertical lines to draw on the chart.
   final ExtraLinesData extraLinesData;
 
+  /// Rotates the chart by 90 degrees clockwise in each turn
+  final int rotationQuarterTurns;
+
   /// Used for equality check, see [EquatableMixin].
   @override
   List<Object?> get props => [
@@ -79,11 +83,22 @@ abstract class AxisChartData extends BaseChartData with EquatableMixin {
         borderData,
         touchData,
         extraLinesData,
+        rotationQuarterTurns,
       ];
 }
 
 /// Represents a side of the chart
-enum AxisSide { left, top, right, bottom }
+enum AxisSide {
+  left,
+  top,
+  right,
+  bottom;
+
+  AxisSide rotateByQuarterTurns(int quarterTurns) {
+    const values = AxisSide.values;
+    return values[(values.indexOf(this) + quarterTurns) % values.length];
+  }
+}
 
 /// Contains meta information about the drawing title.
 class TitleMeta {
@@ -96,7 +111,11 @@ class TitleMeta {
     required this.sideTitles,
     required this.formattedValue,
     required this.axisSide,
-  });
+    required this.rotationQuarterTurns,
+  }) : assert(
+          rotationQuarterTurns >= 0,
+          "TitleMeta.rotationQuarterTurns couldn't be negative",
+        );
 
   /// min axis value
   final double min;
@@ -122,6 +141,11 @@ class TitleMeta {
 
   /// Determines the axis side of titles (left, top, right, bottom)
   final AxisSide axisSide;
+
+  /// Chart is rotated by 90 degrees clockwise in each turn
+  ///
+  /// default is zero, which means chart is normal and upward
+  final int rotationQuarterTurns;
 }
 
 /// It gives you the axis value and gets a String value based on it.
@@ -132,7 +156,7 @@ typedef GetTitleWidgetFunction = Widget Function(double value, TitleMeta meta);
 /// formats the axis number to a shorter string using [formatNumber].
 Widget defaultGetTitle(double value, TitleMeta meta) {
   return SideTitleWidget(
-    axisSide: meta.axisSide,
+    meta: meta,
     child: Text(
       meta.formattedValue,
     ),
@@ -170,6 +194,11 @@ class SideTitles with EquatableMixin {
 
   /// You can override it to pass your custom widget to show in each axis value
   /// We recommend you to use [SideTitleWidget].
+  ///
+  /// If you decide to implement your custom widget
+  /// (instead of [SideTitleWidget]), you have to take care of the alignment,
+  /// space to the chart and also the rotation (if you are rotating the chart,
+  /// for example for Horizontal Bar Chart)
   final GetTitleWidgetFunction getTitlesWidget;
 
   /// It determines the maximum space that your titles need,
diff --git a/lib/src/chart/base/axis_chart/axis_chart_scaffold_widget.dart b/lib/src/chart/base/axis_chart/axis_chart_scaffold_widget.dart
index 44492a483..7774dd205 100644
--- a/lib/src/chart/base/axis_chart/axis_chart_scaffold_widget.dart
+++ b/lib/src/chart/base/axis_chart/axis_chart_scaffold_widget.dart
@@ -316,7 +316,12 @@ class _AxisChartScaffoldWidgetState extends State<AxisChartScaffoldWidget> {
   Widget build(BuildContext context) {
     return LayoutBuilder(
       builder: (context, constraints) {
-        return Stack(children: stackWidgets(constraints));
+        return RotatedBox(
+          quarterTurns: widget.data.rotationQuarterTurns,
+          child: Stack(
+            children: stackWidgets(constraints),
+          ),
+        );
       },
     );
   }
diff --git a/lib/src/chart/base/axis_chart/axis_chart_widgets.dart b/lib/src/chart/base/axis_chart/axis_chart_widgets.dart
index b9a010b78..424fe4d41 100644
--- a/lib/src/chart/base/axis_chart/axis_chart_widgets.dart
+++ b/lib/src/chart/base/axis_chart/axis_chart_widgets.dart
@@ -16,7 +16,7 @@ class SideTitleWidget extends StatefulWidget {
   const SideTitleWidget({
     super.key,
     required this.child,
-    required this.axisSide,
+    required this.meta,
     this.space = 8.0,
     this.angle = 0.0,
     this.fitInside = const SideTitleFitInsideData(
@@ -27,7 +27,7 @@ class SideTitleWidget extends StatefulWidget {
     ),
   });
 
-  final AxisSide axisSide;
+  final TitleMeta meta;
   final double space;
   final Widget child;
   final double angle;
@@ -53,14 +53,14 @@ class SideTitleWidget extends StatefulWidget {
 }
 
 class _SideTitleWidgetState extends State<SideTitleWidget> {
-  Alignment _getAlignment() => switch (widget.axisSide) {
+  Alignment _getAlignment() => switch (widget.meta.axisSide) {
         AxisSide.left => Alignment.centerRight,
         AxisSide.top => Alignment.bottomCenter,
         AxisSide.right => Alignment.centerLeft,
         AxisSide.bottom => Alignment.topCenter,
       };
 
-  EdgeInsets _getMargin() => switch (widget.axisSide) {
+  EdgeInsets _getMargin() => switch (widget.meta.axisSide) {
         AxisSide.left => EdgeInsets.only(right: widget.space),
         AxisSide.top => EdgeInsets.only(bottom: widget.space),
         AxisSide.right => EdgeInsets.only(left: widget.space),
@@ -82,7 +82,7 @@ class _SideTitleWidgetState extends State<SideTitleWidget> {
     if (context == null) return;
 
     // Set size based on its axis side
-    final size = switch (widget.axisSide) {
+    final size = switch (widget.meta.axisSide) {
       AxisSide.left || AxisSide.right => context.size?.height ?? 0,
       AxisSide.top || AxisSide.bottom => context.size?.width ?? 0,
     };
@@ -111,7 +111,7 @@ class _SideTitleWidgetState extends State<SideTitleWidget> {
       offset: !widget.fitInside.enabled
           ? Offset.zero
           : AxisChartHelper().calcFitInsideOffset(
-              axisSide: widget.axisSide,
+              axisSide: widget.meta.axisSide,
               childSize: _childSize,
               parentAxisSize: widget.fitInside.parentAxisSize,
               axisPosition: widget.fitInside.axisPosition,
@@ -123,7 +123,10 @@ class _SideTitleWidgetState extends State<SideTitleWidget> {
           key: widgetKey,
           margin: _getMargin(),
           alignment: _getAlignment(),
-          child: widget.child,
+          child: RotatedBox(
+            quarterTurns: -widget.meta.rotationQuarterTurns,
+            child: widget.child,
+          ),
         ),
       ),
     );
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 2a134e9bd..d0f67603f 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
@@ -5,6 +5,7 @@ import 'package:fl_chart/src/extensions/bar_chart_data_extension.dart';
 import 'package:fl_chart/src/extensions/edge_insets_extension.dart';
 import 'package:fl_chart/src/extensions/fl_border_data_extension.dart';
 import 'package:fl_chart/src/extensions/fl_titles_data_extension.dart';
+import 'package:fl_chart/src/extensions/size_extension.dart';
 import 'package:fl_chart/src/utils/utils.dart';
 import 'package:flutter/material.dart';
 
@@ -105,13 +106,18 @@ class _SideTitlesWidgetState extends State<SideTitlesWidget> {
   }
 
   Size get viewSize {
+    late Size size;
     final chartVirtualRect = widget.chartVirtualRect;
     if (chartVirtualRect == null) {
-      return widget.parentSize;
+      size = widget.parentSize;
+    } else {
+      size = chartVirtualRect.size +
+          Offset(thisSidePaddingTotal, thisSidePaddingTotal);
     }
 
-    return chartVirtualRect.size +
-        Offset(thisSidePaddingTotal, thisSidePaddingTotal);
+    return size.rotateByQuarterTurns(
+      widget.axisChartData.rotationQuarterTurns,
+    );
   }
 
   double get axisOffset {
@@ -195,6 +201,7 @@ class _SideTitlesWidgetState extends State<SideTitlesWidget> {
               axisSide: side,
               parentAxisSize: axisViewSize,
               axisPosition: metaData.axisPixelLocation,
+              rotationQuarterTurns: widget.axisChartData.rotationQuarterTurns,
             ),
           ),
         );
@@ -209,7 +216,7 @@ class _SideTitlesWidgetState extends State<SideTitlesWidget> {
     final chartSize = Size(
       widget.parentSize.width - thisSidePaddingTotal,
       widget.parentSize.height - thisSidePaddingTotal,
-    );
+    ).rotateByQuarterTurns(widget.axisChartData.rotationQuarterTurns);
     // Add 1 pixel to the chart's edges to avoid clipping the last title.
     final chartRect = (Offset.zero & chartSize).inflate(1);
 
diff --git a/lib/src/chart/line_chart/line_chart_data.dart b/lib/src/chart/line_chart/line_chart_data.dart
index 75417fda1..b32df77d3 100644
--- a/lib/src/chart/line_chart/line_chart_data.dart
+++ b/lib/src/chart/line_chart/line_chart_data.dart
@@ -58,6 +58,7 @@ class LineChartData extends AxisChartData with EquatableMixin {
     super.baselineY,
     super.clipData = const FlClipData.none(),
     super.backgroundColor,
+    super.rotationQuarterTurns,
   }) : super(
           touchData: lineTouchData,
           minX: minX ?? double.nan,
@@ -109,6 +110,7 @@ class LineChartData extends AxisChartData with EquatableMixin {
             lerpBetweenBarsDataList(a.betweenBarsData, b.betweenBarsData, t)!,
         lineTouchData: b.lineTouchData,
         showingTooltipIndicators: b.showingTooltipIndicators,
+        rotationQuarterTurns: b.rotationQuarterTurns,
       );
     } else {
       throw Exception('Illegal State');
@@ -135,6 +137,7 @@ class LineChartData extends AxisChartData with EquatableMixin {
     double? baselineY,
     FlClipData? clipData,
     Color? backgroundColor,
+    int? rotationQuarterTurns,
   }) =>
       LineChartData(
         lineBarsData: lineBarsData ?? this.lineBarsData,
@@ -155,6 +158,7 @@ class LineChartData extends AxisChartData with EquatableMixin {
         baselineY: baselineY ?? this.baselineY,
         clipData: clipData ?? this.clipData,
         backgroundColor: backgroundColor ?? this.backgroundColor,
+        rotationQuarterTurns: rotationQuarterTurns ?? this.rotationQuarterTurns,
       );
 
   /// Used for equality check, see [EquatableMixin].
@@ -177,6 +181,7 @@ class LineChartData extends AxisChartData with EquatableMixin {
         baselineY,
         clipData,
         backgroundColor,
+        rotationQuarterTurns,
       ];
 }
 
diff --git a/lib/src/chart/line_chart/line_chart_painter.dart b/lib/src/chart/line_chart/line_chart_painter.dart
index 74782a0a8..c017d353d 100644
--- a/lib/src/chart/line_chart/line_chart_painter.dart
+++ b/lib/src/chart/line_chart/line_chart_painter.dart
@@ -1018,7 +1018,10 @@ class LineChartPainter extends AxisChartPainter<LineChartData> {
     }
 
     for (var i = 0; i < showingTooltipSpots.showingSpots.length; i++) {
-      final tooltipItem = tooltipItems[i];
+      var tooltipItem = tooltipItems[i];
+      if (holder.data.rotationQuarterTurns % 4 == 2) {
+        tooltipItem = tooltipItems[tooltipItems.length - 1 - i];
+      }
       if (tooltipItem == null) {
         continue;
       }
@@ -1175,12 +1178,12 @@ class LineChartPainter extends AxisChartPainter<LineChartData> {
         ..color = tooltipData.tooltipBorder.color
         ..strokeWidth = tooltipData.tooltipBorder.width;
     }
-
+    final reverseQuarterTurnsAngle = -holder.data.rotationQuarterTurns * 90;
     canvasWrapper.drawRotated(
       size: rect.size,
       rotationOffset: rectRotationOffset,
       drawOffset: rectDrawOffset,
-      angle: rotateAngle,
+      angle: reverseQuarterTurnsAngle + rotateAngle,
       drawCallback: () {
         canvasWrapper
           ..drawRRect(roundedRect, _bgTouchTooltipPaint)
@@ -1209,11 +1212,12 @@ class LineChartPainter extends AxisChartPainter<LineChartData> {
         yOffset,
       );
 
+      final reverseQuarterTurnsAngle = -holder.data.rotationQuarterTurns * 90;
       canvasWrapper.drawRotated(
         size: rect.size,
         rotationOffset: rectRotationOffset,
         drawOffset: rectDrawOffset,
-        angle: rotateAngle,
+        angle: reverseQuarterTurnsAngle + rotateAngle,
         drawCallback: () {
           canvasWrapper.drawText(tp, drawOffset);
         },
diff --git a/lib/src/chart/scatter_chart/scatter_chart_data.dart b/lib/src/chart/scatter_chart/scatter_chart_data.dart
index 63f035bfd..64f0704eb 100644
--- a/lib/src/chart/scatter_chart/scatter_chart_data.dart
+++ b/lib/src/chart/scatter_chart/scatter_chart_data.dart
@@ -49,6 +49,7 @@ class ScatterChartData extends AxisChartData with EquatableMixin {
     FlClipData? clipData,
     super.backgroundColor,
     ScatterLabelSettings? scatterLabelSettings,
+    super.rotationQuarterTurns,
   })  : scatterSpots = scatterSpots ?? const [],
         scatterTouchData = scatterTouchData ?? ScatterTouchData(),
         showingTooltipIndicators = showingTooltipIndicators ?? const [],
@@ -116,6 +117,7 @@ class ScatterChartData extends AxisChartData with EquatableMixin {
           b.scatterLabelSettings,
           t,
         ),
+        rotationQuarterTurns: b.rotationQuarterTurns,
       );
     } else {
       throw Exception('Illegal State');
@@ -140,6 +142,7 @@ class ScatterChartData extends AxisChartData with EquatableMixin {
     FlClipData? clipData,
     Color? backgroundColor,
     ScatterLabelSettings? scatterLabelSettings,
+    int? rotationQuarterTurns,
   }) =>
       ScatterChartData(
         scatterSpots: scatterSpots ?? this.scatterSpots,
@@ -158,6 +161,7 @@ class ScatterChartData extends AxisChartData with EquatableMixin {
         clipData: clipData ?? this.clipData,
         backgroundColor: backgroundColor ?? this.backgroundColor,
         scatterLabelSettings: scatterLabelSettings ?? this.scatterLabelSettings,
+        rotationQuarterTurns: rotationQuarterTurns ?? this.rotationQuarterTurns,
       );
 
   /// Used for equality check, see [EquatableMixin].
@@ -181,6 +185,7 @@ class ScatterChartData extends AxisChartData with EquatableMixin {
         backgroundColor,
         borderData,
         touchData,
+        rotationQuarterTurns,
       ];
 }
 
@@ -245,7 +250,8 @@ class ScatterSpot extends FlSpot with EquatableMixin {
         lerpDouble(a.x, b.x, t)!,
         lerpDouble(a.y, b.y, t)!,
         show: b.show,
-        renderPriority: a.renderPriority + (t * (b.renderPriority - a.renderPriority)).round(),
+        renderPriority: a.renderPriority +
+            (t * (b.renderPriority - a.renderPriority)).round(),
         dotPainter: a.dotPainter.lerp(a.dotPainter, b.dotPainter, t),
       );
 
diff --git a/lib/src/chart/scatter_chart/scatter_chart_painter.dart b/lib/src/chart/scatter_chart/scatter_chart_painter.dart
index f4fa5ef23..0cbb0cb81 100644
--- a/lib/src/chart/scatter_chart/scatter_chart_painter.dart
+++ b/lib/src/chart/scatter_chart/scatter_chart_painter.dart
@@ -360,11 +360,12 @@ class ScatterChartPainter extends AxisChartPainter<ScatterChartData> {
         ..strokeWidth = tooltipData.tooltipBorder.width;
     }
 
+    final reverseQuarterTurnsAngle = -holder.data.rotationQuarterTurns * 90;
     canvasWrapper.drawRotated(
       size: rect.size,
       rotationOffset: rectRotationOffset,
       drawOffset: rectDrawOffset,
-      angle: rotateAngle,
+      angle: reverseQuarterTurnsAngle + rotateAngle,
       drawCallback: () {
         canvasWrapper
           ..drawRRect(roundedRect, _bgTouchTooltipPaint)
diff --git a/lib/src/extensions/size_extension.dart b/lib/src/extensions/size_extension.dart
new file mode 100644
index 000000000..f1ec2769c
--- /dev/null
+++ b/lib/src/extensions/size_extension.dart
@@ -0,0 +1,13 @@
+import 'dart:ui';
+
+extension SizeExtension on Size {
+  Size rotateByQuarterTurns(int quarterTurns) {
+    if (quarterTurns < 0) {
+      throw ArgumentError('quarterTurns must be greater than or equal to 0.');
+    }
+    return switch (quarterTurns % 4) {
+      0 || 2 => this,
+      _ /*2 || 3*/ => Size(height, width),
+    };
+  }
+}
diff --git a/repo_files/documentations/bar_chart.md b/repo_files/documentations/bar_chart.md
index 3c81f4673..a152eb028 100644
--- a/repo_files/documentations/bar_chart.md
+++ b/repo_files/documentations/bar_chart.md
@@ -31,7 +31,7 @@ When you change the chart's state, it animates to the new state internally (usin
 |minY| gets minimum y of y axis, if null, value will be read from the input barGroups (But it is more performant if you provide them) | null|
 |baselineY| defines the baseline of y-axis | 0|
 |extraLinesData| allows extra horizontal lines to be drawn on the chart. Vertical lines are ignored when used with BarChartData, please see [#1149](https://github.com/imaNNeo/fl_chart/issues/1149), check [ExtraLinesData](base_chart.md#ExtraLinesData)|ExtraLinesData()|
-
+|rotationQuarterTurns|Rotates the chart 90 degrees (clockwise) in every quarter turns. This feature works like the [RotatedBox](https://api.flutter.dev/flutter/widgets/RotatedBox-class.html) widget. You can have horizontal BarChart by changing this value to |0|
 
 ### BarChartGroupData
 |PropName		|Description	|default value|
diff --git a/repo_files/documentations/line_chart.md b/repo_files/documentations/line_chart.md
index 92acac127..95e2bdfbc 100644
--- a/repo_files/documentations/line_chart.md
+++ b/repo_files/documentations/line_chart.md
@@ -34,6 +34,8 @@ When you change the chart's state, it animates to the new state internally (usin
 |baselineY| defines the baseline of y-axis | 0|
 |clipData| clip the chart to the border (prevent drawing outside the border) | FlClipData.none()|
 |backgroundColor| a background color which is drawn behind th chart| null |
+|rotationQuarterTurns|Rotates the chart 90 degrees (clockwise) in every quarter turns. This feature works like the [RotatedBox](https://api.flutter.dev/flutter/widgets/RotatedBox-class.html) widget|0|
+
 
 
 ### LineChartBarData
diff --git a/repo_files/documentations/scatter_chart.md b/repo_files/documentations/scatter_chart.md
index 87b7c12fd..84e5355c9 100644
--- a/repo_files/documentations/scatter_chart.md
+++ b/repo_files/documentations/scatter_chart.md
@@ -24,7 +24,7 @@ When you change the chart's state, it animates to the new state internally (usin
 |axisTitleData| check the [FlAxisTitleData](base_chart.md#FlAxisTitleData)| FlAxisTitleData()|
 |scatterTouchData| [ScatterTouchData](#scattertouchdata-read-about-touch-handling) holds the touch interactivity details| ScatterTouchData()|
 |showingTooltipIndicators| indices of showing tooltip, The point is that you need to disable touches to show these tooltips manually|[]|
-
+|rotationQuarterTurns|Rotates the chart 90 degrees (clockwise) in every quarter turns. This feature works like the [RotatedBox](https://api.flutter.dev/flutter/widgets/RotatedBox-class.html) widget|0|
 
 
 ### ScatterSpot


Test patch:
diff --git a/test/chart/bar_chart/bar_chart_test.dart b/test/chart/bar_chart/bar_chart_test.dart
index 13aca5c42..7087721a4 100644
--- a/test/chart/bar_chart/bar_chart_test.dart
+++ b/test/chart/bar_chart/bar_chart_test.dart
@@ -75,8 +75,10 @@ void main() {
             createTestWidget(
               chart: BarChart(
                 BarChartData(alignment: alignment),
+                // This is for test
                 // ignore: avoid_redundant_argument_values
                 transformationConfig: const FlTransformationConfig(
+                  // This is for test
                   // ignore: avoid_redundant_argument_values
                   scaleAxis: FlScaleAxis.none,
                 ),
@@ -228,8 +230,10 @@ void main() {
         createTestWidget(
           chart: BarChart(
             BarChartData(),
+            // This is for test
             // ignore: avoid_redundant_argument_values
             transformationConfig: const FlTransformationConfig(
+              // This is for test
               // ignore: avoid_redundant_argument_values
               scaleAxis: FlScaleAxis.none,
             ),
@@ -756,6 +760,7 @@ void main() {
               chart: BarChart(
                 BarChartData(),
                 transformationConfig: const FlTransformationConfig(
+                  // This is for test
                   // ignore: avoid_redundant_argument_values
                   scaleAxis: FlScaleAxis.none,
                   trackpadScrollCausesScale: true,
@@ -791,6 +796,7 @@ void main() {
                   BarChartData(),
                   transformationConfig: FlTransformationConfig(
                     scaleAxis: scaleAxis,
+                    // This is for test
                     // ignore: avoid_redundant_argument_values
                     trackpadScrollCausesScale: false,
                   ),
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
index cf1bf6126..99a1ff5c7 100644
--- a/test/chart/base/axis_chart/axis_chart_scaffold_widget_test.dart
+++ b/test/chart/base/axis_chart/axis_chart_scaffold_widget_test.dart
@@ -2,6 +2,7 @@ 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:fl_chart/src/chart/base/custom_interactive_viewer.dart';
+import 'package:fl_chart/src/extensions/size_extension.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -366,6 +367,48 @@ void main() {
     },
   );
 
+  testWidgets(
+    'LineChart with rotationQuarterTurns',
+    (WidgetTester tester) async {
+      for (var rotationTurns = 0; rotationTurns <= 8; rotationTurns++) {
+        Size? chartDrawingSize;
+        await tester.pumpWidget(
+          MaterialApp(
+            home: Scaffold(
+              body: Center(
+                child: SizedBox(
+                  width: 400,
+                  height: 200,
+                  child: AxisChartScaffoldWidget(
+                    chartBuilder: (context, chartVirtualRect) => LayoutBuilder(
+                      builder: (context, constraints) {
+                        chartDrawingSize = constraints.biggest;
+                        return const ColoredBox(
+                          color: Colors.red,
+                        );
+                      },
+                    ),
+                    data: lineChartDataWithNoTitles.copyWith(
+                      rotationQuarterTurns: rotationTurns,
+                    ),
+                  ),
+                ),
+              ),
+            ),
+          ),
+        );
+        expect(
+          chartDrawingSize,
+          const Size(400, 200).rotateByQuarterTurns(rotationTurns),
+        );
+        final types = find.byType(RotatedBox);
+        final rotatedBox = tester.widget<RotatedBox>(types);
+        expect(rotatedBox.quarterTurns, rotationTurns);
+        expect(types, findsOne);
+      }
+    },
+  );
+
   group('AxisChartScaffoldWidget', () {
     for (final scaleAxis in FlScaleAxis.scalingEnabledAxis) {
       testWidgets(
@@ -412,8 +455,10 @@ void main() {
                   height: viewSize.height,
                   child: AxisChartScaffoldWidget(
                     data: lineChartDataWithAllTitles,
+                    // This is for test
                     // ignore: avoid_redundant_argument_values
                     transformationConfig: const FlTransformationConfig(
+                      // This is for test
                       // ignore: avoid_redundant_argument_values
                       scaleAxis: FlScaleAxis.none,
                     ),
@@ -1245,8 +1290,10 @@ void main() {
                 height: 300,
                 child: LineChart(
                   lineChartDataWithNoTitles,
+                  // This is for test
                   // ignore: avoid_redundant_argument_values
                   transformationConfig: const FlTransformationConfig(
+                    // This is for test
                     // ignore: avoid_redundant_argument_values
                     scaleAxis: FlScaleAxis.none,
                   ),
@@ -1309,26 +1356,26 @@ void main() {
         'oldWidget.controller is null and widget.controller is null: '
         'keeps old controller',
         (WidgetTester tester) async {
-          final actualchartVirtualRects = <Object?>[isNotScaled, isNotScaled];
+          final actualChartVirtualRects = <Object?>[isNotScaled, isNotScaled];
           await tester.pumpWidget(createTestWidget());
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects);
+          expect(chartVirtualRects, actualChartVirtualRects);
 
           final transformationController = getTransformationController(tester);
           transformationController!.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           await tester.pumpWidget(createTestWidget());
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           final transformationController2 = getTransformationController(tester);
           expect(transformationController2, transformationController);
           transformationController2!.value = Matrix4.identity()..scale(3.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
         },
       );
 
@@ -1336,24 +1383,24 @@ void main() {
         'oldWidget.controller is null and widget.controller is not null: '
         'disposes old controller and sets up widget.controller with listeners',
         (WidgetTester tester) async {
-          final actualchartVirtualRects = <Object?>[isNotScaled, isNotScaled];
+          final actualChartVirtualRects = <Object?>[isNotScaled, isNotScaled];
           await tester.pumpWidget(createTestWidget());
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects);
+          expect(chartVirtualRects, actualChartVirtualRects);
 
           final transformationController = getTransformationController(tester);
           transformationController!.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           final transformationController2 = TransformationController();
 
           await tester.pumpWidget(
             createTestWidget(controller: transformationController2),
           );
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isNotScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isNotScaled));
 
           expect(transformationController2, isNot(transformationController));
           expect(
@@ -1362,7 +1409,7 @@ void main() {
           );
           transformationController2.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
         },
       );
 
@@ -1371,31 +1418,32 @@ void main() {
         'removes listeners from old controller and sets up new controller '
         'with listeners',
         (WidgetTester tester) async {
-          final actualchartVirtualRects = <Object?>[isNotScaled, isNotScaled];
+          final actualChartVirtualRects = <Object?>[isNotScaled, isNotScaled];
           final transformationController = TransformationController();
           await tester.pumpWidget(
             createTestWidget(controller: transformationController),
           );
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects);
+          expect(chartVirtualRects, actualChartVirtualRects);
 
           transformationController.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           await tester.pumpWidget(createTestWidget());
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isNotScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isNotScaled));
 
           final transformationController2 = getTransformationController(tester);
           expect(transformationController2, isNot(transformationController));
+          // This is for test
           // ignore: invalid_use_of_protected_member
           expect(transformationController.hasListeners, false);
           transformationController.addListener(() {}); // throws if disposed
           transformationController2!.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
         },
       );
 
@@ -1405,34 +1453,35 @@ void main() {
         'removes listeners from old controller and sets up '
         'widget.controller with listeners',
         (WidgetTester tester) async {
-          final actualchartVirtualRects = <Object?>[isNotScaled, isNotScaled];
+          final actualChartVirtualRects = <Object?>[isNotScaled, isNotScaled];
           final transformationController = TransformationController();
           await tester.pumpWidget(
             createTestWidget(controller: transformationController),
           );
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects);
+          expect(chartVirtualRects, actualChartVirtualRects);
 
           transformationController.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           final transformationController2 = TransformationController();
 
           await tester.pumpWidget(
             createTestWidget(controller: transformationController2),
           );
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isNotScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isNotScaled));
 
           expect(transformationController2, isNot(transformationController));
+          // This is for test
           // ignore: invalid_use_of_protected_member
           expect(transformationController.hasListeners, false);
           transformationController.addListener(() {}); // throws if disposed
           transformationController2.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
         },
       );
 
@@ -1440,7 +1489,7 @@ void main() {
         'oldWidget.controller is not null and widget.controller is not null, '
         'controllers are the same: keeps old controller',
         (WidgetTester tester) async {
-          final actualchartVirtualRects = <Object?>[isNotScaled, isNotScaled];
+          final actualChartVirtualRects = <Object?>[isNotScaled, isNotScaled];
           final transformationController = TransformationController();
           await tester.pumpWidget(
             createTestWidget(
@@ -1448,26 +1497,26 @@ void main() {
             ),
           );
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects);
+          expect(chartVirtualRects, actualChartVirtualRects);
 
           transformationController.value = Matrix4.identity()..scale(2.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           await tester.pumpWidget(
             createTestWidget(
               controller: transformationController,
             ),
           );
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
           final transformationController2 = getTransformationController(tester);
           expect(transformationController2, transformationController);
           transformationController.value = Matrix4.identity()..scale(3.0);
           await tester.pump();
-          expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+          expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
         },
       );
     });
@@ -1477,7 +1526,7 @@ void main() {
       (WidgetTester tester) async {
         final transformationController = TransformationController();
         final chartVirtualRects = <Rect?>[];
-        final actualchartVirtualRects = <Object?>[isNotScaled, isNotScaled];
+        final actualChartVirtualRects = <Object?>[isNotScaled, isNotScaled];
         await tester.pumpWidget(
           MaterialApp(
             home: AxisChartScaffoldWidget(
@@ -1493,15 +1542,15 @@ void main() {
           ),
         );
         await tester.pump();
-        expect(chartVirtualRects, actualchartVirtualRects);
+        expect(chartVirtualRects, actualChartVirtualRects);
 
         transformationController.value = Matrix4.identity()..scale(2.0);
         await tester.pump();
-        expect(chartVirtualRects, actualchartVirtualRects..add(isScaled));
+        expect(chartVirtualRects, actualChartVirtualRects..add(isScaled));
 
         transformationController.value = Matrix4.identity()..scale(1.0);
         await tester.pump();
-        expect(chartVirtualRects, actualchartVirtualRects..add(isNotScaled));
+        expect(chartVirtualRects, actualChartVirtualRects..add(isNotScaled));
       },
     );
 
@@ -1522,6 +1571,7 @@ void main() {
         ),
       );
       await tester.pumpWidget(Container());
+      // This is for test
       // ignore: invalid_use_of_protected_member
       expect(controller.hasListeners, false);
       controller.addListener(() {}); // throws if disposed
diff --git a/test/chart/base/axis_chart/axis_chart_widgets_test.dart b/test/chart/base/axis_chart/axis_chart_widgets_test.dart
index 90328166f..1a9084582 100644
--- a/test/chart/base/axis_chart/axis_chart_widgets_test.dart
+++ b/test/chart/base/axis_chart/axis_chart_widgets_test.dart
@@ -3,6 +3,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 
 void main() {
+  TitleMeta getTitleMeta(AxisSide axisSide) => TitleMeta(
+        min: 0,
+        max: 10,
+        parentAxisSize: 100,
+        axisPosition: 10,
+        appliedInterval: 10,
+        sideTitles: const SideTitles(),
+        formattedValue: '12',
+        axisSide: axisSide,
+        rotationQuarterTurns: 0,
+      );
+
   group(
     'SideTitle without FitInside enabled',
     () {
@@ -10,11 +22,11 @@ void main() {
         'SideTitleWidget left',
         (WidgetTester tester) async {
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: SideTitleWidget(
-                  axisSide: AxisSide.left,
-                  child: Text('1s'),
+                  meta: getTitleMeta(AxisSide.left),
+                  child: const Text('1s'),
                 ),
               ),
             ),
@@ -29,11 +41,11 @@ void main() {
         'SideTitleWidget top',
         (WidgetTester tester) async {
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: SideTitleWidget(
-                  axisSide: AxisSide.top,
-                  child: Text('1s'),
+                  meta: getTitleMeta(AxisSide.top),
+                  child: const Text('1s'),
                 ),
               ),
             ),
@@ -48,11 +60,11 @@ void main() {
         'SideTitleWidget right',
         (WidgetTester tester) async {
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: SideTitleWidget(
-                  axisSide: AxisSide.right,
-                  child: Text('1s'),
+                  meta: getTitleMeta(AxisSide.right),
+                  child: const Text('1s'),
                 ),
               ),
             ),
@@ -67,14 +79,14 @@ void main() {
         'SideTitleWidget bottom',
         (WidgetTester tester) async {
           const widgetKey = Key('SideTitleWidget');
-          const sideTitleWidget = SideTitleWidget(
+          final sideTitleWidget = SideTitleWidget(
             key: widgetKey,
-            axisSide: AxisSide.bottom,
-            child: Text('1s'),
+            meta: getTitleMeta(AxisSide.bottom),
+            child: const Text('1s'),
           );
 
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: sideTitleWidget,
               ),
@@ -103,20 +115,20 @@ void main() {
         'SideTitleWidget left with FitInsideEnabled on Top Side',
         (WidgetTester tester) async {
           const widgetKey = Key('SideTitleWidget');
-          const sideTitleWidget = SideTitleWidget(
+          final sideTitleWidget = SideTitleWidget(
             key: widgetKey,
-            axisSide: AxisSide.left,
-            fitInside: SideTitleFitInsideData(
+            meta: getTitleMeta(AxisSide.left),
+            fitInside: const SideTitleFitInsideData(
               enabled: true,
               axisPosition: 0,
               parentAxisSize: 100,
               distanceFromEdge: 0,
             ),
-            child: Text('A Long Text'),
+            child: const Text('A Long Text'),
           );
 
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: sideTitleWidget,
               ),
@@ -139,20 +151,20 @@ void main() {
         'SideTitleWidget left with FitInsideEnabled on Bottom Side',
         (WidgetTester tester) async {
           const widgetKey = Key('SideTitleWidget');
-          const sideTitleWidget = SideTitleWidget(
+          final sideTitleWidget = SideTitleWidget(
             key: widgetKey,
-            axisSide: AxisSide.left,
-            fitInside: SideTitleFitInsideData(
+            meta: getTitleMeta(AxisSide.left),
+            fitInside: const SideTitleFitInsideData(
               enabled: true,
               axisPosition: 100,
               parentAxisSize: 100,
               distanceFromEdge: 0,
             ),
-            child: Text('A Long Text'),
+            child: const Text('A Long Text'),
           );
 
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: sideTitleWidget,
               ),
@@ -175,20 +187,20 @@ void main() {
         'SideTitleWidget bottom with FitInsideEnabled on Left Side',
         (WidgetTester tester) async {
           const widgetKey = Key('SideTitleWidget');
-          const sideTitleWidget = SideTitleWidget(
+          final sideTitleWidget = SideTitleWidget(
             key: widgetKey,
-            axisSide: AxisSide.bottom,
-            fitInside: SideTitleFitInsideData(
+            meta: getTitleMeta(AxisSide.bottom),
+            fitInside: const SideTitleFitInsideData(
               enabled: true,
               axisPosition: 0,
               parentAxisSize: 100,
               distanceFromEdge: 0,
             ),
-            child: Text('A Long Text'),
+            child: const Text('A Long Text'),
           );
 
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: sideTitleWidget,
               ),
@@ -211,20 +223,20 @@ void main() {
         'SideTitleWidget bottom with FitInsideEnabled on Right Side',
         (WidgetTester tester) async {
           const widgetKey = Key('SideTitleWidget');
-          const sideTitleWidget = SideTitleWidget(
+          final sideTitleWidget = SideTitleWidget(
             key: widgetKey,
-            axisSide: AxisSide.bottom,
-            fitInside: SideTitleFitInsideData(
+            meta: getTitleMeta(AxisSide.bottom),
+            fitInside: const SideTitleFitInsideData(
               enabled: true,
               axisPosition: 100,
               parentAxisSize: 100,
               distanceFromEdge: 0,
             ),
-            child: Text('A Long Text'),
+            child: const Text('A Long Text'),
           );
 
           await tester.pumpWidget(
-            const MaterialApp(
+            MaterialApp(
               home: Scaffold(
                 body: sideTitleWidget,
               ),
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
index 89ac64d64..3587daec4 100644
--- 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
@@ -188,6 +188,7 @@ void main() {
       const Size(viewSize, 40),
     );
     expect(
+      // This is for test
       // ignore: invalid_use_of_protected_member
       renderFlex.computeDistanceToActualBaseline(TextBaseline.alphabetic),
       null,
@@ -213,6 +214,7 @@ void main() {
       const Size(viewSize, 40),
     );
     expect(
+      // This is for test
       // ignore: invalid_use_of_protected_member
       renderFlex.computeDistanceToActualBaseline(TextBaseline.alphabetic),
       null,
diff --git a/test/chart/data_pool.dart b/test/chart/data_pool.dart
index d187141da..86280c112 100644
--- a/test/chart/data_pool.dart
+++ b/test/chart/data_pool.dart
@@ -2226,6 +2226,26 @@ final LineChartData lineChartData20 = LineChartData(
   minX: 11,
   minY: 302,
 );
+final LineChartData lineChartData21 = LineChartData(
+  borderData: borderData1,
+  rotationQuarterTurns: 1,
+  lineTouchData: lineTouchData1,
+  showingTooltipIndicators: [
+    showingTooltipIndicator1,
+    showingTooltipIndicator2,
+  ],
+  backgroundColor: Colors.red,
+  maxY: 23,
+  rangeAnnotations: rangeAnnotations1,
+  gridData: flGridData1,
+  titlesData: MockData.flTitlesData1,
+  lineBarsData: [lineChartBarData1, lineChartBarData2, lineChartBarData3],
+  betweenBarsData: [betweenBarsData1, betweenBarsData2, betweenBarsData3],
+  extraLinesData: extraLinesData1,
+  maxX: 23,
+  minX: 11,
+  minY: 302,
+);
 
 final PieChartData pieChartData1 = PieChartData(
   borderData: FlBorderData(show: false, border: Border.all()),
diff --git a/test/chart/line_chart/line_chart_data_test.dart b/test/chart/line_chart/line_chart_data_test.dart
index fb2462222..15c84352d 100644
--- a/test/chart/line_chart/line_chart_data_test.dart
+++ b/test/chart/line_chart/line_chart_data_test.dart
@@ -203,6 +203,21 @@ void main() {
       expect(lineChartData1 == lineChartData18, false);
       expect(lineChartData1 == lineChartData19, false);
       expect(lineChartData1 == lineChartData20, false);
+      expect(lineChartData1 == lineChartData21, false);
+      expect(
+        lineChartData21 ==
+            lineChartData21.copyWith(
+              rotationQuarterTurns: 2,
+            ),
+        false,
+      );
+      expect(
+        lineChartData21 ==
+            lineChartData21.copyWith(
+              rotationQuarterTurns: 1,
+            ),
+        true,
+      );
     });
   });
 }
diff --git a/test/chart/line_chart/line_chart_painter_test.dart b/test/chart/line_chart/line_chart_painter_test.dart
index 4dd2dcd0e..a20caea73 100644
--- a/test/chart/line_chart/line_chart_painter_test.dart
+++ b/test/chart/line_chart/line_chart_painter_test.dart
@@ -2897,6 +2897,136 @@ void main() {
       expect(drawOffset, const Offset(22, 52));
     });
 
+    test('test 4 - rotated chart with rotationQuarterTurns 2', () {
+      const viewSize = Size(100, 100);
+
+      final barData1 = LineChartBarData(
+        spots: const [
+          FlSpot(1, 1),
+          FlSpot(2, 2),
+          FlSpot(3, 3),
+          FlSpot(4, 4),
+          FlSpot.nullSpot,
+          FlSpot(5, 5),
+        ],
+      );
+      final barData2 = LineChartBarData(
+        spots: const [
+          FlSpot(1, 6),
+          FlSpot(2, 7),
+          FlSpot(3, 8),
+          FlSpot(4, 9),
+          FlSpot.nullSpot,
+          FlSpot(5, 10),
+        ],
+      );
+
+      final tooltipData = LineTouchTooltipData(
+        getTooltipColor: (touchedSpot) => const Color(0x11111111),
+        tooltipRoundedRadius: 12,
+        rotateAngle: 43,
+        maxContentWidth: 100,
+        tooltipMargin: 12,
+        tooltipHorizontalAlignment: FLHorizontalAlignment.right,
+        tooltipPadding: const EdgeInsets.all(12),
+        fitInsideVertically: true,
+        getTooltipItems: (List<LineBarSpot> touchedSpots) {
+          return touchedSpots
+              .map((e) => LineTooltipItem(e.barIndex.toString(), textStyle1))
+              .toList();
+        },
+        tooltipBorder: const BorderSide(color: Color(0x11111111), width: 2),
+      );
+      final data = LineChartData(
+        rotationQuarterTurns: 2,
+        minY: 0,
+        maxY: 10,
+        minX: 0,
+        maxX: 10,
+        titlesData: const FlTitlesData(show: false),
+        lineTouchData: LineTouchData(
+          touchTooltipData: tooltipData,
+        ),
+      );
+
+      final lineChartPainter = LineChartPainter();
+      final holder =
+          PaintHolder<LineChartData>(data, data, TextScaler.noScaling);
+      final mockCanvasWrapper = MockCanvasWrapper();
+      when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize);
+      when(mockCanvasWrapper.canvas).thenReturn(MockCanvas());
+
+      final mockBuildContext = MockBuildContext();
+      final mockUtils = MockUtils();
+      Utils.changeInstance(mockUtils);
+      when(mockUtils.getThemeAwareTextStyle(any, any))
+          .thenAnswer((realInvocation) => textStyle1);
+      when(mockUtils.calculateRotationOffset(any, any))
+          .thenAnswer((realInvocation) => Offset.zero);
+      when(
+        mockCanvasWrapper.drawRotated(
+          size: anyNamed('size'),
+          rotationOffset: anyNamed('rotationOffset'),
+          drawOffset: anyNamed('drawOffset'),
+          angle: anyNamed('angle'),
+          drawCallback: anyNamed('drawCallback'),
+        ),
+      ).thenAnswer((realInvocation) {
+        final callback = realInvocation
+            .namedArguments[const Symbol('drawCallback')] as DrawCallback;
+        callback();
+      });
+      lineChartPainter.drawTouchTooltip(
+        mockBuildContext,
+        mockCanvasWrapper,
+        tooltipData,
+        barData2.spots.first,
+        ShowingTooltipIndicators([
+          LineBarSpot(
+            barData1,
+            0,
+            barData1.spots.first,
+          ),
+          LineBarSpot(
+            barData2,
+            1,
+            barData2.spots.first,
+          ),
+        ]),
+        holder,
+      );
+
+      final result1 =
+          verify(mockCanvasWrapper.drawRRect(captureAny, captureAny))
+            ..called(2);
+      final rRect = result1.captured[0] as RRect;
+      final paint = result1.captured[1] as Paint;
+      expect(
+        rRect,
+        RRect.fromLTRBR(10, 0, 48, 56, const Radius.circular(12)),
+      );
+      expect(paint.color, isSameColorAs(const Color(0x11111111)));
+      final rRectBorder = result1.captured[2] as RRect;
+      final paintBorder = result1.captured[3] as Paint;
+      expect(
+        rRectBorder,
+        RRect.fromLTRBR(10, 0, 48, 56, const Radius.circular(12)),
+      );
+      expect(paintBorder.color, isSameColorAs(const Color(0x11111111)));
+      expect(paintBorder.strokeWidth, 2);
+
+      final result2 = verify(mockCanvasWrapper.drawText(captureAny, captureAny))
+        ..called(2);
+      final textPainter1 = result2.captured[0] as TextPainter;
+      final drawOffset1 = result2.captured[1] as Offset;
+      final textPainter2 = result2.captured[2] as TextPainter;
+      expect((textPainter1.text as TextSpan?)!.text, '1');
+      expect((textPainter1.text as TextSpan?)!.style, textStyle1);
+      expect((textPainter2.text as TextSpan?)!.text, '0');
+      expect((textPainter2.text as TextSpan?)!.style, textStyle1);
+      expect(drawOffset1, const Offset(22, 12));
+    });
+
     test('does not draw tooltip if it is outside of the chart virtual rect',
         () {
       const viewSize = Size(100, 100);
diff --git a/test/chart/line_chart/line_chart_test.dart b/test/chart/line_chart/line_chart_test.dart
index 47f31a705..48a34d3c8 100644
--- a/test/chart/line_chart/line_chart_test.dart
+++ b/test/chart/line_chart/line_chart_test.dart
@@ -105,8 +105,10 @@ void main() {
         createTestWidget(
           chart: LineChart(
             LineChartData(),
+            // This is for test
             // ignore: avoid_redundant_argument_values
             transformationConfig: const FlTransformationConfig(
+              // This is for test
               // ignore: avoid_redundant_argument_values
               scaleAxis: FlScaleAxis.none,
             ),
@@ -647,6 +649,7 @@ void main() {
               chart: LineChart(
                 LineChartData(),
                 transformationConfig: const FlTransformationConfig(
+                  // This is for test
                   // ignore: avoid_redundant_argument_values
                   scaleAxis: FlScaleAxis.none,
                   trackpadScrollCausesScale: true,
@@ -684,6 +687,7 @@ void main() {
                   LineChartData(),
                   transformationConfig: FlTransformationConfig(
                     scaleAxis: scaleAxis,
+                    // This is for test
                     // ignore: avoid_redundant_argument_values
                     trackpadScrollCausesScale: false,
                   ),
diff --git a/test/chart/scatter_chart/scatter_chart_test.dart b/test/chart/scatter_chart/scatter_chart_test.dart
index 872572a43..184d252fd 100644
--- a/test/chart/scatter_chart/scatter_chart_test.dart
+++ b/test/chart/scatter_chart/scatter_chart_test.dart
@@ -110,8 +110,10 @@ void main() {
         createTestWidget(
           chart: ScatterChart(
             ScatterChartData(),
+            // This is for test
             // ignore: avoid_redundant_argument_values
             transformationConfig: const FlTransformationConfig(
+              // This is for test
               // ignore: avoid_redundant_argument_values
               scaleAxis: FlScaleAxis.none,
             ),
@@ -646,6 +648,7 @@ void main() {
               chart: ScatterChart(
                 ScatterChartData(),
                 transformationConfig: const FlTransformationConfig(
+                  // This is for test
                   // ignore: avoid_redundant_argument_values
                   scaleAxis: FlScaleAxis.none,
                   trackpadScrollCausesScale: true,
@@ -682,6 +685,7 @@ void main() {
                   ScatterChartData(),
                   transformationConfig: FlTransformationConfig(
                     scaleAxis: scaleAxis,
+                    // This is for test
                     // ignore: avoid_redundant_argument_values
                     trackpadScrollCausesScale: false,
                   ),
diff --git a/test/extensions/size_extension_test.dart b/test/extensions/size_extension_test.dart
new file mode 100644
index 000000000..c04b6fcdc
--- /dev/null
+++ b/test/extensions/size_extension_test.dart
@@ -0,0 +1,24 @@
+import 'dart:ui';
+
+import 'package:fl_chart/src/extensions/size_extension.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  test('test rotateByQuarterTurns extension.', () {
+    expect(const Size(100, 200).rotateByQuarterTurns(0), const Size(100, 200));
+    expect(const Size(100, 200).rotateByQuarterTurns(1), const Size(200, 100));
+    expect(const Size(100, 200).rotateByQuarterTurns(2), const Size(100, 200));
+    expect(const Size(100, 200).rotateByQuarterTurns(3), const Size(200, 100));
+    expect(const Size(100, 200).rotateByQuarterTurns(4), const Size(100, 200));
+    expect(const Size(100, 200).rotateByQuarterTurns(5), const Size(200, 100));
+    expect(const Size(100, 200).rotateByQuarterTurns(6), const Size(100, 200));
+    expect(
+      () => const Size(100, 200).rotateByQuarterTurns(-1),
+      throwsArgumentError,
+    );
+    expect(
+      () => const Size(100, 200).rotateByQuarterTurns(-3),
+      throwsArgumentError,
+    );
+  });
+}