Skip to content

Bottom tab blur is not working for Android #39

Description

@Sameed1998

Describe the bug
Blur effect is not applied when using BlurView as tabBarBackground in a bottom tab navigator. The blur works correctly on iOS, but on Android it either does not render at all or behaves like a simple transparent/colored view instead of an actual blur

To Reproduce
Steps to reproduce the behavior:

Create a bottom tab navigator using @react-navigation/bottom-tabs

Add BlurView from @danielsaraldi/react-native-blur-view

Use it inside tabBarBackground

Run the app on Android

Observe that blur is not applied behind the tab bar

Expected behavior
The tab bar background should have a real blur effect (frosted glass appearance), similar to iOS behavior, blurring the content behind it.

Desktop:
OS: macOS

Smartphone:
Device: Android (multiple tested)
OS: Android
Library Version: @danielsaraldi/react-native-blur-view 2.1.3

Additional context
The issue seems specific to rendering BlurView inside tabBarBackground.
Regular usage of BlurView in other parts of the screen works inconsistently or requires specific setup.
The tab bar uses position: 'absolute' and overflow: 'hidden', which might be affecting rendering on Android.
iOS works perfectly with the same implementation.

Code snippet

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import React, { useCallback, useMemo } from 'react';
import routes from '../routes';
import * as Screens from '../routes_barrel';
import { HomeFilledIcon, HomeOutlineIcon, UserFilledIcon, UserOutlineIcon } from '@assets/svgs';
import { useAppLocalization, useAppTheme } from '@core/hooks';
import { Platform, StyleSheet } from 'react-native';
import { HPX, WPX } from '@config/ui_config';
import { ColorScheme } from '@visuals/colors';
import { FONT } from '@visuals/typography';
import { ThemedText, ThemedView } from '@component';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { BlurView } from '@danielsaraldi/react-native-blur-view';

interface TabLabelProps {
focused: boolean;
label: string;
colors: ColorScheme;
}

const TabLabel = ({ focused, label, colors }: TabLabelProps) => {
const styles = createBottomNavigatorStyles(colors);

return (
<ThemedView overrideTheme>
<ThemedText
style={[
styles.tabBarLabelStyle,
{ color: focused ? colors.SECONDARY : colors.ON_SECONDARY_VARIANT },
]}
>
{label}
</ThemedText>

{focused && <ThemedView style={styles.underline} overrideTheme />}
</ThemedView>
);
};

const TabBarBackground = (colors: ColorScheme) => {
const styles = createBottomNavigatorStyles(colors);
return (
<BlurView
style={StyleSheet.absoluteFill}
type="light"
radius={2}
overlayColor={colors.WHITE70}
reducedTransparencyFallbackColor={colors.WHITE}
/>
);
};

const Tab = createBottomTabNavigator();

const BottomNavigator = () => {
const { t } = useAppLocalization();
const { colors } = useAppTheme();
const styles = createBottomNavigatorStyles(colors);
const insets = useSafeAreaInsets();

const homeLabel = t('bottomNavigation.home');
const profileLabel = t('bottomNavigation.myProfile');

const renderHomeIcon = useCallback(
({ color, focused }: { color: string; focused: boolean }) =>
focused ? <HomeFilledIcon color={color} /> : <HomeOutlineIcon color={color} />,
[],
);

const renderHomeLabel = useCallback(
({ focused }: { focused: boolean }) => (
<TabLabel focused={focused} label={homeLabel} colors={colors} />
),
[homeLabel, colors],
);

const renderProfileIcon = useCallback(
({ color, focused }: { color: string; focused: boolean }) =>
focused ? <UserFilledIcon color={color} /> : <UserOutlineIcon color={color} />,
[],
);

const renderProfileLabel = useCallback(
({ focused }: { focused: boolean }) => (
<TabLabel focused={focused} label={profileLabel} colors={colors} />
),
[profileLabel, colors],
);

const dashboardOptions = useMemo(
() => ({
tabBarIcon: renderHomeIcon,
tabBarLabel: renderHomeLabel,
tabBarItemStyle: styles.tabBarItemStyleForHome,
}),
[renderHomeIcon, renderHomeLabel, styles.tabBarItemStyleForHome],
);

const profileOptions = useMemo(
() => ({
tabBarIcon: renderProfileIcon,
tabBarLabel: renderProfileLabel,
tabBarItemStyle: styles.tabBarItemStyleForProfile,
}),
[renderProfileIcon, renderProfileLabel, styles.tabBarItemStyleForProfile],
);

return (
<Tab.Navigator
id="BottomTabNavigator"
screenOptions={{
headerShown: false,

tabBarStyle: styles.tabBar,

tabBarBackground: () => TabBarBackground(colors),

tabBarInactiveTintColor: colors.ON_SECONDARY_VARIANT,
tabBarActiveTintColor: colors.SECONDARY,
}}
safeAreaInsets={{
bottom: Platform.OS === 'ios' ? insets.bottom / 2 : insets.bottom,
}}
>
<Tab.Screen
name={routes.DASHBOARD_SCREEN}
component={Screens.DashboardScreen}
options={dashboardOptions}
/>

<Tab.Screen
name={routes.PROFILE_SCREEN}
component={Screens.ProfileScreen}
options={profileOptions}
/>
</Tab.Navigator>
);
};

const createBottomNavigatorStyles = (colors: ColorScheme) =>
StyleSheet.create({
tabBar: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,

backgroundColor: 'transparent',

borderTopWidth: 0,
borderTopLeftRadius: WPX(16),
borderTopRightRadius: WPX(16),

shadowColor: colors.BLACK,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 0,

overflow: 'hidden',
},
tabBarBackgroundAndroid: {
backgroundColor: colors.WHITE90,
},

tabBarItemStyleForHome: {
flex: 1,
alignItems: 'flex-end',
paddingRight: WPX(30),
},

tabBarItemStyleForProfile: {
flex: 1,
alignItems: 'flex-start',
paddingLeft: WPX(30),
},

tabBarLabelStyle: {
fontSize: FONT.Size_01_Weight_500.fontSize,
fontFamily: FONT.Size_01_Weight_500.fontFamily,
},

underline: {
height: HPX(2),
backgroundColor: colors.SECONDARY,
borderRadius: WPX(1),
},
});

export default BottomNavigator;

Metadata

Metadata

Assignees

Labels

androidAndroid onlyenhancementNew feature or requesthelp wantedExtra attention is needed

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions