Skip to content

Commit 80e384a

Browse files
alanleedevmeta-codesync[bot]
authored andcommitted
Add listener API to OverrideColorScheme for dynamic dark mode updates (facebook#54791)
Summary: Pull Request resolved: facebook#54791 This change adds a listener mechanism to the `OverrideColorScheme` interface to enable dynamic updates when the user's dark mode preference changes via `OverrideColorScheme`. Previously, `AppearanceModule` would only reflect the initial color scheme state when instantiated. If a user toggled dark mode through an `OverrideColorScheme` implementation, React Native JavaScript would not be notified of the change, causing UI inconsistencies. This implementation adds: 1. An optional `addSchemeChangeListener()` method to the `OverrideColorScheme` interface with a default no-op implementation for backward compatibility. Also adding matching `removeSchemeChangeListener()`. 2. Automatic listener registration in `AppearanceModule`'s init block that triggers `onConfigurationChanged()` when the color scheme changes Changelog: [Android][Added] - Add `{add|remove}SchemeChangeListener()` function to `OverrideColorScheme` interface to support dynamic appearance updates via override Reviewed By: javache Differential Revision: D88427482 fbshipit-source-id: 02b71335bb5bebd1f44bf9723c3120f9a1fc182c
1 parent 17b3c1c commit 80e384a

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,6 +2465,7 @@ public final class com/facebook/react/modules/appearance/AppearanceModule : com/
24652465
public fun addListener (Ljava/lang/String;)V
24662466
public final fun emitAppearanceChanged (Ljava/lang/String;)V
24672467
public fun getColorScheme ()Ljava/lang/String;
2468+
public fun invalidate ()V
24682469
public final fun invalidatePlatformColorCache ()V
24692470
public final fun onConfigurationChanged (Landroid/content/Context;)V
24702471
public fun removeListeners (D)V
@@ -2475,7 +2476,14 @@ public final class com/facebook/react/modules/appearance/AppearanceModule$Compan
24752476
}
24762477

24772478
public abstract interface class com/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme {
2479+
public abstract fun addSchemeChangeListener (Lkotlin/jvm/functions/Function0;)V
24782480
public abstract fun getScheme ()Ljava/lang/String;
2481+
public abstract fun removeSchemeChangeListener (Lkotlin/jvm/functions/Function0;)V
2482+
}
2483+
2484+
public final class com/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme$DefaultImpls {
2485+
public static fun addSchemeChangeListener (Lcom/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme;Lkotlin/jvm/functions/Function0;)V
2486+
public static fun removeSchemeChangeListener (Lcom/facebook/react/modules/appearance/AppearanceModule$OverrideColorScheme;Lkotlin/jvm/functions/Function0;)V
24792487
}
24802488

24812489
public abstract interface class com/facebook/react/modules/appregistry/AppRegistry : com/facebook/react/bridge/JavaScriptModule {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,42 @@ constructor(
2828

2929
private var lastEmittedColorScheme: String? = null
3030

31+
private val schemeChangeListener: () -> Unit = {
32+
val activity = reactApplicationContext.getCurrentActivity()
33+
onConfigurationChanged(activity ?: reactApplicationContext)
34+
}
35+
36+
init {
37+
// Register as a listener for color scheme changes if override is provided
38+
overrideColorScheme?.addSchemeChangeListener(schemeChangeListener)
39+
}
40+
3141
/** Optional override to the current color scheme */
32-
public fun interface OverrideColorScheme {
42+
public interface OverrideColorScheme {
3343
/**
3444
* Color scheme will use the return value instead of the current system configuration. Available
3545
* scheme: {light, dark}
3646
*/
3747
public fun getScheme(): String
48+
49+
/**
50+
* Register a listener to be notified when the color scheme changes. The listener will be
51+
* invoked whenever the underlying theme preference changes.
52+
*
53+
* Default implementation does nothing. Override this method if you want to support dynamic
54+
* color scheme updates.
55+
*/
56+
public fun addSchemeChangeListener(listener: () -> Unit) {
57+
// no-op
58+
}
59+
60+
/**
61+
* Unregisters a previously added color scheme change listener. Default implementation is a
62+
* no-op; override to remove the listener from your source.
63+
*/
64+
public fun removeSchemeChangeListener(listener: () -> Unit) {
65+
// no-op
66+
}
3867
}
3968

4069
private fun colorSchemeForCurrentConfiguration(context: Context): String {
@@ -98,6 +127,12 @@ constructor(
98127
Companion.invalidatePlatformColorCache?.run()
99128
}
100129

130+
public override fun invalidate() {
131+
overrideColorScheme?.removeSchemeChangeListener(schemeChangeListener)
132+
invalidatePlatformColorCache()
133+
super.invalidate()
134+
}
135+
101136
public companion object {
102137
public const val NAME: String = NativeAppearanceSpec.NAME
103138
private const val APPEARANCE_CHANGED_EVENT_NAME = "appearanceChanged"

0 commit comments

Comments
 (0)