Skip to content

Commit b6d053c

Browse files
authored
feat(Android, Stack v5): nested stack uses childFragmentManager of parent container (#3597)
## Description Closes <software-mansion/react-native-screens-labs#921> `StackScreen` implements now `FragmentProviding` interface. This causes `StackContainer` of nested stack to use `childFragmentManager` of parent container instead of reusing `supportFragmentManager`. ## Changes N/A ## Visual documentation N/A ## Test plan Not a fix or change in behavior, this is just new behavior. To verify you need to attach a debugger in `StackContainer` and verify that it uses `childFragmentManager` of parent container. I've added test example for this use case that I'll likely follow developing in next PRs.
1 parent 7f6e4cf commit b6d053c

File tree

4 files changed

+107
-1
lines changed

4 files changed

+107
-1
lines changed

android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.swmansion.rnscreens.ext
33
import android.graphics.drawable.ColorDrawable
44
import android.view.View
55
import android.view.ViewGroup
6+
import androidx.fragment.app.Fragment
7+
import androidx.fragment.app.findFragment
68

79
internal fun View.parentAsView() = this.parent as? View
810

@@ -37,3 +39,10 @@ internal fun View.maybeBgColor(): Int? {
3739
}
3840

3941
internal fun View.asViewGroupOrNull(): ViewGroup? = this as? ViewGroup
42+
43+
internal fun View.findFragmentOrNull(): Fragment? =
44+
try {
45+
this.findFragment()
46+
} catch (_: IllegalStateException) {
47+
null
48+
}

android/src/main/java/com/swmansion/rnscreens/gamma/stack/screen/StackScreen.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@ package com.swmansion.rnscreens.gamma.stack.screen
22

33
import android.annotation.SuppressLint
44
import android.view.ViewGroup
5+
import androidx.fragment.app.Fragment
56
import androidx.lifecycle.LifecycleOwner
67
import com.facebook.react.uimanager.ThemedReactContext
8+
import com.swmansion.rnscreens.ext.findFragmentOrNull
9+
import com.swmansion.rnscreens.gamma.common.FragmentProviding
710
import com.swmansion.rnscreens.gamma.stack.host.StackHost
811
import java.lang.ref.WeakReference
912
import kotlin.properties.Delegates
1013

1114
@SuppressLint("ViewConstructor") // should never be restored
1215
class StackScreen(
1316
private val reactContext: ThemedReactContext,
14-
) : ViewGroup(reactContext) {
17+
) : ViewGroup(reactContext), FragmentProviding {
1518
enum class ActivityMode {
1619
DETACHED,
1720
ATTACHED,
@@ -65,4 +68,8 @@ class StackScreen(
6568
r: Int,
6669
b: Int,
6770
) = Unit
71+
72+
override fun getAssociatedFragment(): Fragment? = this.findFragmentOrNull()?.also {
73+
check(it is StackScreenFragment) { "[RNScreens] Unexpected fragment type: ${it.javaClass.simpleName}"}
74+
}
6875
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React from 'react';
2+
3+
import {
4+
StackContainer,
5+
StackRouteConfig,
6+
useStackNavigationContext,
7+
} from '../../shared/gamma/containers/stack';
8+
import { Button, Text, View } from 'react-native';
9+
10+
function TemplateScreen() {
11+
const navigation = useStackNavigationContext();
12+
13+
return (
14+
<View
15+
style={{ flex: 1, backgroundColor: 'white', justifyContent: 'center' }}>
16+
<Text>Route: {navigation.routeKey}</Text>
17+
<Button title="Push A" onPress={() => navigation.push('A')} />
18+
<Button title="Push B" onPress={() => navigation.push('B')} />
19+
<Button
20+
title="Push NestedStack"
21+
onPress={() => navigation.push('NestedStack')}
22+
/>
23+
<Button title="Pop" onPress={() => navigation.pop(navigation.routeKey)} />
24+
<Button title="Preload A" onPress={() => navigation.preload('A')} />
25+
<Button title="Preload B" onPress={() => navigation.preload('B')} />
26+
</View>
27+
);
28+
}
29+
30+
function NestedTemplateScreen() {
31+
const navigation = useStackNavigationContext();
32+
33+
return (
34+
<View
35+
style={{ flex: 1, backgroundColor: 'white', justifyContent: 'center' }}>
36+
<Text>Route: {navigation.routeKey}</Text>
37+
<Button title="Push NestedA" onPress={() => navigation.push('NestedA')} />
38+
<Button title="Push NestedB" onPress={() => navigation.push('NestedB')} />
39+
<Button title="Pop" onPress={() => navigation.pop(navigation.routeKey)} />
40+
<Button
41+
title="Preload NestedA"
42+
onPress={() => navigation.preload('NestedA')}
43+
/>
44+
<Button
45+
title="Preload NestedB"
46+
onPress={() => navigation.preload('NestedB')}
47+
/>
48+
</View>
49+
);
50+
}
51+
52+
function NestedStackScreen() {
53+
return <StackContainer routeConfigs={ROUTE_CONFIGS_NESTED_STACK} />;
54+
}
55+
56+
const ROUTE_CONFIGS: StackRouteConfig[] = [
57+
{
58+
name: 'A',
59+
Component: TemplateScreen,
60+
options: {},
61+
},
62+
{
63+
name: 'B',
64+
Component: TemplateScreen,
65+
options: {},
66+
},
67+
{
68+
name: 'NestedStack',
69+
Component: NestedStackScreen,
70+
options: {},
71+
},
72+
];
73+
74+
const ROUTE_CONFIGS_NESTED_STACK: StackRouteConfig[] = [
75+
{
76+
name: 'NestedA',
77+
Component: NestedTemplateScreen,
78+
options: {},
79+
},
80+
{
81+
name: 'NestedB',
82+
Component: NestedTemplateScreen,
83+
options: {},
84+
},
85+
];
86+
87+
export default function App() {
88+
return <StackContainer routeConfigs={ROUTE_CONFIGS} />;
89+
}

apps/src/tests/issue-tests/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,4 @@ export { default as TestBottomTabsOrientation } from './TestBottomTabsOrientatio
199199
export { default as TestScreenStack } from './TestScreenStack';
200200
export { default as TestSplit } from './TestSplit';
201201
export { default as TestSafeAreaViewIOS } from './TestSafeAreaViewIOS';
202+
export { default as TestStackNesting } from './TestStackNesting';

0 commit comments

Comments
 (0)