fix(Android, FormSheet): Fix dynamic height change for fitToContents
Description
This PR introduces improved support for fitToContents in BottomSheet by modifying its style to absoluteWithNoBottom. This style provides a reference to the current top position of the sheet, making it easier to handle dynamic content within FormSheet.
Having improved control over maxHeight (added in previous PRs), we are now able to implement a smooth resizing animation for the BottomSheet.
[!IMPORTANT] Key assumptions (these should be carefully reviewed)
- Unmounting of components remains the responsibility of the application code. We do not include default enter/exit animations at the component level.
- Visual styles such as
backgroundColorshould be applied viacontentStyleon the Screen level. In fitToContents mode, the height of the ScreenContentWrapper should always match the height of the content.- When a component unmounts, the ScreenContentWrapper's size shrinks before we can start the transition. Since its size is necessary to calculate the animation delta, styles should be applied at the Screen level, where we have better control over when layout occurs.
Fixes: https://github.com/software-mansion/react-native-screens/issues/2560
Changes
- Updated style to
absoluteWithNoBottomfor androidfitToContents - Added expand/shrink animations
Screenshots / GIFs
Here you can add screenshots / GIFs documenting your change.
You can add before / after section if you're changing some behavior.
Before
https://github.com/user-attachments/assets/b9fc3461-31c7-4a71-9a00-6badd1ba3633
After
https://github.com/user-attachments/assets/ef2fcdd9-7942-40c3-8528-d4d8fdcf4e0e
Test code and steps to reproduce
Test2560
Checklist
- [x] Included code example that can be used to test this change
- [x] Ensured that CI passes
NOW it's feels like Christmas.
🎉
Also, not sure if this is a real concern but I was able to make the form sheet not adapt to content by changing the content quickly:
Screen.Recording.2025-12-18.at.09.04.29.mov I used
TestFormSheetwithfitToContentsand just added some text at the bottom.
checking..
Also, not sure if this is a real concern but I was able to make the form sheet not adapt to content by changing the content quickly:
Screen.Recording.2025-12-18.at.09.04.29.mov I used
TestFormSheetwithfitToContentsand just added some text at the bottom.
Should be fixed, tested with
import React, { useRef, useState } from 'react';
import {
Button,
View,
Text,
StyleSheet,
Animated,
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackScreenProps,
} from '@react-navigation/native-stack';
import Colors from '../shared/styling/Colors';
type StackParamList = {
Home: undefined;
FormSheet: undefined;
};
const Stack = createNativeStackNavigator<StackParamList>();
const HomeScreen = ({ navigation }: NativeStackScreenProps<StackParamList>) => (
<View style={styles.screen}>
<Text style={styles.title}>Home Screen</Text>
<Button
title="Open Form Sheet"
onPress={() => navigation.navigate('FormSheet')}
/>
</View>
);
const FormSheetScreen = () => {
const [isTextVisible, setIsTextVisible] = useState(true);
React.useEffect(() => {
const timer = setTimeout(() => {
setIsTextVisible(false);
}, 1000);
return () => clearTimeout(timer);
}, []);
React.useEffect(() => {
const timer = setTimeout(() => {
setIsTextVisible(true);
}, 1016);
return () => clearTimeout(timer);
}, []);
return (
<View style={styles.formSheetContainer}>
<Text style={styles.formSheetTitle}>Form Sheet Content</Text>
{isTextVisible && (
<Text>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Velit deserunt
qui fugit unde assumenda. Non id facere mollitia sequi aliquid nostrum,
tenetur eligendi quos at natus eius, corporis quidem eaque libero
reiciendis, labore sed minima doloremque temporibus! Veritatis harum
provident molestias incidunt at temporibus quod ipsam totam optio,
quisquam amet quae molestiae. Exercitationem animi facere iure, nobis
nulla repudiandae aut nam natus! Qui, id? Nobis quia recusandae
repellendus illum voluptas, ipsa, doloribus reprehenderit nulla quas
facere eos! Alias, accusantium voluptatibus doloremque unde eius totam
et officia asperiores? Voluptate totam provident, quas inventore
doloribus autem nihil quisquam soluta eum, dolorum nobis.{' '}
</Text>
)}
</View>
);
};
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="FormSheet"
component={FormSheetScreen}
options={{
presentation: 'formSheet',
sheetAllowedDetents: 'fitToContents',
contentStyle: {
backgroundColor: Colors.YellowLight40,
},
// TODO(@t0maboro) - add `sheetContentDefaultResizeAnimationEnabled` prop here when possible
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
title: {
fontSize: 24,
marginBottom: 20,
},
formSheetContainer: {
alignItems: 'center',
justifyContent: 'center',
padding: 20,
gap: 20,
},
formSheetTitle: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 10,
},
text: {
fontSize: 16,
marginBottom: 8,
color: Colors.White,
},
rectangle: {
width: '100%',
backgroundColor: Colors.NavyLight80,
height: 200,
alignItems: 'center',
justifyContent: 'center',
},
input: {
width: 100,
height: 20,
borderColor: Colors.BlueDark100,
borderWidth: 1,
},
});
Thank you very much for this PR — looking forward to seeing it merged. :)