From ffa6ae63655f826a4f6c367f71d594406e29d695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sans=C3=A3o=20Araldi?= Date: Mon, 11 May 2026 14:27:56 -0300 Subject: [PATCH 1/4] fix(android): remove unnecessary mathematical logic from downscaleFactor property --- README.md | 2 +- example/ios/Podfile.lock | 4 ++-- src/BlurTarget.tsx | 2 +- src/BlurView.tsx | 14 ++++++-------- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3585184..8cad5d3 100755 --- a/README.md +++ b/README.md @@ -439,7 +439,7 @@ The blur effect on Android has always been a challenge. Therefore, version 2.0.0 On Android platforms, the component utilizes the [BlurView](https://github.com/Dimezis/BlurView) library to offer native blur effects with hardware-accelerated rendering. -For different types of `x-light`, `light`, and `dark`, the `radius` is fixed at `35` and the `downscaleFactor` is only 66% of the stated value. This is done to maintain similarity with the iOS effect. +For different types of `x-light`, `light`, and `dark`, the `radius` is fixed at `35`. This is done to maintain similarity with the iOS effect. The `androidColor` property can be useful when you want to achieve a specific look or match the blur effect to other elements in your app. It **overrides** the `type` property. diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 21bca78..2011e0e 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - BlurView (2.2.0): + - BlurView (2.3.1): - DoubleConversion - glog - hermes-engine @@ -2195,7 +2195,7 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - BlurView: 1b4e4ddd595512d354f5a0c3621c6a1659462021 + BlurView: 228736ad9914f6d37bb68dba81bcb667bd261716 boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 diff --git a/src/BlurTarget.tsx b/src/BlurTarget.tsx index a6c61fe..7619eb5 100644 --- a/src/BlurTarget.tsx +++ b/src/BlurTarget.tsx @@ -21,7 +21,7 @@ import type { BlurTargetProps } from './@types'; * import { styles } from './styles'; * * const MyComponent = () => { - * const blurTargetRef = useRef(null); + * const blurTargetRef = useRef(null); * * return ( * diff --git a/src/BlurView.tsx b/src/BlurView.tsx index 020e120..48f8acd 100755 --- a/src/BlurView.tsx +++ b/src/BlurView.tsx @@ -28,7 +28,7 @@ import { globalStyles } from './styles'; * import { styles } from './styles'; * * const MyComponent = () => { - * const blurTargetRef = useRef(null); + * const blurTargetRef = useRef(null); * * return ( * @@ -69,20 +69,19 @@ export const BlurView = forwardRef((props, ref) => { const commonProps = useMemo(() => { const isPrimary = type === 'x-light' || type === 'light' || type === 'dark'; const _radius = isPrimary ? radius : 35; - const _downscaleFactor = isPrimary - ? downscaleFactor - : downscaleFactor * 0.66; return { + ref, androidColor, + downscaleFactor, targetId: isAndroid ? targetId : undefined, reducedTransparencyFallbackColor, - downscaleFactor: _downscaleFactor, overlayColor: type, radius: isAndroid ? _radius : radius, ...rest, }; }, [ + ref, type, radius, downscaleFactor, @@ -102,7 +101,6 @@ export const BlurView = forwardRef((props, ref) => { return ( @@ -115,14 +113,14 @@ export const BlurView = forwardRef((props, ref) => { style={[globalStyles.container, style, !isAndroid && backgroundColor]} > {isAndroid ? ( - + {children} ) : ( <> - + {children} From 61eb771ef6420c3a8003453d2da88a62e55cc9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sans=C3=A3o=20Araldi?= Date: Mon, 11 May 2026 14:52:36 -0300 Subject: [PATCH 2/4] fix(android): ensure the blur view updates it's overlay color based on the UI mode new configuration --- .../java/com/blurview/BlurOverlayColor.kt | 40 +++++++++---------- .../src/main/java/com/blurview/BlurView.kt | 18 ++++++++- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/android/src/main/java/com/blurview/BlurOverlayColor.kt b/android/src/main/java/com/blurview/BlurOverlayColor.kt index 8c7abf0..d4d2154 100644 --- a/android/src/main/java/com/blurview/BlurOverlayColor.kt +++ b/android/src/main/java/com/blurview/BlurOverlayColor.kt @@ -1,50 +1,50 @@ package com.blurview +import android.content.res.Configuration import android.graphics.Color enum class BlurOverlayColor(val color: Int) { X_LIGHT(Color.argb(140, 240, 240, 240)), LIGHT(Color.argb(42, 255, 255, 255)), DARK(Color.argb(120, 26, 22, 22)), - REGULAR(Color.argb(35, 255, 255, 255)), - PROMINENT(Color.argb(140, 240, 240, 240)), - ULTRA_THIN_MATERIAL(Color.argb(75, 240, 240, 240)), - ULTRA_THIN_MATERIAL_LIGHT(Color.argb(77, 240, 240, 240)), + REGULAR_LIGHT(Color.argb(35, 255, 255, 255)), + REGULAR_DARK(Color.argb(35, 28, 28, 30)), + PROMINENT_LIGHT(Color.argb(140, 240, 240, 240)), + PROMINENT_DARK(Color.argb(140, 28, 28, 30)), + ULTRA_THIN_MATERIAL_LIGHT(Color.argb(75, 240, 240, 240)), ULTRA_THIN_MATERIAL_DARK(Color.argb(65, 40, 40, 40)), - THIN_MATERIAL(Color.argb(102, 240, 240, 240)), - THIN_MATERIAL_LIGHT(Color.argb(105, 240, 240, 240)), + THIN_MATERIAL_LIGHT(Color.argb(102, 240, 240, 240)), THIN_MATERIAL_DARK(Color.argb(102, 35, 35, 35)), - MATERIAL(Color.argb(140, 245, 245, 245)), - MATERIAL_LIGHT(Color.argb(140, 248, 248, 248)), + MATERIAL_LIGHT(Color.argb(140, 245, 245, 245)), MATERIAL_DARK(Color.argb(215, 65, 60, 60)), - THICK_MATERIAL(Color.argb(210, 248, 248, 248)), - THICK_MATERIAL_LIGHT(Color.argb(212, 248, 248, 248)), + THICK_MATERIAL_LIGHT(Color.argb(210, 248, 248, 248)), THICK_MATERIAL_DARK(Color.argb(165, 35, 35, 35)), - CHROME_MATERIAL(Color.argb(165, 248, 248, 248)), - CHROME_MATERIAL_LIGHT(Color.argb(167, 248, 248, 248)), + CHROME_MATERIAL_LIGHT(Color.argb(165, 248, 248, 248)), CHROME_MATERIAL_DARK(Color.argb(100, 32, 32, 32)); companion object { - fun fromString(color: String): BlurOverlayColor { + fun fromString(color: String, configuration: Configuration): BlurOverlayColor { + val isDarkMode = (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES + return when (color.lowercase()) { "x-light" -> X_LIGHT "light" -> LIGHT "dark" -> DARK - "regular" -> REGULAR - "prominent" -> PROMINENT - "ultra-thin-material" -> ULTRA_THIN_MATERIAL + "regular" -> if (isDarkMode) REGULAR_DARK else REGULAR_LIGHT + "prominent" -> if (isDarkMode) PROMINENT_DARK else PROMINENT_LIGHT + "ultra-thin-material" -> if (isDarkMode) ULTRA_THIN_MATERIAL_DARK else ULTRA_THIN_MATERIAL_LIGHT "ultra-thin-material-light" -> ULTRA_THIN_MATERIAL_LIGHT "ultra-thin-material-dark" -> ULTRA_THIN_MATERIAL_DARK - "thin-material" -> THIN_MATERIAL + "thin-material" -> if (isDarkMode) THIN_MATERIAL_DARK else THIN_MATERIAL_LIGHT "thin-material-light" -> THIN_MATERIAL_LIGHT "thin-material-dark" -> THIN_MATERIAL_DARK - "material" -> MATERIAL + "material" -> if (isDarkMode) MATERIAL_DARK else MATERIAL_LIGHT "material-light" -> MATERIAL_LIGHT "material-dark" -> MATERIAL_DARK - "thick-material" -> THICK_MATERIAL + "thick-material" -> if (isDarkMode) THICK_MATERIAL_DARK else THICK_MATERIAL_LIGHT "thick-material-light" -> THICK_MATERIAL_LIGHT "thick-material-dark" -> THICK_MATERIAL_DARK - "chrome-material" -> CHROME_MATERIAL + "chrome-material" -> if (isDarkMode) CHROME_MATERIAL_DARK else CHROME_MATERIAL_LIGHT "chrome-material-light" -> CHROME_MATERIAL_LIGHT "chrome-material-dark" -> CHROME_MATERIAL_DARK else -> LIGHT diff --git a/android/src/main/java/com/blurview/BlurView.kt b/android/src/main/java/com/blurview/BlurView.kt index 449df90..b50a847 100755 --- a/android/src/main/java/com/blurview/BlurView.kt +++ b/android/src/main/java/com/blurview/BlurView.kt @@ -1,6 +1,7 @@ package com.blurview import android.content.Context +import android.content.res.Configuration import android.os.Build import android.util.AttributeSet import android.util.Log @@ -15,7 +16,8 @@ import com.facebook.react.uimanager.common.UIManagerType class BlurView : eightbitlab.com.blurview.BlurView { private var targetId: Int? = null private var androidColor: Int? = null - private var overlayColor: BlurOverlayColor = BlurOverlayColor.fromString("light") + private var colorString: String = "light" + private var overlayColor: BlurOverlayColor = BlurOverlayColor.fromString(this.colorString, resources.configuration) private var radius: Float = 10f * INTENSITY private var downscaleFactor: Float = 6f private var targetView: TargetView? = null @@ -86,6 +88,17 @@ class BlurView : eightbitlab.com.blurview.BlurView { // conflict with React Native's layout. } + /** + * Handle configuration changes, such as dark mode or orientation changes. + * This ensures the blur view updates its overlay color based on the new + * configuration. + */ + override fun onConfigurationChanged(config: Configuration) { + super.onConfigurationChanged(config) + + this.setOverlayColor(this.colorString) + } + private fun setupBlurView() { val color = this.getColorForBlur() @@ -199,9 +212,10 @@ class BlurView : eightbitlab.com.blurview.BlurView { } fun setOverlayColor(overlayColor: String) { - val overlay = BlurOverlayColor.fromString(overlayColor) + val overlay = BlurOverlayColor.fromString(overlayColor, resources.configuration) this.overlayColor = overlay + this.colorString = overlayColor if (this.androidColor != null) return; From 9043ea1d93b6685e5b8896c01b76679963c0a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sans=C3=A3o=20Araldi?= Date: Mon, 11 May 2026 15:06:50 -0300 Subject: [PATCH 3/4] fix(example): update text color based on the UI mode --- example/src/constants/blur.ts | 14 +++++++++++-- example/src/screens/Blurs/index.tsx | 11 +++++++--- example/src/screens/Settings/index.tsx | 16 +++++++-------- example/src/screens/Vibrancies/index.tsx | 26 ++++++++++++++++++------ 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/example/src/constants/blur.ts b/example/src/constants/blur.ts index 5af076b..f8b5346 100644 --- a/example/src/constants/blur.ts +++ b/example/src/constants/blur.ts @@ -6,6 +6,16 @@ export const NAVIGATION_ICONS = { Settings: '⚙️', } as const; +export const BLUR_UI_MODES = [ + 'regular', + 'prominent', + 'material', + 'thin-material', + 'chrome-material', + 'thick-material', + 'ultra-thin-material', +] as const; + export const BLUR_TYPES_DATA: BlurTypeData[] = [ { type: 'x-light', @@ -122,7 +132,7 @@ export const EFFECT_STYLES_DATA: EffectStyleData[] = [ style: 'separator', label: 'Separator ➗', }, -]; +] as const; export const BLUR_RADIUS_DATA: BlurRadiusData[] = [ { @@ -169,4 +179,4 @@ export const BLUR_RADIUS_DATA: BlurRadiusData[] = [ radius: 100, label: 'Blur radius 100', }, -]; +] as const; diff --git a/example/src/screens/Blurs/index.tsx b/example/src/screens/Blurs/index.tsx index 46af3a9..5a5b953 100644 --- a/example/src/screens/Blurs/index.tsx +++ b/example/src/screens/Blurs/index.tsx @@ -5,16 +5,18 @@ import { TouchableOpacity, View, ScrollView, + useColorScheme, } from 'react-native'; import { BlurTarget, BlurView } from '@danielsaraldi/react-native-blur-view'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useBlur } from '../../hooks'; import { MOUNTAIN } from '../../assets'; -import { BLUR_TYPES_DATA } from '../../constants'; +import { BLUR_TYPES_DATA, BLUR_UI_MODES } from '../../constants'; import { makeStyles } from './styles'; export function Blurs() { const targetRef = useRef(null); + const colorScheme = useColorScheme(); const { top, bottom } = useSafeAreaInsets(); const { radius, onBlurType } = useBlur(); @@ -23,7 +25,10 @@ export function Blurs() { const renderBlurs = useMemo( () => BLUR_TYPES_DATA.map(({ type, label }) => { - const color = type.includes('dark') ? 'white' : 'black'; + const colorByType = type.includes('dark') ? 'white' : 'black'; + const isDarkMode = colorScheme === 'dark'; + const isUIMode = BLUR_UI_MODES.some((mode) => type === mode); + const color = isUIMode ? (isDarkMode ? 'white' : 'black') : colorByType; return ( ); }), - [radius, styles, onBlurType] + [radius, styles, colorScheme, onBlurType] ); return ( diff --git a/example/src/screens/Settings/index.tsx b/example/src/screens/Settings/index.tsx index 7807923..6da68d0 100644 --- a/example/src/screens/Settings/index.tsx +++ b/example/src/screens/Settings/index.tsx @@ -6,6 +6,7 @@ import { ScrollView, Text, TouchableOpacity, + useColorScheme, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -22,7 +23,7 @@ import Animated, { } from 'react-native-reanimated'; import { useBlur } from '../../hooks'; import { PORSCHE_ARCHITECTURE } from '../../assets'; -import { BLUR_RADIUS_DATA } from '../../constants'; +import { BLUR_RADIUS_DATA, BLUR_UI_MODES } from '../../constants'; import { makeStyles } from './styles'; import { isIos } from '../../utils'; @@ -34,12 +35,16 @@ export function Settings() { const targetRef = useRef(null); const scrollTargetRef = useRef(null); + const colorScheme = useColorScheme(); const { top, bottom } = useSafeAreaInsets(); const { blurType, effectStyle, radius, isDark, onRadius } = useBlur(); const radiusAnimation = useSharedValue(0.01); - const color = blurType.includes('dark') ? 'white' : 'black'; + const colorByType = blurType.includes('dark') ? 'white' : 'black'; + const isDarkMode = colorScheme === 'dark'; + const isUIMode = BLUR_UI_MODES.some((mode) => blurType === mode); + const color = isUIMode ? (isDarkMode ? 'white' : 'black') : colorByType; const defaultMessage = isIos ? 'Default blur type is light, radius is 10 and vibrancy effect style is label.' : 'Default blur type is light and default radius is 10.'; @@ -109,12 +114,7 @@ export function Settings() { style={styles.absoluteFill} /> - + Explore radius and type configurations to customize the blur effect ✨{'\n'}Adjust the settings to see how they impact the appearance of the blur on both Android and iOS platforms 🌫️ diff --git a/example/src/screens/Vibrancies/index.tsx b/example/src/screens/Vibrancies/index.tsx index 997a914..a78201f 100644 --- a/example/src/screens/Vibrancies/index.tsx +++ b/example/src/screens/Vibrancies/index.tsx @@ -4,6 +4,7 @@ import { ScrollView, Text, TouchableOpacity, + useColorScheme, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -13,19 +14,32 @@ import { } from '@danielsaraldi/react-native-blur-view'; import { useBlur } from '../../hooks'; import { PORSCHE_MOUNTAIN } from '../../assets'; -import { BLUR_TYPES_DATA, EFFECT_STYLES_DATA } from '../../constants'; +import { + BLUR_TYPES_DATA, + BLUR_UI_MODES, + EFFECT_STYLES_DATA, +} from '../../constants'; import { makeStyles } from './styles'; export function Vibrancies() { + const colorScheme = useColorScheme(); const { radius, blurType, effectStyle, onBlurType, onEffectStyle } = useBlur(); const { top, bottom } = useSafeAreaInsets(); - const getTextColor = useCallback((type: BlurType) => { - const exceptions = ['x-light', 'light', 'dark', 'regular', 'prominent']; - const color = type.includes('dark') ? 'white' : 'black'; - return exceptions.some((exception) => exception === type) ? 'white' : color; - }, []); + const getTextColor = useCallback( + (type: BlurType) => { + const exceptions = ['x-light', 'light', 'dark', 'regular', 'prominent']; + const isException = exceptions.some((exception) => exception === type); + if (isException) return 'white'; + + const isDarkMode = colorScheme === 'dark'; + const isUIMode = BLUR_UI_MODES.some((mode) => type === mode); + const colorByType = type.includes('dark') ? 'white' : 'black'; + return isUIMode ? (isDarkMode ? 'white' : 'black') : colorByType; + }, + [colorScheme] + ); const styles = useMemo(() => makeStyles({ top, bottom }), [top, bottom]); From 3848c6232a4a70e1f1a460b8d6f6a2001531d198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sans=C3=A3o=20Araldi?= Date: Mon, 11 May 2026 15:20:46 -0300 Subject: [PATCH 4/4] refactor(android): avoid unnecessary reinitialization work during rotations/locale changes --- android/src/main/java/com/blurview/BlurView.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/blurview/BlurView.kt b/android/src/main/java/com/blurview/BlurView.kt index b50a847..a3af73e 100755 --- a/android/src/main/java/com/blurview/BlurView.kt +++ b/android/src/main/java/com/blurview/BlurView.kt @@ -96,6 +96,9 @@ class BlurView : eightbitlab.com.blurview.BlurView { override fun onConfigurationChanged(config: Configuration) { super.onConfigurationChanged(config) + val currentOverlayColor = BlurOverlayColor.fromString(this.colorString, config) + if (this.androidColor != null || currentOverlayColor == this.overlayColor) return + this.setOverlayColor(this.colorString) } @@ -217,7 +220,7 @@ class BlurView : eightbitlab.com.blurview.BlurView { this.overlayColor = overlay this.colorString = overlayColor - if (this.androidColor != null) return; + if (this.androidColor != null) return super.setBackgroundColor(overlay.color) super.setOverlayColor(overlay.color)