Repository: imaNNeo/fl_chart (dart)

Issue:
Is it possible to move x axis labels in chart area?
On smaller screen, when x labels' String can be long, it would be nice move them inside chart area.

<img width="917" alt="Image" src="https://github.com/user-attachments/assets/087cca32-d91c-4455-a8f0-ca629f87f8f6" />

Because on smaller screens, you don't really want them to take so much space on the left

Pull request:
Hello! 👋

First of all, thank you for an awesome chart package 😊

This contribution is to resolve #1946.

## What I did

I added an optional Boolean property for the `AxisTitles` class to allow each side title to be overlayed on the chart area if the user so desires.

For example, in Line Chart Sample 9, I added the property to the left titles and now it is placed on the chart area.

You can see the example use here:
<img width="400"  alt="image" src="https://github.com/user-attachments/assets/b8f2655a-b10f-4864-94ba-efa367a15847" />

With the result:
<img width="454" height="485" alt="image" src="https://github.com/user-attachments/assets/b52e94ea-2f88-44e2-92d8-191db7affb78" />

Patch:
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 71203fb73..be027ee56 100644
--- a/lib/src/chart/base/axis_chart/axis_chart_data.dart
+++ b/lib/src/chart/base/axis_chart/axis_chart_data.dart
@@ -112,6 +112,9 @@ enum AxisSide {
   }
 }
 
