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;

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions