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;
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