Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FabricExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"react": "19.0.0",
"react-native": "0.78.0-rc.5",
"react-native-gesture-handler": "2.22.0",
"react-native-reanimated": "^4.0.0-nightly-20250218-e5a0cdf69",
"react-native-reanimated": "4.0.0-nightly-20250218-e5a0cdf69",
"react-native-restart": "^0.0.27",
"react-native-safe-area-context": "5.2.0",
"react-native-screens": "link:../"
Expand Down
9 changes: 9 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.graphics.Paint
import android.os.Parcelable
import android.util.Log
import android.util.SparseArray
import android.view.MotionEvent
import android.view.View
Expand Down Expand Up @@ -132,18 +133,22 @@ class Screen(
) {
val height = bottom - top

Log.i("HT", "Screen [$id] onContentWrapperLayout - $height")

if (usesFormSheetPresentation()) {
if (isSheetFitToContents()) {
sheetBehavior?.useSingleDetent(height)
}

if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// On old architecture we delay enter transition in order to wait for initial frame.
Log.i("HT", "Screen [$id] onContentWrapperLayout - request transition trigger")
shouldTriggerPostponedTransitionAfterLayout = true
val parent = parentAsViewGroup()
if (parent != null && !parent.isInLayout) {
// There are reported cases (irreproducible) when Screen is not laid out after
// maxHeight is set on behaviour.
Log.i("HT", "Screen [$id] onContentWrapperLayout - request parent layout")
parent.requestLayout()
}
}
Expand Down Expand Up @@ -171,6 +176,7 @@ class Screen(
r: Int,
b: Int,
) {
Log.i("HT", "Screen [$id] received layout ${b - t}")
// In case of form sheet we get layout notification a bit later, in `onBottomSheetBehaviorDidLayout`
// after the attached behaviour laid out this view.
if (changed && isNativeStackScreen && !usesFormSheetPresentation()) {
Expand All @@ -193,6 +199,7 @@ class Screen(
}

footer?.onParentLayout(coordinatorLayoutDidChange, left, top, right, bottom, container!!.height)
Log.i("HT", "Screen [$id] behavior layout")

if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// When using form sheet presentation we want to delay enter transition **on Paper** in order
Expand All @@ -206,6 +213,7 @@ class Screen(
if (shouldTriggerPostponedTransitionAfterLayout) {
shouldTriggerPostponedTransitionAfterLayout = false
// This will trigger enter transition only if one was requested by ScreenStack
Log.i("HT", "Screen [$id] triggering postponed transition")
fragment?.startPostponedEnterTransition()
}
}
Expand Down Expand Up @@ -307,6 +315,7 @@ class Screen(
throw IllegalStateException("[RNScreens] activityState can only progress in NativeStack")
}
this.activityState = activityState
Log.i("HT", "Screen [$id] activityState change to $activityState")
container?.onChildUpdate()
}

Expand Down
14 changes: 12 additions & 2 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.swmansion.rnscreens
import android.content.Context
import android.graphics.Canvas
import android.os.Build
import android.util.Log
import android.view.View
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
Expand Down Expand Up @@ -213,6 +214,7 @@ class ScreenStack(
}

createTransaction().let { transaction ->
Log.i("HT", "ScreenStack createTransaction")
if (stackAnimation != null) {
transaction.setTweenAnimations(stackAnimation, shouldUseOpenAnimation)
}
Expand All @@ -226,14 +228,18 @@ class ScreenStack(
dismissedWrappers.contains(
wrapper,
)
}.forEach { wrapper -> transaction.remove(wrapper.fragment) }
}.forEach { wrapper ->
Log.i("HT", "ScreenStack remove 1 ${wrapper.fragment.id}")
transaction.remove(wrapper.fragment) }

// Remove all screens underneath visibleBottom && these marked for preload, but keep newTop.
screenWrappers
.asSequence()
.takeWhile { it !== visibleBottom }
.filter { (it !== newTop && !dismissedWrappers.contains(it)) || it.screen.activityState === Screen.ActivityState.INACTIVE }
.forEach { wrapper -> transaction.remove(wrapper.fragment) }
.forEach { wrapper ->
Log.i("HT", "ScreenStack remove 2 ${wrapper.fragment.id}")
transaction.remove(wrapper.fragment) }

// attach screens that just became visible
if (visibleBottom != null && !visibleBottom.fragment.isAdded) {
Expand All @@ -243,14 +249,17 @@ class ScreenStack(
.dropWhile { it !== visibleBottom } // ignore all screens beneath the visible bottom
.forEach { wrapper ->
// TODO: It should be enough to dispatch this on commit action once.
Log.i("HT", "ScreenStack add 1 ${wrapper.fragment.id}")
transaction.add(id, wrapper.fragment).runOnCommit {
top?.screen?.bringToFront()
}
}
} else if (newTop != null && !newTop.fragment.isAdded) {
if (newTop.screen.requiresEnterTransitionPostponing()) {
Log.i("HT", "ScreenStack postponeEnterTransition of ${newTop.screen.id}")
newTop.fragment.postponeEnterTransition()
}
Log.i("HT", "ScreenStack add 2 ${newTop.screen.id}")
transaction.add(id, newTop.fragment)
}

Expand All @@ -259,6 +268,7 @@ class ScreenStack(
stack.addAll(screenWrappers.asSequence().map { it as ScreenStackFragmentWrapper })

turnOffA11yUnderTransparentScreen(visibleBottom)
Log.i("HT", "ScreenStack commitTransaction")
transaction.commitNowAllowingStateLoss()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
Expand Down Expand Up @@ -285,6 +286,7 @@ class ScreenStackFragment :
ValueAnimator.ofObject(evaluator, screen.height.toFloat(), 0f).apply {
addUpdateListener { anim ->
val animatedValue = anim.animatedValue as? Float
Log.i("HT", "transitionProgress $animatedValue")
animatedValue?.let { screen.translationY = it }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ScreenAnimationDelegate(
override fun onAnimationStart(animation: Animator) {
if (currentState === LifecycleState.INITIALIZED) {
progressState()
Log.i("HT", "AnimationDelegate onAnimationStart")

// These callbacks do not work as expected from this call site, TODO: investigate it.
// To fix it quickly we emit required events manually
Expand All @@ -53,6 +54,7 @@ class ScreenAnimationDelegate(
if (currentState === LifecycleState.START_DISPATCHED) {
progressState()
animation.removeListener(this)
Log.i("HT", "AnimationDelegate onAnimationEnd")

// wrapper.onViewAnimationEnd()

Expand Down
8 changes: 4 additions & 4 deletions apps/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import { enableFreeze } from 'react-native-screens';
import Example from './Example';
//import * as Test from './src/tests';
// import Example from './Example';
import * as Test from './src/tests';

enableFreeze(true);

export default function App() {
return <Example />;
//return <Test.Test42 />;
// return <Example />;
return <Test.TestRepro />;
}
141 changes: 141 additions & 0 deletions apps/src/tests/TestRepro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from 'react';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { Button, Text, View } from 'react-native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const RootStack = createNativeStackNavigator();
const Tabs = createBottomTabNavigator();
const TabThreeStack = createNativeStackNavigator();

function RootStackHost() {
return (
<RootStack.Navigator>
<RootStack.Screen name="TabsHost" component={TabsHost} options={{
headerShown: false,
}} />
<RootStack.Screen name="RootSheet" component={RootSheet} options={{
presentation: 'formSheet',
sheetAllowedDetents: [0.54, 1],
headerShown: false,
sheetCornerRadius: 12,
contentStyle: {

},
}} />
</RootStack.Navigator>
);
}

function TabsHost() {
const navigation = useNavigation();

React.useEffect(() => {
navigation.preload('RootSheet');
}, [navigation]);

return (
<Tabs.Navigator>
<Tabs.Screen name="TabOne" component={TabOne} />
<Tabs.Screen name="TabTwo" component={TabTwo} />
<Tabs.Screen name="TabThree" component={TabThree} listeners={({ route, navigation }) => {
return {
tabPress: (event) => {
event.preventDefault();
navigation.preload('TabThree');
navigation.navigate('RootSheet');
},
};
}} />
</Tabs.Navigator>
);
}

function RootHome() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, backgroundColor: 'seagreen' }}>
<Text>RootHome</Text>
<Button title="Open TabThree" onPress={() => navigation.navigate('TabsHost', { screen: 'TabThree' })} />
</View>
);
}

function RootSheet() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, backgroundColor: 'seagreen' }}>
<Text>RootSheet</Text>
<Button title="Open TabTwo" onPress={() => navigation.navigate('TabsHost', { screen: 'TabTwo' })} />
<Button title="Open TabThree" onPress={() => navigation.navigate('TabsHost', { screen: 'TabThree' })} />
<Button title="Open TabThreeStackScreenOne" onPress={() => navigation.navigate('TabsHost', { screen: 'TabThree', params: { screen: 'TabThreeStackScreenOne' } })} />
<Button title="Open TabThreeStackScreenTwo" onPress={() => navigation.navigate('TabsHost', { screen: 'TabThree', params: { screen: 'TabThreeStackScreenTwo' } })} />
<Button title="Open TabThreeStackScreenOne OS" onPress={() => navigation.navigate('TabThreeStackScreenOne')} />
<Button title="Open TabThreeStackScreenTwo OS" onPress={() => navigation.navigate('TabThreeStackScreenTwo')} />
</View>
);
}

function TabOne() {
return (
<View style={{ flex: 1, backgroundColor: 'orange' }}>
<Text>TabOne</Text>
</View>
);
}

function TabTwo() {
return (
<View style={{ flex: 1, backgroundColor: 'lightblue' }}>
<Text>TabTwo</Text>
</View>
);
}

function TabThree() {
// return (
// <View style={{ flex: 1, backgroundColor: 'darkgreen' }}>
// <Text>TabThree</Text>
// </View>
// );
return (
<TabThreeStackHost />
);
}

function TabThreeStackScreenOne() {
return (
<View style={{ flex: 1, backgroundColor: 'orange' }}>
<Text>TabThreeStackScreenOne</Text>
</View>
);
}

function TabThreeStackScreenTwo() {
return (
<View style={{ flex: 1, backgroundColor: 'lightblue' }}>
<Text>TabThreeStackScreenTwo</Text>
</View>
);
}

function TabThreeStackHost() {
return (
<TabThreeStack.Navigator>
<TabThreeStack.Screen name="TabThreeStackScreenOne" component={TabThreeStackScreenOne} />
<TabThreeStack.Screen name="TabThreeStackScreenTwo" component={TabThreeStackScreenTwo} />
</TabThreeStack.Navigator>

);
}

function App() {
return (
<NavigationContainer navigationInChildEnabled>
<RootStackHost />
</NavigationContainer>
);
}
export default App;
1 change: 1 addition & 0 deletions apps/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export { default as Test2767 } from './Test2767';
export { default as Test2789 } from './Test2789';
export { default as Test2811 } from './Test2811';
export { default as Test2819 } from './Test2819';
export { default as TestRepro } from './TestRepro';
export { default as TestScreenAnimation } from './TestScreenAnimation';
export { default as TestScreenAnimationV5 } from './TestScreenAnimationV5';
export { default as TestHeader } from './TestHeader';
Expand Down
Loading