From 867f75cfc7dae18c2d9662c226d8bbc604d4eff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sans=C3=A3o=20Araldi?= Date: Sat, 2 May 2026 17:22:09 -0300 Subject: [PATCH 1/2] feat(android): add blur rounds support for handle blur interactions --- README.md | 27 +++++++++++-------- .../reactnativeblur/ReactNativeBlurSwitch.kt | 15 +++++++++++ .../ReactNativeBlurSwitchManager.kt | 5 ++++ .../reactnativeblur/ReactNativeBlurView.kt | 15 +++++++++++ .../ReactNativeBlurViewManager.kt | 5 ++++ .../ReactNativeProgressiveBlurView.kt | 15 +++++++++++ .../ReactNativeProgressiveBlurViewManager.kt | 5 ++++ example/package.json | 2 +- src/BlurSwitch.tsx | 26 +++++++++++++++--- src/BlurView.tsx | 12 +++++++++ src/ProgressiveBlurView.tsx | 13 +++++++++ src/ReactNativeBlurSwitchNativeComponent.ts | 2 ++ src/ReactNativeBlurViewNativeComponent.ts | 2 ++ ...ativeProgressiveBlurViewNativeComponent.ts | 2 ++ 14 files changed, 131 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7c82faf..be7645a 100644 --- a/README.md +++ b/README.md @@ -626,6 +626,7 @@ All props are optional and have sensible defaults. | ---------------------------------- | ------------ | ----------- | ----------------------------------------------------------------------------- | | `blurType` | `BlurType` | `'xlight'` | The type of blur effect to apply | | `blurAmount` | `number` | `10.0` | The intensity of the blur effect (0-100) | +| `blurRounds` | `number` | `5` | The number of blur interactions must be an integer value (1-15) | | `ignoreSafeArea` | `boolean` | `true` | (iOS only) Controls whether the blur effect should ignore all safe area edges | | `reducedTransparencyFallbackColor` | `string` | `'#FFFFFF'` | Fallback color when reduced transparency is enabled | | `overlayColor` | `ColorValue` | `undefined` | The overlay color to apply on top of the blur effect | @@ -647,16 +648,17 @@ All props are optional and have sensible defaults. All props are optional and have sensible defaults. -| Prop | Type | Default | Description | -| ---------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------- | ---------------------------------------------------- | -| `blurType` | `BlurType` | `'regular'` | The type of blur effect to apply | -| `blurAmount` | `number` | `20.0` | Maximum blur radius in pixels | -| `direction` | `'blurredTopClearBottom' \| 'blurredBottomClearTop' \| 'blurredCenterClearTopAndBottom'` | `'blurredTopClearBottom'` | Direction of the blur gradient | -| `startOffset` | `number` | `0.0` | Where the gradient starts (0.0 to 1.0) | -| `reducedTransparencyFallbackColor` | `string` | `'#FFFFFF'` | Fallback color when reduced transparency is enabled | -| `overlayColor` | `ColorValue` | `undefined` | The overlay color to apply on top of the blur effect | -| `style` | `ViewStyle` | `undefined` | Style object for the blur view | -| `children` | `ReactNode` | `undefined` | Child components to render inside the blur view | +| Prop | Type | Default | Description | +| ---------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------- | --------------------------------------------------------------- | +| `blurType` | `BlurType` | `'regular'` | The type of blur effect to apply | +| `blurAmount` | `number` | `20.0` | Maximum blur radius in pixels | +| `blurRounds` | `number` | `5` | The number of blur interactions must be an integer value (1-15) | +| `direction` | `'blurredTopClearBottom' \| 'blurredBottomClearTop' \| 'blurredCenterClearTopAndBottom'` | `'blurredTopClearBottom'` | Direction of the blur gradient | +| `startOffset` | `number` | `0.0` | Where the gradient starts (0.0 to 1.0) | +| `reducedTransparencyFallbackColor` | `string` | `'#FFFFFF'` | Fallback color when reduced transparency is enabled | +| `overlayColor` | `ColorValue` | `undefined` | The overlay color to apply on top of the blur effect | +| `style` | `ViewStyle` | `undefined` | Style object for the blur view | +| `children` | `ReactNode` | `undefined` | Child components to render inside the blur view | > **Platform Note**: `ProgressiveBlurView` works on both **iOS** and **Android**. > @@ -696,7 +698,8 @@ All props are optional and have sensible defaults. | --------------- | ------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `value` | `boolean` | `false` | The current value of the switch | | `onValueChange` | `(value: boolean) => void` | `undefined` | Callback invoked when the switch value changes | -| `blurAmount` | `number` | `10` | (Android only) The intensity of the blur effect (0-100) | +| `blurAmount` | `number` | `10.0` | (Android only) The intensity of the blur effect (0-100) | +| `blurRounds` | `number` | `5` | The number of blur interactions must be an integer value (1-15) | | `thumbColor` | `ColorValue` | `'#FFFFFF'` | (iOS only) The color of the switch thumb | | `trackColor` | `{ false?: ColorValue; true?: ColorValue }` | `{ false: '#E5E5EA', true: '#34C759' }` | Track colors. On Android, only `true` is used - QmBlurView auto-calculates on/off colors from base color | | `disabled` | `boolean` | `false` | Whether the switch is disabled (prevents interaction but maintains current value) | @@ -845,6 +848,7 @@ interface MyGlassContainerProps { const blurProps: BlurViewProps = { blurType: 'systemMaterial', blurAmount: 50, + blurRounds: 10, reducedTransparencyFallbackColor: '#FFFFFF', overlayColor: '#FF000040', }; @@ -867,6 +871,7 @@ const blurSwitchProps: BlurSwitchProps = { value: true, onValueChange: (value) => console.log(value), blurAmount: 20, + blurRounds: 15, trackColor: { true: '#34C759' }, disabled: false, }; diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt index 36fb82c..4bb57fb 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt @@ -172,6 +172,21 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView { } } + /** + * Set the number of blur rounds. + * @param rounds The number of blur rounds (1-15) + */ + fun setRounds(rounds: Int) { + val blurRounds = rounds.coerceIn(1, 15) + logDebug("setRounds: $rounds -> $blurRounds") + + try { + super.setBlurRounds(blurRounds) + } catch (e: Exception) { + logError("Failed to set blur rounds: ${e.message}", e) + } + } + /** * Set whether the switch is disabled. * @param disabled True to disable, false to enable diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitchManager.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitchManager.kt index 33c4e4b..2fbf39e 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitchManager.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitchManager.kt @@ -40,6 +40,11 @@ class ReactNativeBlurSwitchManager : SimpleViewManager() view?.setBlurAmount(blurAmount.toFloat()) } + @ReactProp(name = "blurRounds") + fun setBlurRounds(view: ReactNativeBlurSwitch?, blurRounds: Int) { + view?.setRounds(blurRounds) + } + @ReactProp(name = "thumbColor") fun setThumbColor(view: ReactNativeBlurSwitch?, color: String?) { color?.let { diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt index 6a7658e..2568ca8 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt @@ -290,6 +290,21 @@ class ReactNativeBlurView : BlurViewGroup { } } + /** + * Set the number of blur rounds. + * @param rounds The number of blur rounds (1-15) + */ + fun setRounds(rounds: Int) { + val blurRounds = rounds.coerceIn(1, 15) + logDebug("setRounds: $rounds -> $blurRounds") + + try { + super.setBlurRounds(blurRounds) + } catch (e: Exception) { + logError("Failed to set blur rounds: ${e.message}", e) + } + } + /** * Set the blur type which determines the overlay color. * @param type The blur type string (case-insensitive) diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurViewManager.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurViewManager.kt index 64ac152..6dfa860 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurViewManager.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurViewManager.kt @@ -39,6 +39,11 @@ class ReactNativeBlurViewManager : ViewGroupManager(), view?.setBlurAmount(blurAmount.toFloat()) } + @ReactProp(name = "blurRounds") + override fun setBlurRounds(view: ReactNativeBlurView?, blurRounds: Int) { + view?.setRounds(blurRounds) + } + @ReactProp(name = "borderRadius") override fun setBorderRadius(view: ReactNativeBlurView?, borderRadius: Float) { view?.setBorderRadius(borderRadius) diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt index 0ae1848..78719a9 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt @@ -460,6 +460,21 @@ class ReactNativeProgressiveBlurView : FrameLayout { } } + /** + * Set the number of blur rounds. + * @param rounds The number of blur rounds (1-15) + */ + fun setRounds(rounds: Int) { + val blurRounds = rounds.coerceIn(1, 15) + logDebug("setRounds: $rounds -> $blurRounds") + + try { + blurView?.blurRounds = blurRounds + } catch (e: Exception) { + logError("Failed to set blur rounds: ${e.message}", e) + } + } + /** * Set the direction of the progressive blur gradient. * @param direction The direction string: "blurredTopClearBottom" or "blurredBottomClearTop" diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurViewManager.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurViewManager.kt index e69ce6f..11bff44 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurViewManager.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurViewManager.kt @@ -45,6 +45,11 @@ class ReactNativeProgressiveBlurViewManager : SimpleViewManager = ({ value = false, - onValueChange, blurAmount = 10, + blurRounds = 5, + onValueChange, thumbColor = '#FFFFFF', trackColor = { false: '#E5E5EA', true: '#34C759' }, disabled = false, @@ -117,12 +129,13 @@ export const BlurSwitch: React.FC = ({ return ( { onValueChange?.(event.nativeEvent.value); }} blurAmount={blurAmount} + blurRounds={blurRounds} thumbColor={toColorString(thumbColor, '#FFFFFF')} trackColorOff={toColorString(trackColor?.false, '#E5E5EA')} trackColorOn={toColorString(trackColor?.true, '#34C759')} @@ -132,4 +145,11 @@ export const BlurSwitch: React.FC = ({ ); }; +const styles = StyleSheet.create({ + switch: { + width: 65, + height: 36, + }, +}); + export default BlurSwitch; diff --git a/src/BlurView.tsx b/src/BlurView.tsx index 6e5b887..a4cc495 100644 --- a/src/BlurView.tsx +++ b/src/BlurView.tsx @@ -20,6 +20,16 @@ export interface BlurViewProps { */ blurAmount?: number; + /** + * @description The number of blur interactions to perform for a smoother + * effect (1-15) + * + * @default 5 + * + * @platform Android + */ + blurRounds?: number; + /** * @description Fallback color when reduced transparency is enabled * @@ -83,6 +93,7 @@ export interface BlurViewProps { export const BlurView: React.FC = ({ blurType = 'xlight', blurAmount = 10, + blurRounds = 5, reducedTransparencyFallbackColor = '#FFFFFF', overlayColor, style, @@ -94,6 +105,7 @@ export const BlurView: React.FC = ({ const commonProps: BlurViewProps = { blurType, blurAmount, + blurRounds, ignoreSafeArea, reducedTransparencyFallbackColor, }; diff --git a/src/ProgressiveBlurView.tsx b/src/ProgressiveBlurView.tsx index b8375ad..b331cac 100644 --- a/src/ProgressiveBlurView.tsx +++ b/src/ProgressiveBlurView.tsx @@ -22,6 +22,16 @@ export interface ProgressiveBlurViewProps { */ blurAmount?: number; + /** + * @description The number of blur interactions to perform for a smoother + * effect (1-15) + * + * @default 5 + * + * @platform Android + */ + blurRounds?: number; + /** * @description The direction of the progressive blur gradient * - 'blurredTopClearBottom': Blur starts at top, clear at bottom @@ -115,6 +125,7 @@ export interface ProgressiveBlurViewProps { export const ProgressiveBlurView: React.FC = ({ blurType = 'regular', blurAmount = 20, + blurRounds = 5, direction = 'blurredTopClearBottom', startOffset = 0.0, reducedTransparencyFallbackColor = '#FFFFFF', @@ -131,6 +142,7 @@ export const ProgressiveBlurView: React.FC = ({ = ({ ; blurAmount?: WithDefault; + blurRounds?: WithDefault; thumbColor?: WithDefault; trackColorOff?: WithDefault; trackColorOn?: WithDefault; diff --git a/src/ReactNativeBlurViewNativeComponent.ts b/src/ReactNativeBlurViewNativeComponent.ts index 90d1960..6d4c4d8 100644 --- a/src/ReactNativeBlurViewNativeComponent.ts +++ b/src/ReactNativeBlurViewNativeComponent.ts @@ -3,6 +3,7 @@ import type { ViewProps } from 'react-native'; import type { WithDefault, Double, + Int32, } from 'react-native/Libraries/Types/CodegenTypes'; export type BlurType = @@ -31,6 +32,7 @@ export type BlurType = interface NativeProps extends ViewProps { blurAmount?: WithDefault; blurType?: WithDefault; + blurRounds?: WithDefault; reducedTransparencyFallbackColor?: WithDefault; ignoreSafeArea?: WithDefault; } diff --git a/src/ReactNativeProgressiveBlurViewNativeComponent.ts b/src/ReactNativeProgressiveBlurViewNativeComponent.ts index f238cad..4bd4666 100644 --- a/src/ReactNativeProgressiveBlurViewNativeComponent.ts +++ b/src/ReactNativeProgressiveBlurViewNativeComponent.ts @@ -3,6 +3,7 @@ import type { ViewProps } from 'react-native'; import type { WithDefault, Double, + Int32, } from 'react-native/Libraries/Types/CodegenTypes'; export type BlurType = @@ -36,6 +37,7 @@ export type ProgressiveBlurDirection = interface NativeProps extends ViewProps { blurAmount?: WithDefault; blurType?: WithDefault; + blurRounds?: WithDefault; direction?: WithDefault; startOffset?: WithDefault; reducedTransparencyFallbackColor?: WithDefault; From 91af23ee40a41c0911e1173353cfdacec8743c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sans=C3=A3o=20Araldi?= Date: Sat, 2 May 2026 17:57:40 -0300 Subject: [PATCH 2/2] feat(android): re-apply blur rounds in the `onAttachedToWindow` call --- .../com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt | 5 ++++- .../com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt | 5 ++++- .../reactnativeblur/ReactNativeProgressiveBlurView.kt | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt index 4bb57fb..1317567 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt @@ -22,9 +22,11 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView { private var onValueChangeListener: ((Boolean) -> Unit)? = null private var currentValue = false private var isDisabled = false + private var currentBlurRounds = DEFAULT_BLUR_ROUNDS companion object { private const val TAG = "ReactNativeBlurSwitch" + private const val DEFAULT_BLUR_ROUNDS = 5 private const val DEFAULT_WIDTH_DP = 65f private const val DEFAULT_HEIGHT_DP = 36f private const val MIN_BLUR_AMOUNT = 0f @@ -70,7 +72,7 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView { */ private fun initializeSwitch() { try { - blurRounds = 5 + blurRounds = currentBlurRounds setOnCheckedChangeListener { isChecked -> if (isDisabled) { @@ -178,6 +180,7 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView { */ fun setRounds(rounds: Int) { val blurRounds = rounds.coerceIn(1, 15) + currentBlurRounds = blurRounds logDebug("setRounds: $rounds -> $blurRounds") try { diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt index 2568ca8..7bd9bd9 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt @@ -31,6 +31,7 @@ import android.view.View.MeasureSpec class ReactNativeBlurView : BlurViewGroup { private var currentBlurRadius = DEFAULT_BLUR_RADIUS private var currentOverlayColor = Color.TRANSPARENT + private var currentBlurRounds = DEFAULT_BLUR_ROUNDS private var currentCornerRadius = 0f private var glassTintColor: Int = Color.TRANSPARENT private var glassOpacity: Float = 1.0f @@ -44,6 +45,7 @@ class ReactNativeBlurView : BlurViewGroup { private const val TAG = "ReactNativeBlurView" private const val MAX_BLUR_RADIUS = 100f private const val DEFAULT_BLUR_RADIUS = 10f + private const val DEFAULT_BLUR_ROUNDS = 5 private const val DEBUG = false // Cross-platform blur amount constants @@ -93,7 +95,7 @@ class ReactNativeBlurView : BlurViewGroup { super.setBackgroundColor(currentOverlayColor) clipChildren = true clipToOutline = true - blurRounds = 5 + blurRounds = currentBlurRounds super.setDownsampleFactor(6.0F) } @@ -296,6 +298,7 @@ class ReactNativeBlurView : BlurViewGroup { */ fun setRounds(rounds: Int) { val blurRounds = rounds.coerceIn(1, 15) + currentBlurRounds = blurRounds logDebug("setRounds: $rounds -> $blurRounds") try { diff --git a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt index 78719a9..dff40cb 100644 --- a/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt +++ b/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt @@ -31,6 +31,7 @@ class ReactNativeProgressiveBlurView : FrameLayout { private val gradientPaint = Paint(Paint.ANTI_ALIAS_FLAG) private var currentBlurRadius = DEFAULT_BLUR_RADIUS + private var currentBlurRounds = DEFAULT_BLUR_ROUNDS private var currentOverlayColor = Color.TRANSPARENT private var currentBlurType = "xlight" private var currentDirection = "topToBottom" @@ -44,6 +45,7 @@ class ReactNativeProgressiveBlurView : FrameLayout { private const val TAG = "ReactNativeProgressiveBlur" private const val MAX_BLUR_RADIUS = 100f private const val DEFAULT_BLUR_RADIUS = 10f + private const val DEFAULT_BLUR_ROUNDS = 5 private const val DEBUG = false // Cross-platform blur amount constants @@ -131,7 +133,7 @@ class ReactNativeProgressiveBlurView : FrameLayout { blurView = BlurView(context, null).apply { layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) setDownsampleFactor(6.0F) - blurRounds = 5 + blurRounds = currentBlurRounds } addView(blurView) } @@ -466,6 +468,7 @@ class ReactNativeProgressiveBlurView : FrameLayout { */ fun setRounds(rounds: Int) { val blurRounds = rounds.coerceIn(1, 15) + currentBlurRounds = blurRounds logDebug("setRounds: $rounds -> $blurRounds") try {