+/// Represents where the [SideTitles] are drawn in relation to the chart.
+enum SideTitleAlignment { outside, border, inside }
+
 /// Contains meta information about the drawing title.
 class TitleMeta {
   TitleMeta({
@@ -355,6 +358,7 @@ class AxisTitles with EquatableMixin {
     this.axisNameSize = 16,
     this.sideTitles = const SideTitles(),
     this.drawBelowEverything = true,
+    this.sideTitleAlignment = SideTitleAlignment.outside,
   });
 
   /// Determines the size of [axisName]
@@ -372,6 +376,9 @@ class AxisTitles with EquatableMixin {
   /// In the future, we will convert tooltips to a widget, that would solve this problem.
   final bool drawBelowEverything;
 
+  /// Where the [SideTitles] are drawn in relation to the chart.
+  final SideTitleAlignment sideTitleAlignment;
+
   /// If there is something to show as axisTitles, it returns true
   bool get showAxisTitles => axisNameWidget != null && axisNameSize != 0;
 
@@ -385,6 +392,7 @@ class AxisTitles with EquatableMixin {
         axisNameSize: lerpDouble(a.axisNameSize, b.axisNameSize, t)!,
         sideTitles: SideTitles.lerp(a.sideTitles, b.sideTitles, t),
         drawBelowEverything: b.drawBelowEverything,
+        sideTitleAlignment: b.sideTitleAlignment,
       );
 
   /// Copies current [SideTitles] to a new [SideTitles],
@@ -394,12 +402,14 @@ class AxisTitles with EquatableMixin {
     double? axisNameSize,
     SideTitles? sideTitles,
     bool? drawBelowEverything,
+    SideTitleAlignment? sideTitleAlignment,
   }) =>
       AxisTitles(
         axisNameWidget: axisNameWidget ?? this.axisNameWidget,
         axisNameSize: axisNameSize ?? this.axisNameSize,
         sideTitles: sideTitles ?? this.sideTitles,
         drawBelowEverything: drawBelowEverything ?? this.drawBelowEverything,
+        sideTitleAlignment: sideTitleAlignment ?? this.sideTitleAlignment,
       );
 
   /// Used for equality check, see [EquatableMixin].
@@ -409,6 +419,7 @@ class AxisTitles with EquatableMixin {
         axisNameSize,
         sideTitles,
         drawBelowEverything,
+        sideTitleAlignment,
       ];
 }
 
diff --git a/lib/src/extensions/fl_titles_data_extension.dart b/lib/src/extensions/fl_titles_data_extension.dart
index 28565d749..371cfb129 100644
--- a/lib/src/extensions/fl_titles_data_extension.dart
+++ b/lib/src/extensions/fl_titles_data_extension.dart
@@ -4,9 +4,31 @@ import 'package:flutter/widgets.dart';
 
 extension FlTitlesDataExtension on FlTitlesData {
   EdgeInsets get allSidesPadding => EdgeInsets.only(
-        left: show ? leftTitles.totalReservedSize : 0.0,
-        top: show ? topTitles.totalReservedSize : 0.0,
-        right: show ? rightTitles.totalReservedSize : 0.0,
-        bottom: show ? bottomTitles.totalReservedSize : 0.0,
+        left: _getPadding(
+          leftTitles.sideTitleAlignment,
+          leftTitles.totalReservedSize,
+        ),
+        top: _getPadding(
+          topTitles.sideTitleAlignment,
+          topTitles.totalReservedSize,
+        ),
+        right: _getPadding(
+          rightTitles.sideTitleAlignment,
+          rightTitles.totalReservedSize,
+        ),
+        bottom: _getPadding(
+          bottomTitles.sideTitleAlignment,
+          bottomTitles.totalReservedSize,
+        ),
       );
+
+  double _getPadding(SideTitleAlignment alignment, double reservedSize) {
+    if (!show || alignment == SideTitleAlignment.inside) {
+      return 0;
+    } else if (alignment == SideTitleAlignment.border) {
+      return reservedSize / 2;
+    } else {
+      return reservedSize;
+    }
+  }
 }
diff --git a/repo_files/documentations/base_chart.md b/repo_files/documentations/base_chart.md
index 3b59a48a1..fab5b26b4 100644
--- a/repo_files/documentations/base_chart.md
+++ b/repo_files/documentations/base_chart.md
@@ -23,6 +23,7 @@
 |axisNameWidget| It shows the name of axis (you can pass a Widget)| `null`|
 |sideTitles| It accepts a [SideTitles](#SideTitles) which is responsible to show your axis side titles| `SideTitles()`|
 |drawBehindEverything| If titles are showing on top of your tooltip, you can draw them behind everything.| `true`|
+|sideTitleAlignment| It accepts a [SideTitleAlignment](#SideTitleAlignment) which is responsible for positioning the axis labels inside, outside, or on the border of the chart| `SideTitleAlignment.outside`
 
 ### SideTitles
 |PropName		|Description	|default value|
@@ -34,7 +35,6 @@
 |minIncluded| Determines whether to include title for minimum data value | true |
 |maxIncluded| Determines whether to include title for maximum data value | true |
 
-
 ### SideTitleFitInsideData
 |PropName		|Description	|default value|
 |:---------------|:---------------|:-------|
@@ -207,3 +207,10 @@ enum values {`center`, `left`, `right`}
 |x|x value of the touched spot|required|
 |y|y value of the touched spot|required|
 |AxisSpotIndicatorPainter|a painter that is used to draw the touched spot indicator. You can use this to customize the appearance of the touched spot indicator (Or you can implement your own painter).|AxisLinesIndicatorPainter()|
+
+### SideTitleAlignment
+|Option		|Description	|   Is Default
+|:---------------|:---------------|:-------|
+|`SideTitleAlignment.outside`| Places the axis labels outside the chart area| Yes|
+|`SideTitleAlignment.border`| Places the axis labels along the border of the chart area| No|
+|`SideTitleAlignment.inside`| Places the axis labels inside the border of the chart area| No|


Test patch:
diff --git a/test/chart/base/axis_chart/side_titles/side_titles_test.dart b/test/chart/base/axis_chart/side_titles/side_titles_test.dart
index 72a283f17..dbecd0c88 100644
--- a/test/chart/base/axis_chart/side_titles/side_titles_test.dart
+++ b/test/chart/base/axis_chart/side_titles/side_titles_test.dart
@@ -78,4 +78,100 @@ void main() {
       }
     },
   );
+
+  testWidgets('LineChart with only left titles overlayed on chart area',
+      (WidgetTester tester) async {
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          body: Center(
+            child: SizedBox(
+              width: viewSize.width,
+              height: viewSize.height,
+              child: LineChart(
+                LineChartData(
+                  titlesData: FlTitlesData(
+                    leftTitles: AxisTitles(
+                      sideTitleAlignment: SideTitleAlignment.inside,
+                      axisNameWidget: const Text('Left Titles'),
+                      sideTitles: SideTitles(
+                        showTitles: true,
+                        getTitlesWidget: (double value, TitleMeta meta) {
+                          return Text('L-${value.toInt()}');
+                        },
+                        interval: 1,
+                      ),
+                    ),
+                  ),
+                  lineBarsData: [
+                    LineChartBarData(
+                      spots: data,
+                    ),
+                  ],
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+
+    final leftTitleFinder = find.text('L-0');
+    expect(leftTitleFinder, findsOneWidget);
+
+    final leftTitleRect = tester.getRect(leftTitleFinder);
+
+    final chartFinder = find.byType(LineChart);
+    final chartRect = tester.getRect(chartFinder);
+
+    expect(leftTitleRect.left >= chartRect.left, true);
+  });
+
+  testWidgets('LineChart with only left titles overlayed on chart border',
+      (WidgetTester tester) async {
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          body: Center(
+            child: SizedBox(
+              width: viewSize.width,
+              height: viewSize.height,
+              child: LineChart(
+                LineChartData(
+                  titlesData: FlTitlesData(
+                    leftTitles: AxisTitles(
+                      sideTitleAlignment: SideTitleAlignment.border,
+                      axisNameWidget: const Text('Left Titles'),
+                      sideTitles: SideTitles(
+                        showTitles: true,
+                        getTitlesWidget: (double value, TitleMeta meta) {
+                          return Text('L-${value.toInt()}');
+                        },
+                        interval: 1,
+                      ),
+                    ),
+                  ),
+                  lineBarsData: [
+                    LineChartBarData(
+                      spots: data,
+                    ),
+                  ],
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+
+    final leftTitleFinder = find.text('L-0');
+    expect(leftTitleFinder, findsOneWidget);
+
+    final leftTitleRect = tester.getRect(leftTitleFinder);
+
+    final chartFinder = find.byType(LineChart);
+    final chartRect = tester.getRect(chartFinder);
+
+    expect(leftTitleRect.left >= chartRect.left, true);
+  });
 }