diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md
index 62e038740..3e66ef7ca 100644
--- a/flutter_local_notifications/CHANGELOG.md
+++ b/flutter_local_notifications/CHANGELOG.md
@@ -1,3 +1,87 @@
+# 17.2.6
+
+## [17.2.6]
+
+* [Android] Added font scale compensation for custom notification layouts.
+
+When the user increases the system font size (Settings → Display → Font size), text in custom
+`RemoteViews`-based notification layouts can overflow because Android scales `sp` values before
+rendering and `autoSizeTextType` is not supported in `RemoteViews`.
+
+The plugin now detects `fontScale > 1.0` at notification creation time and programmatically
+overrides text sizes using `RemoteViews.setTextViewTextSize()` with compensated pixel values,
+keeping text at a fixed visual size regardless of the system font scale.
+When `fontScale == 1.0` (default), no overrides are applied.
+
+### How to use
+
+**1. Define dimen resources** in `android/app/src/main/res/values/dimens.xml` that match the
+`textSize` values in your custom notification layout XML. The naming convention is
+`{view_id}_text_size`:
+
+```xml
+
+
+ 14sp
+ 12sp
+
+
+ 15sp
+ 15sp
+
+```
+
+For example, if your layout has:
+
+```xml
+
+
+
+
+
+```
+
+Then each dimen name is `push_title` + `_text_size` = `push_title_text_size`, etc.
+
+**2. No changes to Dart code or layout XML are required.** Existing code works as before:
+
+```dart
+await flutterLocalNotificationsPlugin.show(
+ id,
+ 'Title',
+ 'Body text',
+ NotificationDetails(
+ android: AndroidNotificationDetails(
+ 'channel_id',
+ 'Channel Name',
+ customLayoutExpandedName: 'scheduled_notification_expanded',
+ customLayoutCollapsedName: 'scheduled_notification_collapsed',
+ actions: [
+ AndroidNotificationAction(
+ 'enter',
+ 'Show',
+ customViewId: 'action_enter',
+ ),
+ AndroidNotificationAction(
+ 'skip',
+ 'Close',
+ customViewId: 'action_skip',
+ ),
+ ],
+ ),
+ ),
+);
+```
+
+**Note:** If a dimen resource for a given view ID is not found, no override is applied for that
+view. This makes the feature fully backwards-compatible.
+
# 17.2.5
Include changes from parent repo v17.0.1 - 17.2.4
diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md
index b36268324..81791f138 100644
--- a/flutter_local_notifications/README.md
+++ b/flutter_local_notifications/README.md
@@ -21,6 +21,7 @@ This is fork of [flutter_local_notifications](https://github.com/MaikuB/flutter_
it will save in local store if notification show.
* You can load this stored information by `FlutterLocalNotificationsPlugin.getShownNotificationsInfo()`
method and clear stored info by `FlutterLocalNotificationsPlugin.cleanShownNotificationsInfo()` method.
+* Font scale compensation for custom notification layouts. See [Font scale compensation for custom layouts](#font-scale-compensation-for-custom-layouts).
### iOS
@@ -51,6 +52,7 @@ This is fork of [flutter_local_notifications](https://github.com/MaikuB/flutter_
- [Scheduled notifications](#scheduling-a-notification)
- [Fullscreen intent notifications](#full-screen-intent-notifications)
- [Release build configuration](#release-build-configuration)
+ - [Font scale compensation for custom layouts](#font-scale-compensation-for-custom-layouts)
- **[🔧 iOS setup](#-ios-setup)**
- [General setup](#general-setup)
- [Handling notifications whilst the app is in the foreground](#handling-notifications-whilst-the-app-is-in-the-foreground)
@@ -330,6 +332,38 @@ Before creating the release build of your app (which is the default setting when
⚠️ Ensure that you have configured the resources that should be kept so that resources like your notification icons aren't discarded by the R8 compiler by following the instructions [here](https://developer.android.com/studio/build/shrink-code#keep-resources). If you have chosen to use `@mipmap/ic_launcher` as the notification icon (against the official Android guidance), be sure to include this in the `keep.xml` file. If you fail to do this, notifications might be broken. In the worst case they will never show, instead silently failing when the system looks for a resource that has been removed. If they do still show, you might not see the icon you specified. The configuration used by the example app can be found [here](https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/example/android/app/src/main/res/raw/keep.xml) where it is specifying that all drawable resources should be kept, as well as the file used to play a custom notification sound (sound file is located [here](https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/example/android/app/src/main/res/raw/slow_spring_board.mp3)).
+### Font scale compensation for custom layouts
+
+When users increase the system font size (Settings → Display → Font size), text in custom `RemoteViews`-based notification layouts can overflow. This happens because Android scales all `sp` values globally before rendering `RemoteViews`, and `autoSizeTextType` is not supported in `RemoteViews`.
+
+The plugin detects `fontScale > 1.0` at notification creation time and programmatically overrides text sizes via `RemoteViews.setTextViewTextSize()` with compensated pixel values. At `fontScale == 1.0` (default) no overrides are applied.
+
+#### Setup
+
+Define dimen resources in `android/app/src/main/res/values/dimens.xml` whose values match the `textSize` in your layout XML. The naming convention is **`{view_id}_text_size`**, where `{view_id}` is the `android:id` of the view.
+
+For a layout with:
+
+```xml
+
+
+
+
+```
+
+Add dimens:
+
+```xml
+
+ 14sp
+ 12sp
+ 15sp
+ 15sp
+
+```
+
+No changes to Dart code or layout XML are required. If a dimen resource for a given view ID is not found, no override is applied — the feature is fully backwards-compatible.
+
## 🔧 iOS setup
### General setup
diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java
index d8dfcca39..ca6328224 100644
--- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java
+++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java
@@ -34,6 +34,7 @@
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
+import android.util.TypedValue;
import android.view.View;
import android.widget.RemoteViews;
import android.util.Log;
@@ -426,6 +427,8 @@ protected static Notification createNotification(
Resources resources = null;
resources = manager.getResourcesForApplication(packageName);
+ float fontScale = context.getResources().getConfiguration().fontScale;
+
if (VERSION.SDK_INT < VERSION_CODES.S) {
RemoteViews contentView =
setCustomContentView(
@@ -433,10 +436,12 @@ protected static Notification createNotification(
resources,
notificationDetails,
notificationDetails.customLayoutLegacyName,
- packageName);
+ packageName,
+ fontScale);
if (notificationDetails.actions != null) {
setCustomContentViewActions(
- contentView, resources, packageName, actionIntents, notificationDetails.actions);
+ contentView, resources, packageName, actionIntents, notificationDetails.actions,
+ fontScale);
}
builder.setCustomContentView(contentView);
} else {
@@ -448,7 +453,8 @@ protected static Notification createNotification(
resources,
notificationDetails,
customLayoutCollapsedName,
- packageName);
+ packageName,
+ fontScale);
builder.setCustomContentView(contentView);
}
@@ -460,14 +466,16 @@ protected static Notification createNotification(
resources,
notificationDetails,
customLayoutExpandedName,
- packageName);
+ packageName,
+ fontScale);
if (notificationDetails.actions != null) {
setCustomContentViewActions(
contentView,
resources,
packageName,
actionIntents,
- notificationDetails.actions);
+ notificationDetails.actions,
+ fontScale);
}
builder.setCustomBigContentView(contentView);
}
@@ -519,13 +527,14 @@ private static RemoteViews setCustomContentView(
Resources resources,
NotificationDetails notificationDetails,
String layoutName,
- String packageName) {
+ String packageName,
+ float fontScale) {
int layoutId = resources.getIdentifier(layoutName, "layout", packageName);
RemoteViews contentView = new RemoteViews(packageName, layoutId);
setCustomContentViewField(
- contentView, resources, notificationDetails.title, "push_title", packageName);
+ contentView, resources, notificationDetails.title, "push_title", packageName, fontScale);
setCustomContentViewField(
- contentView, resources, notificationDetails.body, "push_text", packageName);
+ contentView, resources, notificationDetails.body, "push_text", packageName, fontScale);
setCustomContentViewImage(
context,
contentView,
@@ -542,7 +551,8 @@ private static void setCustomContentViewActions(
Resources resources,
String packageName,
Map actionIntents,
- List actions) {
+ List actions,
+ float fontScale) {
for (NotificationAction action : actions) {
if (!StringUtils.isNullOrEmpty(action.customViewId)) {
int viewId = resources.getIdentifier(action.customViewId, "id", packageName);
@@ -551,6 +561,8 @@ private static void setCustomContentViewActions(
if (action.titleColor != null) {
contentView.setTextColor(viewId, action.titleColor);
}
+ compensateTextSizeForFontScale(
+ contentView, resources, viewId, action.customViewId, packageName, fontScale);
contentView.setOnClickPendingIntent(viewId, actionIntents.get(action.id));
}
}
@@ -558,15 +570,30 @@ private static void setCustomContentViewActions(
}
private static void setCustomContentViewField(
- RemoteViews contentView, Resources resources, String value, String name, String packageName) {
+ RemoteViews contentView, Resources resources, String value, String name, String packageName,
+ float fontScale) {
int id = resources.getIdentifier(name, "id", packageName);
if (!StringUtils.isNullOrEmpty(value)) {
contentView.setTextViewText(id, value);
+ compensateTextSizeForFontScale(contentView, resources, id, name, packageName, fontScale);
} else {
contentView.setViewVisibility(id, View.GONE);
}
}
+ private static void compensateTextSizeForFontScale(
+ RemoteViews contentView, Resources resources, int viewId, String viewName,
+ String packageName, float fontScale) {
+ if (fontScale > 1.0f) {
+ int dimenId = resources.getIdentifier(viewName + "_text_size", "dimen", packageName);
+ if (dimenId != 0) {
+ float baseSizePx = resources.getDimension(dimenId);
+ float compensatedSizePx = baseSizePx / fontScale;
+ contentView.setTextViewTextSize(viewId, TypedValue.COMPLEX_UNIT_PX, compensatedSizePx);
+ }
+ }
+ }
+
private static void setCustomContentViewImage(
Context context,
RemoteViews contentView,
diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml
index 6ff174865..4c8846d20 100644
--- a/flutter_local_notifications/pubspec.yaml
+++ b/flutter_local_notifications/pubspec.yaml
@@ -2,7 +2,7 @@ name: flutter_local_notifications_plus
description: A cross platform plugin for displaying and scheduling local
notifications for Flutter applications with the ability to customise for each
platform.
-version: 17.2.5
+version: 17.2.6
homepage: https://github.com/Innim/flutter_local_notifications
repository: https://github.com/Innim/flutter_local_notifications
issue_tracker: https://github.com/Innim/flutter_local_notifications/issues