react-native-screens 3.7.2 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -3
- package/android/build.gradle +8 -7
- package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +71 -0
- package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +29 -0
- package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -1
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +7 -41
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +55 -40
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +19 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +30 -101
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +76 -14
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +13 -4
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +8 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +7 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +90 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +150 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +40 -0
- package/ios/RNSScreen.h +1 -0
- package/ios/RNSScreen.m +35 -0
- package/ios/RNSScreenContainer.h +2 -0
- package/ios/RNSScreenStack.m +24 -6
- package/ios/RNSScreenStackHeaderConfig.m +45 -2
- package/ios/RNSScreenWindowTraits.h +5 -0
- package/ios/RNSScreenWindowTraits.m +29 -0
- package/lib/commonjs/index.js +24 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.native.js +103 -17
- package/lib/commonjs/index.native.js.map +1 -1
- package/lib/commonjs/native-stack/utils/useBackPressSubscription.js +67 -0
- package/lib/commonjs/native-stack/utils/useBackPressSubscription.js.map +1 -0
- package/lib/commonjs/native-stack/views/HeaderConfig.js +46 -4
- package/lib/commonjs/native-stack/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +60 -0
- package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
- package/lib/commonjs/reanimated/ReanimatedScreen.js +7 -79
- package/lib/commonjs/reanimated/ReanimatedScreen.js.map +1 -1
- package/lib/commonjs/reanimated/ReanimatedScreenProvider.js +61 -0
- package/lib/commonjs/reanimated/ReanimatedScreenProvider.js.map +1 -0
- package/lib/commonjs/reanimated/index.js +2 -2
- package/lib/commonjs/reanimated/index.js.map +1 -1
- package/lib/commonjs/utils.js +20 -0
- package/lib/commonjs/utils.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.native.js +99 -19
- package/lib/module/index.native.js.map +1 -1
- package/lib/module/native-stack/utils/useBackPressSubscription.js +50 -0
- package/lib/module/native-stack/utils/useBackPressSubscription.js.map +1 -0
- package/lib/module/native-stack/views/HeaderConfig.js +46 -5
- package/lib/module/native-stack/views/HeaderConfig.js.map +1 -1
- package/lib/module/reanimated/ReanimatedNativeStackScreen.js +40 -0
- package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
- package/lib/module/reanimated/ReanimatedScreen.js +6 -73
- package/lib/module/reanimated/ReanimatedScreen.js.map +1 -1
- package/lib/module/reanimated/ReanimatedScreenProvider.js +49 -0
- package/lib/module/reanimated/ReanimatedScreenProvider.js.map +1 -0
- package/lib/module/reanimated/index.js +1 -1
- package/lib/module/reanimated/index.js.map +1 -1
- package/lib/module/utils.js +8 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/native-stack/types.d.ts +0 -2
- package/lib/typescript/native-stack/utils/useBackPressSubscription.d.ts +16 -0
- package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +5 -0
- package/lib/typescript/reanimated/ReanimatedScreen.d.ts +5 -2
- package/lib/typescript/reanimated/ReanimatedScreenProvider.d.ts +2 -0
- package/lib/typescript/reanimated/index.d.ts +1 -1
- package/lib/typescript/types.d.ts +46 -1
- package/lib/typescript/utils.d.ts +2 -0
- package/native-stack/README.md +35 -7
- package/package.json +5 -2
- package/src/index.native.tsx +138 -43
- package/src/index.tsx +10 -0
- package/src/native-stack/types.tsx +0 -2
- package/src/native-stack/utils/useBackPressSubscription.tsx +66 -0
- package/src/native-stack/views/HeaderConfig.tsx +46 -3
- package/src/reanimated/ReanimatedNativeStackScreen.tsx +61 -0
- package/src/reanimated/ReanimatedScreen.tsx +6 -84
- package/src/reanimated/ReanimatedScreenProvider.tsx +42 -0
- package/src/reanimated/index.tsx +1 -1
- package/src/types.tsx +46 -1
- package/src/utils.ts +12 -0
package/README.md
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
<img src="https://user-images.githubusercontent.com/16062886/117443651-c13d9500-af38-11eb-888d-b6a0b580760c.png" width="100%" alt="React Native Screens by Software Mansion" >
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
This project aims to expose native navigation container components to React Native. It is not designed to be used as a standalone library but rather as a dependency of a [full-featured navigation library](https://github.com/react-navigation/react-navigation).
|
|
5
4
|
|
|
5
|
+
## Supported platforms
|
|
6
|
+
|
|
7
|
+
- [x] iOS
|
|
8
|
+
- [x] Android
|
|
9
|
+
- [x] tvOS
|
|
10
|
+
- [x] Windows
|
|
11
|
+
- [x] Web
|
|
12
|
+
|
|
6
13
|
## Installation
|
|
7
14
|
|
|
8
15
|
### iOS
|
|
@@ -28,6 +35,26 @@ protected void onCreate(Bundle savedInstanceState) {
|
|
|
28
35
|
|
|
29
36
|
For people that must handle cases like this, there is [a more detailed discussion of the difficulties in a series of related comments](https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704633).
|
|
30
37
|
|
|
38
|
+
<details>
|
|
39
|
+
<summary>Need to use a custom Kotlin version?</summary>
|
|
40
|
+
<br>
|
|
41
|
+
|
|
42
|
+
Since `v3.6.0` `react-native-screens` has been rewritten with Kotlin. Kotlin version used in this library defaults to `1.4.10`.
|
|
43
|
+
|
|
44
|
+
If you need to use a different Kotlin version, set `kotlinVersion` ext property in your project's `android/build.gradle` and the library will use this version accordingly:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
buildscript {
|
|
48
|
+
ext {
|
|
49
|
+
...
|
|
50
|
+
kotlinVersion = "1.4.10"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Disclaimer**: `react-native-screens` requires Kotlin `1.3.50` or higher.
|
|
56
|
+
</details>
|
|
57
|
+
|
|
31
58
|
### Windows
|
|
32
59
|
|
|
33
60
|
Installation on Windows should be completely handled with auto-linking when using React Native Windows 0.63+. For earlier versions, you must [manually link](https://microsoft.github.io/react-native-windows/docs/native-modules-using) the native module.
|
|
@@ -61,6 +88,24 @@ Just make sure that the version of [react-navigation](https://github.com/react-n
|
|
|
61
88
|
|
|
62
89
|
You are all set 🎉 – when screens are enabled in your application code react-navigation will automatically use them instead of relying on plain React Native Views.
|
|
63
90
|
|
|
91
|
+
### Experimental support for `react-freeze`
|
|
92
|
+
|
|
93
|
+
> You have to use React Native 0.64 or higher, react-navigation 5.x or 6.x and react-native-screens >= v3.9.0
|
|
94
|
+
|
|
95
|
+
Since `v3.9.0`, `react-native-screens` comes with experimental support for [`react-freeze`](https://github.com/software-mansion-labs/react-freeze). It uses the React `Suspense` mechanism to prevent parts of the React component tree from rendering, while keeping its state untouched.
|
|
96
|
+
|
|
97
|
+
To benefit from this feature, enable it in your entry file (e.g. `App.js`) with this snippet:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import { enableFreeze } from 'react-native-screens';
|
|
101
|
+
|
|
102
|
+
enableFreeze(true);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Want to know more? Check out [react-freeze README](https://github.com/software-mansion-labs/react-freeze#readme)
|
|
106
|
+
|
|
107
|
+
Found a bug? File an issue [here](https://github.com/software-mansion/react-native-screens/issues) or directly in [react-freeze repository](https://github.com/software-mansion-labs/react-freeze/issues).
|
|
108
|
+
|
|
64
109
|
### Disabling `react-native-screens`
|
|
65
110
|
|
|
66
111
|
If, for whatever reason, you'd like to disable native screens support and use plain React Native Views add the following code in your entry file (e.g. `App.js`):
|
|
@@ -76,9 +121,10 @@ You can also disable the usage of native screens per navigator with [`detachInac
|
|
|
76
121
|
### Using `createNativeStackNavigator` with React Navigation
|
|
77
122
|
|
|
78
123
|
To take advantage of the native stack navigator primitive for React Navigation that leverages `UINavigationController` on iOS and `Fragment` on Android, please refer:
|
|
124
|
+
|
|
79
125
|
- for React Navigation >= v6 to the [Native Stack Navigator part of React Navigation documentation](https://reactnavigation.org/docs/native-stack-navigator)
|
|
80
|
-
- for React Navigation v5 to the [README in react-native-screens/native-stack](https://github.com/software-mansion/react-native-screens/tree/master/native-stack)
|
|
81
|
-
- for older versions to the [README in react-native-screens/createNativeStackNavigator](https://github.com/software-mansion/react-native-screens/tree/master/createNativeStackNavigator)
|
|
126
|
+
- for React Navigation v5 to the [README in react-native-screens/native-stack](https://github.com/software-mansion/react-native-screens/tree/master/native-stack)
|
|
127
|
+
- for older versions to the [README in react-native-screens/createNativeStackNavigator](https://github.com/software-mansion/react-native-screens/tree/master/createNativeStackNavigator)
|
|
82
128
|
|
|
83
129
|
## Interop with [react-native-navigation](https://github.com/wix/react-native-navigation)
|
|
84
130
|
|
|
@@ -93,6 +139,26 @@ This library should work out of the box with all existing react-native libraries
|
|
|
93
139
|
If you are building a navigation library you may want to use `react-native-screens` to have control over which parts of the React component tree are attached to the native view hierarchy.
|
|
94
140
|
To do that, `react-native-screens` provides you with the components documented [here](https://github.com/kmagiera/react-native-screens/tree/master/guides/GUIDE_FOR_LIBRARY_AUTHORS.md).
|
|
95
141
|
|
|
142
|
+
## Common problems
|
|
143
|
+
|
|
144
|
+
### Problems with header on iOS
|
|
145
|
+
|
|
146
|
+
- [Focused search bar causes new screens to have incorrect header](https://github.com/software-mansion/react-native-screens/issues/996)
|
|
147
|
+
- [Scrollable content gets cut off by the header with a search bar](https://github.com/software-mansion/react-native-screens/issues/1120)
|
|
148
|
+
- [RefreshControl does not work properly with NativeStackNavigator and largeTitle](https://github.com/software-mansion/react-native-screens/issues/395)
|
|
149
|
+
|
|
150
|
+
#### Solution
|
|
151
|
+
|
|
152
|
+
Use `ScrollView` with prop `contentInsetAdjustmentBehavior=“automatic”` as a main container of the screen and set `headerTranslucent: true` in screen options.
|
|
153
|
+
|
|
154
|
+
### Other problems
|
|
155
|
+
|
|
156
|
+
| Problem | Solution |
|
|
157
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| [SVG component becomes transparent when goBack](https://github.com/software-mansion/react-native-screens/issues/773) | [related PRs](https://github.com/software-mansion/react-native-screens/issues/773#issuecomment-783469792) |
|
|
159
|
+
| [Memory leak while moving from one screen to another in the same stack](https://github.com/software-mansion/react-native-screens/issues/843) | [explanation](https://github.com/software-mansion/react-native-screens/issues/843#issuecomment-832034119) |
|
|
160
|
+
| [LargeHeader stays small after pop/goBack/swipe gesture on iOS 14+](https://github.com/software-mansion/react-native-screens/issues/649) | [potential fix](https://github.com/software-mansion/react-native-screens/issues/649#issuecomment-712199895) |
|
|
161
|
+
|
|
96
162
|
## Contributing
|
|
97
163
|
|
|
98
164
|
There are many ways to contribute to this project. See [CONTRIBUTING](https://github.com/kmagiera/react-native-screens/tree/master/guides/CONTRIBUTING.md) guide for more information. Thank you for your interest in contributing!
|
package/android/build.gradle
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
buildscript {
|
|
2
|
+
ext.safeExtGet = {prop, fallback ->
|
|
3
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
4
|
+
}
|
|
2
5
|
repositories {
|
|
3
6
|
google()
|
|
4
7
|
jcenter()
|
|
@@ -6,7 +9,7 @@ buildscript {
|
|
|
6
9
|
}
|
|
7
10
|
dependencies {
|
|
8
11
|
classpath('com.android.tools.build:gradle:4.2.2')
|
|
9
|
-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin
|
|
12
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.4.10')}"
|
|
10
13
|
classpath "com.diffplug.spotless:spotless-plugin-gradle:5.15.0"
|
|
11
14
|
}
|
|
12
15
|
}
|
|
@@ -19,10 +22,6 @@ if (project == rootProject) {
|
|
|
19
22
|
apply plugin: 'com.android.library'
|
|
20
23
|
apply plugin: 'kotlin-android'
|
|
21
24
|
|
|
22
|
-
def safeExtGet(prop, fallback) {
|
|
23
|
-
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
24
|
-
}
|
|
25
|
-
|
|
26
25
|
android {
|
|
27
26
|
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
|
28
27
|
|
|
@@ -35,6 +34,10 @@ android {
|
|
|
35
34
|
lintOptions {
|
|
36
35
|
abortOnError false
|
|
37
36
|
}
|
|
37
|
+
compileOptions {
|
|
38
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
39
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
40
|
+
}
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
repositories {
|
|
@@ -57,6 +60,4 @@ dependencies {
|
|
|
57
60
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
|
|
58
61
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
|
59
62
|
implementation 'com.google.android.material:material:1.1.0'
|
|
60
|
-
implementation "androidx.core:core-ktx:1.6.0"
|
|
61
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.20"
|
|
62
63
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.activity.OnBackPressedCallback
|
|
5
|
+
import androidx.appcompat.widget.SearchView
|
|
6
|
+
import androidx.fragment.app.Fragment
|
|
7
|
+
|
|
8
|
+
class CustomSearchView(context: Context, fragment: Fragment) : SearchView(context) {
|
|
9
|
+
/*
|
|
10
|
+
CustomSearchView uses some variables from SearchView. They are listed below with links to documentation
|
|
11
|
+
isIconified - https://developer.android.com/reference/android/widget/SearchView#setIconified(boolean)
|
|
12
|
+
maxWidth - https://developer.android.com/reference/android/widget/SearchView#setMaxWidth(int)
|
|
13
|
+
setOnSearchClickListener - https://developer.android.com/reference/android/widget/SearchView#setOnSearchClickListener(android.view.View.OnClickListener)
|
|
14
|
+
setOnCloseListener - https://developer.android.com/reference/android/widget/SearchView#setOnCloseListener(android.widget.SearchView.OnCloseListener)
|
|
15
|
+
*/
|
|
16
|
+
private var mCustomOnCloseListener: OnCloseListener? = null
|
|
17
|
+
private var mCustomOnSearchClickedListener: OnClickListener? = null
|
|
18
|
+
|
|
19
|
+
private var mOnBackPressedCallback: OnBackPressedCallback =
|
|
20
|
+
object : OnBackPressedCallback(true) {
|
|
21
|
+
override fun handleOnBackPressed() {
|
|
22
|
+
isIconified = true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
private val backPressOverrider = FragmentBackPressOverrider(fragment, mOnBackPressedCallback)
|
|
26
|
+
var overrideBackAction: Boolean
|
|
27
|
+
set(value) {
|
|
28
|
+
backPressOverrider.overrideBackAction = value
|
|
29
|
+
}
|
|
30
|
+
get() = backPressOverrider.overrideBackAction
|
|
31
|
+
|
|
32
|
+
fun focus() {
|
|
33
|
+
isIconified = false
|
|
34
|
+
requestFocusFromTouch()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override fun setOnCloseListener(listener: OnCloseListener?) {
|
|
38
|
+
mCustomOnCloseListener = listener
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override fun setOnSearchClickListener(listener: OnClickListener?) {
|
|
42
|
+
mCustomOnSearchClickedListener = listener
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override fun onAttachedToWindow() {
|
|
46
|
+
super.onAttachedToWindow()
|
|
47
|
+
if (!isIconified) {
|
|
48
|
+
backPressOverrider.maybeAddBackCallback()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun onDetachedFromWindow() {
|
|
53
|
+
super.onDetachedFromWindow()
|
|
54
|
+
backPressOverrider.removeBackCallbackIfAdded()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
init {
|
|
58
|
+
super.setOnSearchClickListener { v ->
|
|
59
|
+
mCustomOnSearchClickedListener?.onClick(v)
|
|
60
|
+
backPressOverrider.maybeAddBackCallback()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
super.setOnCloseListener {
|
|
64
|
+
val result = mCustomOnCloseListener?.onClose() ?: false
|
|
65
|
+
backPressOverrider.removeBackCallbackIfAdded()
|
|
66
|
+
result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
maxWidth = Integer.MAX_VALUE
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.appcompat.widget.Toolbar
|
|
5
|
+
|
|
6
|
+
// This class is used to store config closer to search bar
|
|
7
|
+
open class CustomToolbar(context: Context, val config: ScreenStackHeaderConfig) : Toolbar(context)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import androidx.activity.OnBackPressedCallback
|
|
4
|
+
import androidx.fragment.app.Fragment
|
|
5
|
+
|
|
6
|
+
class FragmentBackPressOverrider(
|
|
7
|
+
private val fragment: Fragment,
|
|
8
|
+
private val mOnBackPressedCallback: OnBackPressedCallback
|
|
9
|
+
) {
|
|
10
|
+
private var mIsBackCallbackAdded: Boolean = false
|
|
11
|
+
var overrideBackAction: Boolean = true
|
|
12
|
+
|
|
13
|
+
fun maybeAddBackCallback() {
|
|
14
|
+
if (!mIsBackCallbackAdded && overrideBackAction) {
|
|
15
|
+
fragment.activity?.onBackPressedDispatcher?.addCallback(
|
|
16
|
+
fragment,
|
|
17
|
+
mOnBackPressedCallback
|
|
18
|
+
)
|
|
19
|
+
mIsBackCallbackAdded = true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fun removeBackCallbackIfAdded() {
|
|
24
|
+
if (mIsBackCallbackAdded) {
|
|
25
|
+
mOnBackPressedCallback.remove()
|
|
26
|
+
mIsBackCallbackAdded = false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
package com.swmansion.rnscreens
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
-
import android.content.Context
|
|
5
4
|
import android.content.pm.ActivityInfo
|
|
6
5
|
import android.graphics.Paint
|
|
7
|
-
import android.os.Build
|
|
8
6
|
import android.os.Parcelable
|
|
9
7
|
import android.util.SparseArray
|
|
10
|
-
import android.view.View
|
|
11
8
|
import android.view.ViewGroup
|
|
12
9
|
import android.view.WindowManager
|
|
13
|
-
import android.view.inputmethod.InputMethodManager
|
|
14
10
|
import android.webkit.WebView
|
|
15
|
-
import android.widget.TextView
|
|
16
11
|
import com.facebook.react.bridge.GuardedRunnable
|
|
17
12
|
import com.facebook.react.bridge.ReactContext
|
|
18
13
|
import com.facebook.react.uimanager.UIManagerModule
|
|
@@ -87,28 +82,6 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
87
82
|
}
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
override fun onAttachedToWindow() {
|
|
91
|
-
super.onAttachedToWindow()
|
|
92
|
-
// This method implements a workaround for RN's autoFocus functionality. Because of the way
|
|
93
|
-
// autoFocus is implemented it sometimes gets triggered before native text view is mounted. As
|
|
94
|
-
// a result Android ignores calls for opening soft keyboard and here we trigger it manually
|
|
95
|
-
// again after the screen is attached.
|
|
96
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
97
|
-
var view = focusedChild
|
|
98
|
-
if (view != null) {
|
|
99
|
-
while (view is ViewGroup) {
|
|
100
|
-
view = view.focusedChild
|
|
101
|
-
}
|
|
102
|
-
if (view is TextView) {
|
|
103
|
-
val textView = view
|
|
104
|
-
if (textView.showSoftInputOnFocus) {
|
|
105
|
-
textView.addOnAttachStateChangeListener(sShowSoftKeyboardOnAttach)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
85
|
val headerConfig: ScreenStackHeaderConfig?
|
|
113
86
|
get() {
|
|
114
87
|
val child = getChildAt(0)
|
|
@@ -183,6 +156,13 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
183
156
|
fragment?.let { ScreenWindowTraits.setOrientation(this, it.tryGetActivity()) }
|
|
184
157
|
}
|
|
185
158
|
|
|
159
|
+
// Accepts one of 4 accessibility flags
|
|
160
|
+
// developer.android.com/reference/android/view/View#attr_android:importantForAccessibility
|
|
161
|
+
fun changeAccessibilityMode(mode: Int) {
|
|
162
|
+
this.importantForAccessibility = mode
|
|
163
|
+
this.headerConfig?.toolbar?.importantForAccessibility = mode
|
|
164
|
+
}
|
|
165
|
+
|
|
186
166
|
var statusBarStyle: String?
|
|
187
167
|
get() = mStatusBarStyle
|
|
188
168
|
set(statusBarStyle) {
|
|
@@ -254,18 +234,4 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
254
234
|
enum class WindowTraits {
|
|
255
235
|
ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED
|
|
256
236
|
}
|
|
257
|
-
|
|
258
|
-
companion object {
|
|
259
|
-
private val sShowSoftKeyboardOnAttach: OnAttachStateChangeListener =
|
|
260
|
-
object : OnAttachStateChangeListener {
|
|
261
|
-
override fun onViewAttachedToWindow(view: View) {
|
|
262
|
-
val inputMethodManager =
|
|
263
|
-
view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
264
|
-
inputMethodManager.showSoftInput(view, 0)
|
|
265
|
-
view.removeOnAttachStateChangeListener(this)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
override fun onViewDetachedFromWindow(view: View) {}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
237
|
}
|
|
@@ -11,6 +11,7 @@ import androidx.fragment.app.FragmentActivity
|
|
|
11
11
|
import androidx.fragment.app.FragmentManager
|
|
12
12
|
import androidx.fragment.app.FragmentTransaction
|
|
13
13
|
import com.facebook.react.ReactRootView
|
|
14
|
+
import com.facebook.react.bridge.ReactContext
|
|
14
15
|
import com.facebook.react.modules.core.ChoreographerCompat
|
|
15
16
|
import com.facebook.react.modules.core.ReactChoreographer
|
|
16
17
|
import com.swmansion.rnscreens.Screen.ActivityState
|
|
@@ -176,16 +177,12 @@ open class ScreenContainer<T : ScreenFragment>(context: Context?) : ViewGroup(co
|
|
|
176
177
|
return transaction
|
|
177
178
|
}
|
|
178
179
|
|
|
179
|
-
private fun attachScreen(screenFragment: ScreenFragment) {
|
|
180
|
-
|
|
180
|
+
private fun attachScreen(transaction: FragmentTransaction, screenFragment: ScreenFragment) {
|
|
181
|
+
transaction.add(id, screenFragment)
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
private fun
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private fun detachScreen(screenFragment: ScreenFragment) {
|
|
188
|
-
createTransaction().remove(screenFragment).commitNowAllowingStateLoss()
|
|
184
|
+
private fun detachScreen(transaction: FragmentTransaction, screenFragment: ScreenFragment) {
|
|
185
|
+
transaction.remove(screenFragment)
|
|
189
186
|
}
|
|
190
187
|
|
|
191
188
|
private fun getActivityState(screenFragment: ScreenFragment): ActivityState? {
|
|
@@ -276,10 +273,18 @@ open class ScreenContainer<T : ScreenFragment>(context: Context?) : ViewGroup(co
|
|
|
276
273
|
// The exception to this rule is `updateImmediately` which is triggered by actions
|
|
277
274
|
// not connected to React view hierarchy changes, but rather internal events
|
|
278
275
|
mNeedUpdate = true
|
|
276
|
+
(context as? ReactContext)?.runOnUiQueueThread {
|
|
277
|
+
// We schedule the update here because LayoutAnimations of `react-native-reanimated`
|
|
278
|
+
// sometimes attach/detach screens after the layout block of `ScreensShadowNode` has
|
|
279
|
+
// already run, and we want to update the container then too. In the other cases,
|
|
280
|
+
// this code will do nothing since it will run after the UIBlock when `mNeedUpdate`
|
|
281
|
+
// will already be false.
|
|
282
|
+
performUpdates()
|
|
283
|
+
}
|
|
279
284
|
}
|
|
280
285
|
|
|
281
286
|
protected fun performUpdatesNow() {
|
|
282
|
-
// we want to update
|
|
287
|
+
// we want to update immediately when the fragment manager is set or native back button
|
|
283
288
|
// dismiss is dispatched or Screen's activityState changes since it is not connected to React
|
|
284
289
|
// view hierarchy changes and will not trigger `onBeforeLayout` method of `ScreensShadowNode`
|
|
285
290
|
mNeedUpdate = true
|
|
@@ -287,7 +292,7 @@ open class ScreenContainer<T : ScreenFragment>(context: Context?) : ViewGroup(co
|
|
|
287
292
|
}
|
|
288
293
|
|
|
289
294
|
fun performUpdates() {
|
|
290
|
-
if (!mNeedUpdate || !mIsAttached || mFragmentManager == null) {
|
|
295
|
+
if (!mNeedUpdate || !mIsAttached || mFragmentManager == null || mFragmentManager?.isDestroyed == true) {
|
|
291
296
|
return
|
|
292
297
|
}
|
|
293
298
|
mNeedUpdate = false
|
|
@@ -296,43 +301,53 @@ open class ScreenContainer<T : ScreenFragment>(context: Context?) : ViewGroup(co
|
|
|
296
301
|
}
|
|
297
302
|
|
|
298
303
|
open fun onUpdate() {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
screenFragment.
|
|
304
|
-
|
|
305
|
-
|
|
304
|
+
createTransaction().let {
|
|
305
|
+
// detach screens that are no longer active
|
|
306
|
+
val orphaned: MutableSet<Fragment> = HashSet(requireNotNull(mFragmentManager, { "mFragmentManager is null when performing update in ScreenContainer" }).fragments)
|
|
307
|
+
for (screenFragment in mScreenFragments) {
|
|
308
|
+
if (getActivityState(screenFragment) === ActivityState.INACTIVE &&
|
|
309
|
+
screenFragment.isAdded
|
|
310
|
+
) {
|
|
311
|
+
detachScreen(it, screenFragment)
|
|
312
|
+
}
|
|
313
|
+
orphaned.remove(screenFragment)
|
|
306
314
|
}
|
|
307
|
-
orphaned.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
detachScreen(fragment)
|
|
315
|
+
if (orphaned.isNotEmpty()) {
|
|
316
|
+
val orphanedAry = orphaned.toTypedArray()
|
|
317
|
+
for (fragment in orphanedAry) {
|
|
318
|
+
if (fragment is ScreenFragment) {
|
|
319
|
+
if (fragment.screen.container == null) {
|
|
320
|
+
detachScreen(it, fragment)
|
|
321
|
+
}
|
|
315
322
|
}
|
|
316
323
|
}
|
|
317
324
|
}
|
|
318
|
-
|
|
319
|
-
var transitioning = true
|
|
320
|
-
if (topScreen != null) {
|
|
325
|
+
|
|
321
326
|
// if there is an "onTop" screen it means the transition has ended
|
|
322
|
-
transitioning =
|
|
323
|
-
}
|
|
327
|
+
val transitioning = topScreen == null
|
|
324
328
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
329
|
+
// attach newly activated screens
|
|
330
|
+
var addedBefore = false
|
|
331
|
+
val pendingFront: ArrayList<T> = ArrayList()
|
|
332
|
+
|
|
333
|
+
for (screenFragment in mScreenFragments) {
|
|
334
|
+
val activityState = getActivityState(screenFragment)
|
|
335
|
+
if (activityState !== ActivityState.INACTIVE && !screenFragment.isAdded) {
|
|
336
|
+
addedBefore = true
|
|
337
|
+
attachScreen(it, screenFragment)
|
|
338
|
+
} else if (activityState !== ActivityState.INACTIVE && addedBefore) {
|
|
339
|
+
// we detach the screen and then reattach it later to make it appear on front
|
|
340
|
+
detachScreen(it, screenFragment)
|
|
341
|
+
pendingFront.add(screenFragment)
|
|
342
|
+
}
|
|
343
|
+
screenFragment.screen.setTransitioning(transitioning)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
for (screenFragment in pendingFront) {
|
|
347
|
+
attachScreen(it, screenFragment)
|
|
334
348
|
}
|
|
335
|
-
|
|
349
|
+
|
|
350
|
+
it.commitNowAllowingStateLoss()
|
|
336
351
|
}
|
|
337
352
|
}
|
|
338
353
|
|
|
@@ -2,6 +2,7 @@ package com.swmansion.rnscreens
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.app.Activity
|
|
5
|
+
import android.content.Context
|
|
5
6
|
import android.os.Bundle
|
|
6
7
|
import android.view.LayoutInflater
|
|
7
8
|
import android.view.View
|
|
@@ -61,7 +62,7 @@ open class ScreenFragment : Fragment {
|
|
|
61
62
|
container: ViewGroup?,
|
|
62
63
|
savedInstanceState: Bundle?
|
|
63
64
|
): View? {
|
|
64
|
-
val wrapper = context?.let {
|
|
65
|
+
val wrapper = context?.let { ScreensFrameLayout(it) }
|
|
65
66
|
|
|
66
67
|
val params = FrameLayout.LayoutParams(
|
|
67
68
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
|
|
@@ -71,6 +72,23 @@ open class ScreenFragment : Fragment {
|
|
|
71
72
|
return wrapper
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
private class ScreensFrameLayout(
|
|
76
|
+
context: Context,
|
|
77
|
+
) : FrameLayout(context) {
|
|
78
|
+
/**
|
|
79
|
+
* This method implements a workaround for RN's autoFocus functionality. Because of the way
|
|
80
|
+
* autoFocus is implemented it dismisses soft keyboard in fragment transition
|
|
81
|
+
* due to change of visibility of the view at the start of the transition. Here we override the
|
|
82
|
+
* call to `clearFocus` when the visibility of view is `INVISIBLE` since `clearFocus` triggers the
|
|
83
|
+
* hiding of the keyboard in `ReactEditText.java`.
|
|
84
|
+
*/
|
|
85
|
+
override fun clearFocus() {
|
|
86
|
+
if (visibility != INVISIBLE) {
|
|
87
|
+
super.clearFocus()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
74
92
|
open fun onContainerUpdate() {
|
|
75
93
|
updateWindowTraits()
|
|
76
94
|
}
|