@apexnative/navigation 1.0.0-alpha.6 → 1.0.0-alpha.8

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.
Files changed (242) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +15 -0
  3. package/android/CLAUDE.md +19 -10
  4. package/android/CORE.md +3 -2
  5. package/android/consumer-rules.pro +6 -0
  6. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  7. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
  8. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  9. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  10. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  11. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  12. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  13. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  14. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  15. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  16. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  17. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  18. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +4 -0
  19. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
  20. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
  21. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
  22. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
  23. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  24. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  25. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +329 -55
  26. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +26 -5
  27. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
  28. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +46 -7
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +7 -4
  33. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +86 -39
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
  40. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +124 -181
  41. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  42. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  43. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +56 -3
  44. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +170 -153
  45. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  46. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -45
  47. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/{TextChangeAnimator.kt → TextMorph.kt} +79 -129
  48. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  49. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  50. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  51. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  52. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  53. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  54. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  55. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  56. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  57. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  58. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  59. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  60. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  61. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  62. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  63. package/android/src/main/res/values/styles.xml +1 -1
  64. package/android/src/main/res/values-night/styles.xml +1 -1
  65. package/cli/doctor/checks/app.js +16 -1
  66. package/cli/project.js +4 -0
  67. package/cli/setup/index.js +5 -0
  68. package/cli/setup/steps/metro.js +46 -0
  69. package/docs-md/architecture/native-screens.md +4 -0
  70. package/docs-md/architecture/theme-and-context.md +131 -12
  71. package/docs-md/architecture/transitions.md +43 -4
  72. package/docs-md/flows/bottom-sheets.md +4 -2
  73. package/docs-md/fundamentals/screens-and-props.md +9 -1
  74. package/docs-md/guides/cli.md +3 -1
  75. package/docs-md/help/troubleshooting.md +1 -1
  76. package/docs-md/index.json +29 -1
  77. package/docs-md/screen-ui/back-and-gestures.md +3 -1
  78. package/docs-md/screen-ui/bar-buttons.md +1 -1
  79. package/docs-md/screen-ui/toolbar-and-fab.md +2 -0
  80. package/docs-md/start/installation.md +16 -1
  81. package/ios/Animations/ANNFrameDriver.h +55 -15
  82. package/ios/Animations/ANNFrameDriver.mm +214 -60
  83. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  84. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  85. package/ios/Animations/ANNTransitionPlan.h +2 -2
  86. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  87. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  88. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  89. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  90. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  91. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  92. package/ios/Animations/Elements/ANNSharedElementFlight.mm +181 -43
  93. package/ios/Animations/Interpolators/ANNViewTracks.h +6 -5
  94. package/ios/Animations/Interpolators/ANNViewTracks.mm +51 -21
  95. package/ios/CLAUDE.md +19 -8
  96. package/ios/CORE.md +1 -1
  97. package/ios/Core/ANNAppDelegate.mm +0 -2
  98. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  99. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  100. package/ios/Core/ANNSceneDelegate.mm +2 -2
  101. package/ios/Core/ANNUtils.h +7 -0
  102. package/ios/Core/ANNUtils.mm +10 -0
  103. package/ios/Core/ApexNavigation.mm +6 -2
  104. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +1 -7
  105. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  106. package/ios/Layouts/Screen/ANNComponentViewController.mm +4 -1
  107. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  108. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  109. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  110. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  111. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  112. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  113. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  114. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +2 -4
  115. package/ios/Options/ANNAnimationOptions.h +2 -1
  116. package/ios/Options/ANNAnimationOptions.mm +1 -1
  117. package/ios/Options/ANNOptions.h +7 -0
  118. package/ios/Options/ANNOptions.mm +45 -0
  119. package/ios/Options/ANNOptionsReader.mm +2 -4
  120. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  121. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  122. package/ios/Options/Params/ColorParser.h +4 -3
  123. package/ios/Options/Params/ColorParser.mm +22 -4
  124. package/ios/React/ANNSurfaceHostingView.h +8 -0
  125. package/ios/React/ANNSurfaceHostingView.mm +28 -1
  126. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  127. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  128. package/ios/TurboModules/ANNTurboModule.mm +46 -0
  129. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  130. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  131. package/lib/module/ARCHITECTURE.md +9 -3
  132. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  133. package/lib/module/api/app/AppRegistry.js +14 -2
  134. package/lib/module/api/app/AppRegistry.js.map +1 -1
  135. package/lib/module/api/app/createNavigation.js +188 -66
  136. package/lib/module/api/app/createNavigation.js.map +1 -1
  137. package/lib/module/api/events/navigationEvents.js +6 -2
  138. package/lib/module/api/events/navigationEvents.js.map +1 -1
  139. package/lib/module/api/linking/linking.js +11 -0
  140. package/lib/module/api/linking/linking.js.map +1 -1
  141. package/lib/module/api/screens/defineScreen.js +45 -3
  142. package/lib/module/api/screens/defineScreen.js.map +1 -1
  143. package/lib/module/api/screens/nativeScreens.js +31 -6
  144. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  145. package/lib/module/api/screens/registerScreens.js +193 -35
  146. package/lib/module/api/screens/registerScreens.js.map +1 -1
  147. package/lib/module/api/theme/theme.js +5 -0
  148. package/lib/module/api/theme/theme.js.map +1 -1
  149. package/lib/module/engine/commands.js +21 -1
  150. package/lib/module/engine/commands.js.map +1 -1
  151. package/lib/module/engine/layout.js +7 -2
  152. package/lib/module/engine/layout.js.map +1 -1
  153. package/lib/module/engine/nativeScreens.js +9 -0
  154. package/lib/module/engine/nativeScreens.js.map +1 -1
  155. package/lib/module/engine/options.js +5 -4
  156. package/lib/module/engine/options.js.map +1 -1
  157. package/lib/module/engine/optionsChange.js +102 -0
  158. package/lib/module/engine/optionsChange.js.map +1 -0
  159. package/lib/module/engine/refresh.js +85 -0
  160. package/lib/module/engine/refresh.js.map +1 -0
  161. package/lib/module/engine/rememberedOptions.js +60 -0
  162. package/lib/module/engine/rememberedOptions.js.map +1 -1
  163. package/lib/module/engine/screens.js +26 -0
  164. package/lib/module/engine/screens.js.map +1 -1
  165. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  166. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  167. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  168. package/lib/typescript/api/app/createNavigation.d.ts +5 -0
  169. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  170. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  171. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  172. package/lib/typescript/api/index.d.ts +1 -1
  173. package/lib/typescript/api/index.d.ts.map +1 -1
  174. package/lib/typescript/api/linking/linking.d.ts +8 -0
  175. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  176. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  177. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  178. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  179. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  180. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  181. package/lib/typescript/api/theme/theme.d.ts +2 -0
  182. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  183. package/lib/typescript/engine/commands.d.ts +5 -0
  184. package/lib/typescript/engine/commands.d.ts.map +1 -1
  185. package/lib/typescript/engine/layout.d.ts.map +1 -1
  186. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  187. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  188. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  189. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  190. package/lib/typescript/engine/refresh.d.ts +35 -0
  191. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  192. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  193. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  194. package/lib/typescript/engine/screens.d.ts +7 -0
  195. package/lib/typescript/engine/screens.d.ts.map +1 -1
  196. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  197. package/lib/typescript/interfaces/options/animations.d.ts +40 -15
  198. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  199. package/lib/typescript/interfaces/options/bottomTabs.d.ts +2 -2
  200. package/lib/typescript/interfaces/options/buttons.d.ts +2 -2
  201. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  202. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  203. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  204. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  205. package/lib/typescript/interfaces/options/topBar.d.ts +14 -0
  206. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  207. package/metro/acceptPlugin.js +57 -0
  208. package/metro/babelTransformer.js +45 -0
  209. package/metro/index.js +36 -0
  210. package/package.json +3 -1
  211. package/src/ARCHITECTURE.md +9 -3
  212. package/src/adapters/NativeANNTurboModule.ts +7 -0
  213. package/src/api/app/AppRegistry.ts +16 -2
  214. package/src/api/app/createNavigation.ts +198 -66
  215. package/src/api/events/navigationEvents.ts +9 -3
  216. package/src/api/index.ts +1 -0
  217. package/src/api/linking/linking.ts +22 -1
  218. package/src/api/screens/defineScreen.ts +49 -5
  219. package/src/api/screens/nativeScreens.ts +40 -4
  220. package/src/api/screens/registerScreens.tsx +185 -42
  221. package/src/api/theme/theme.ts +5 -0
  222. package/src/engine/commands.ts +21 -0
  223. package/src/engine/layout.ts +11 -2
  224. package/src/engine/nativeScreens.ts +10 -1
  225. package/src/engine/options.ts +6 -8
  226. package/src/engine/optionsChange.ts +107 -0
  227. package/src/engine/refresh.ts +97 -0
  228. package/src/engine/rememberedOptions.ts +70 -0
  229. package/src/engine/screens.tsx +24 -0
  230. package/src/interfaces/ComponentEvents.ts +2 -2
  231. package/src/interfaces/options/animations.ts +40 -15
  232. package/src/interfaces/options/bottomTabs.ts +2 -2
  233. package/src/interfaces/options/buttons.ts +2 -2
  234. package/src/interfaces/options/modals.ts +6 -7
  235. package/src/interfaces/options/stack.ts +8 -3
  236. package/src/interfaces/options/topBar.ts +14 -0
  237. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  238. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -134
  239. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -78
  240. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  241. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -57
  242. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -146
@@ -0,0 +1,46 @@
1
+ // @ts-check
2
+ 'use strict';
3
+
4
+ /** @typedef {import('./types').StepResult} StepResult */
5
+
6
+ const REQUIRE = "const { withApexNavigation } = require('@apexnative/navigation/metro');";
7
+
8
+ // The config the file exports: `module.exports = …;`, its last statement.
9
+ const EXPORT = /^module\.exports\s*=\s*([\s\S]+?);?\s*$/m;
10
+
11
+ const HOW_TO = `Wrap the config metro.config.js exports, so an edit updates the app by Fast Refresh instead of
12
+ reloading it:
13
+
14
+ ${REQUIRE}
15
+
16
+ module.exports = withApexNavigation(mergeConfig(getDefaultConfig(__dirname), config));`;
17
+
18
+ /**
19
+ * metro.config.js: the config it exports goes through withApexNavigation, which puts the navigation's Babel
20
+ * transformer in front of the app's own.
21
+ * @param {string} text
22
+ * @returns {StepResult}
23
+ */
24
+ function planMetro(text) {
25
+ if (text.includes('withApexNavigation')) return { status: 'done', note: 'already set up' };
26
+ const found = EXPORT.exec(text);
27
+ // Only an export that ends the file: one in the middle may be changed by what comes after it.
28
+ if (!found || text.slice(found.index + found[0].length).trim() !== '') {
29
+ return { status: 'manual', note: 'changed by hand, see below', howTo: HOW_TO };
30
+ }
31
+ const exported = found[1].trim();
32
+ const before = text.slice(0, found.index);
33
+ const requires = [...before.matchAll(/^.*require\(.*\);?$/gm)];
34
+ const last = requires[requires.length - 1];
35
+ const head =
36
+ last === undefined
37
+ ? `${REQUIRE}\n\n${before}`
38
+ : `${before.slice(0, last.index + last[0].length)}\n${REQUIRE}${before.slice(last.index + last[0].length)}`;
39
+ return {
40
+ status: 'change',
41
+ next: `${head}module.exports = withApexNavigation(${exported});\n`,
42
+ note: 'Fast Refresh for every edit',
43
+ };
44
+ }
45
+
46
+ module.exports = { planMetro, HOW_TO };
@@ -255,6 +255,10 @@ A wrong name or a missing prop is an error of the compiler, not of the app at ru
255
255
  - **Which are left out:** a screen with a required prop that is a function, and one with a prop whose type has no native form (see [Types](#types)). `codegen` names each in a warning. Native code still opens it by its name, `push("Settings", props: [:])`, with its props as a plain dictionary or map.
256
256
  - **After a change** of the screens or their props, run `codegen` again; `doctor` says when the generated code is behind.
257
257
 
258
+ ## Fast Refresh
259
+
260
+ `nativeScreen` returns a component that runs `useProps`. An edit of the hook or of the options applies in place, with the app running. A change of the native name, or adding or removing `useProps`, restarts the app (a reload).
261
+
258
262
  ## UIKit and Android views
259
263
 
260
264
  A screen of UIKit or Android views gets the same `screen`, and follows its props as they change:
@@ -5,7 +5,7 @@ Two things every screen shares: the theme, the colors of the bars and background
5
5
  Every screen of an app shares a look and a few facts: the colors of the bars, the signed-in user, the language. The navigation keeps both for every screen. The **theme** is the colors of the bars, tabs and backgrounds. The **context** is a small object of the app that every screen reads. This page answers:
6
6
 
7
7
  1. [Why the navigation keeps them](#why), and not a React provider.
8
- 2. [The theme](#theme): its colors, dark mode, changing it, Android's own colors.
8
+ 2. [The theme](#theme): its colors, dark mode, changing it, [Android's theme](#android).
9
9
  3. [The context](#context): filling it, reading it, typing it.
10
10
  4. [What goes in the context](#context-roles), and what goes elsewhere.
11
11
 
@@ -37,7 +37,7 @@ export const Navigation = createNavigation({
37
37
 
38
38
  Every top bar and the tab bar are navy with light titles; the buttons, the back arrow and the selected tab are orange.
39
39
 
40
- A color left out keeps the platform's look. On iOS 26 a theme without `bar` keeps the bars on glass. On Android the bars take the colors of the app's Material 3 theme, neutral grays by default (see [Android's colors](#android)).
40
+ A color left out keeps the platform's look. On iOS 26 a theme without `bar` keeps the bars on glass. On Android the bars take the colors of the app's Material 3 theme, neutral grays by default (see [Android's theme](#android)).
41
41
 
42
42
  The theme is the bottom layer of the options of every screen. A screen that needs its own look sets it in its options, and they go over the theme (see [Where a screen gets them](../fundamentals/screen-options#layers)).
43
43
 
@@ -106,9 +106,9 @@ The options of the app and of a screen can be built from the theme: their functi
106
106
 
107
107
  So a color that follows the theme belongs in the theme itself, or in the app's `options`.
108
108
 
109
- ### Android's colors
109
+ ### Android's theme
110
110
 
111
- (Android) The bars, tabs, search, sheets and menus are Material 3 views. What the theme and the options leave out, they take from the app's Android theme, as every Material view of the app does. On an app made from React Native's template, `setup` makes the app's theme inherit `Theme.ApexNavigation`: Material 3 in neutral grays, light and dark, with no tint of its own.
111
+ (Android) The bars, tabs, search, sheets, menus and the floating action button are Material 3 views. What the theme and the options leave out, they take from the app's Android theme, as every Material view of the app does. On an app made from React Native's template, `setup` makes the app's theme inherit `Theme.ApexNavigation`: Material 3 in neutral grays, light and dark, with no tint of its own.
112
112
 
113
113
  An app changes any of these colors in `styles.xml`. A color that differs in dark mode is a color resource with a second value in `values-night`, so the one style serves both modes:
114
114
 
@@ -130,19 +130,138 @@ The selected tab now sits on a peach pill in light mode, and a brown one in dark
130
130
 
131
131
  | Material 3 color | What takes it |
132
132
  | --- | --- |
133
- | `colorSurface` | The top bar. |
134
- | `colorOnSurface` | The title and the back arrow. |
135
- | `colorOnSurfaceVariant` | The subtitle and the "⋮" of the bar. |
136
- | `colorSurfaceContainer`, `colorSurfaceContainerLow`, `colorSurfaceContainerHigh` | The tab bar and the menus of the bar; a sheet; the search field. |
133
+ | `colorSurface` | The top bar, and the menus of the bar while the bar is opaque. |
134
+ | `colorOnSurface` | The title, the large title, the back arrow, the text of the search. |
135
+ | `colorOnSurfaceVariant` | The subtitle and the "⋮" of the bar; the hint of the search. |
136
+ | `colorSurfaceContainer` | The tab bar; the menus of the bar when the bar lets the screen through. |
137
+ | `colorSurfaceContainerLow` | A sheet. |
138
+ | `colorSurfaceContainerHigh` | The search field and its suggestions. |
137
139
  | `colorSecondaryContainer` | The pill of the selected tab, the selected scope of the search. |
138
140
  | `colorPrimaryContainer` | The floating action button. |
139
- | `colorError` | The badges of the tabs. |
141
+ | `colorError` | The badges of the tabs, the destructive items of the bar's menus. |
142
+ | `android:colorBackground` | Behind a screen without `layout.backgroundColor`. It comes from the activity's own theme, Material 3 or not; a screen in a sheet shows the sheet's color. |
140
143
 
141
- The theme's `colors` and a screen's options go over these: `bar` over the top bar and the tab bar, `text` over the titles, `badge` over the badges.
144
+ The theme's `colors` and a screen's options go over these: `bar` over the top bar and the tab bar, `text` over the titles, `badge` over the badges. The menus of the bar take the bar's color and the title's color while the bar is opaque; `topBar.menuBackgroundColor` and `topBar.menuTextColor` set them apart. These two are Android only: iOS draws its menus in the system's colors.
142
145
 
143
146
  - **An app with a Material 3 theme of its own** keeps it, and the navigation takes its colors.
144
- - **An app with a theme that isn't Material 3** keeps it too. The bars, tabs, search and sheets then take `Theme.ApexNavigation` themselves, and follow the app's light or dark mode.
145
- - **An app that is only light** sets it in code: `AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)`.
147
+ - **An app with a theme that isn't Material 3** keeps it too, and the navigation's views take `Theme.ApexNavigation` (see [the next section](#android-other-themes)).
148
+ - **An app that is only light** sets it in code: `AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)`. `Theme.ApexNavigation` follows dark mode, so without it the navigation's views turn dark with the device.
149
+
150
+ #### An app with an AppCompat or MaterialComponents theme
151
+
152
+ Many apps keep a theme of their own, such as `Theme.AppCompat.Light.NoActionBar` or `Theme.MaterialComponents.DayNight.NoActionBar`. `setup` leaves it as it is: it changes only the template's `Theme.AppCompat.DayNight.NoActionBar`.
153
+
154
+ The navigation tells a Material 3 theme by `colorSurfaceContainerHigh`, a role only Material 3 themes have. When the activity's theme has none, each view of the navigation is made in a copy of the app's theme with `Theme.ApexNavigation` **laid over it**. Over it means it wins: the colors, text appearances and shapes it sets replace the app's, so the app's `colorPrimary` and `colorAccent` don't reach the bars, and the bars are neutral gray next to the app's own colors. The app's own views keep its theme.
155
+
156
+ There are two ways to give the bars the app's colors.
157
+
158
+ **Move the theme to Material 3.** The parent becomes `Theme.ApexNavigation`, or `Theme.Material3.DayNight.NoActionBar` for Material's own palette, and the brand colors go to Material 3's roles, the ones in the table above:
159
+
160
+ ```xml
161
+ <!-- Before: parent="Theme.MaterialComponents.DayNight.NoActionBar" -->
162
+ <style name="AppTheme" parent="Theme.ApexNavigation">
163
+ <item name="colorSecondaryContainer">@color/selected_tab</item>
164
+ <item name="colorPrimaryContainer">@color/brand</item>
165
+ </style>
166
+ ```
167
+
168
+ The selected tab and the floating action button take the brand colors. Attributes Material 3 has no role for, such as `colorAccent`, `colorPrimaryDark` or `colorPrimaryVariant`, do nothing in the navigation. The app's own native views change with the theme too: its AppCompat and Material widgets and dialogs take the Material 3 look.
169
+
170
+ **Keep the theme, and color the navigation from JavaScript.** The colors of the [theme](#theme) are options, not Android theme attributes, so they paint the bars with any Android theme:
171
+
172
+ ```ts
173
+ export const Navigation = createNavigation({
174
+ // … screens and root
175
+ theme: {
176
+ colors: {
177
+ primary: '#E76F51',
178
+ bar: '#1E2A44',
179
+ text: '#F3E9D2',
180
+ badge: '#E63946',
181
+ },
182
+ },
183
+ });
184
+ ```
185
+
186
+ The bars, the titles, the buttons, the selected tab and the badges take the brand colors. The rest stays in `Theme.ApexNavigation`'s grays: the pill of the selected tab, a sheet, the search field, the floating action button. The navigation has **no theme attribute or overlay of its own** for them. Two of them have an option of their color: `fab.backgroundColor` and `topBar.searchBar.backgroundColor`; the pill of the selected tab and a sheet have none.
187
+
188
+ The way to recolor them in resources is to replace `Theme.ApexNavigation` itself: a style of the same name in the app wins over the library's when the build merges the resources. It replaces the whole style, so it has Material 3's parent, and it goes in `values` and again in `values-night`, or dark mode keeps the library's grays:
189
+
190
+ ```xml
191
+ <style name="Theme.ApexNavigation"
192
+ parent="Theme.Material3.DayNight.NoActionBar">
193
+ <item name="colorSecondaryContainer">#FCE4D6</item>
194
+ </style>
195
+ ```
196
+
197
+ ```xml
198
+ <style name="Theme.ApexNavigation"
199
+ parent="Theme.Material3.DayNight.NoActionBar">
200
+ <item name="colorSecondaryContainer">#5C3B2E</item>
201
+ </style>
202
+ ```
203
+
204
+ A role the app's style leaves out takes Material 3's own palette, with its violet tint, and not the navigation's grays.
205
+
206
+ #### Fonts and shapes
207
+
208
+ An app with a font of its own sets it in the options, which work with any Android theme. The app's `options` give it to every screen:
209
+
210
+ ```ts
211
+ export const Navigation = createNavigation({
212
+ // … screens and root
213
+ options: {
214
+ topBar: {
215
+ title: { fontFamily: 'Inter', fontWeight: '600' },
216
+ subtitle: { fontFamily: 'Inter' },
217
+ largeTitle: { fontFamily: 'Inter' },
218
+ },
219
+ bottomTab: { fontFamily: 'Inter' },
220
+ },
221
+ });
222
+ ```
223
+
224
+ The family is the name a `Text` style takes: a font in `assets/fonts/` or a `res/font` resource React Native knows. Without options, each text takes its font from the app's theme, or from Material 3's text appearances:
225
+
226
+ | Text | Without options | Options over it |
227
+ | --- | --- | --- |
228
+ | The title and the subtitle | 22 sp and 14 sp, set in code; the theme's `android:fontFamily`, else the system font. | `topBar.title.fontFamily`, `fontSize`, `fontWeight`, `fontStyle`; the same in `topBar.subtitle`. |
229
+ | The large title | 28 sp, set in code; the font as the title. | The same in `topBar.largeTitle`. |
230
+ | A text button of the bar | The theme's `actionMenuTextAppearance`. | `fontFamily`, `fontSize`, `fontWeight`, `fontStyle` of the button. |
231
+ | The labels of the tabs | `textAppearanceLabelMedium`, through Material 3's style of the tab bar. | `bottomTab.fontFamily`, `fontWeight`, `fontStyle`; `fontSize` and `selectedFontSize`. |
232
+ | The items of the bar's menus | `textAppearanceLargePopupMenu`, which Material 3 points to `textAppearanceBodyLarge`; the title of a submenu `textAppearancePopupMenuHeader`. | None. |
233
+ | The text of the search | 16 sp. The field in the bar takes the font as the title; the pill under the title, Material's `SearchBar`, its own `TextAppearance.Material3.SearchBar`. | None. |
234
+ | The scopes of the search | `textAppearanceLabelLarge`. | None. |
235
+
236
+ The shapes come mostly from code, not from `shapeAppearance…` attributes:
237
+
238
+ | Shape | Without options | Option over it |
239
+ | --- | --- | --- |
240
+ | A sheet | 28 dp, the top corners only, set in code. | `modal.sheet.cornerRadius`. |
241
+ | The menus of the bar | The theme's `popupMenuBackground`, a drawable: 4 dp in Material 3. | None. |
242
+ | The search field | A capsule: set in code in the bar, Material's `SearchBar` style under the title; the suggestions 16 dp, set in code. While the search fills the screen, Material's `SearchView` has no corners. | None. |
243
+ | The scopes of the search | `shapeAppearanceCornerSmall`. | None. |
244
+ | The tab bar | Square. | `bottomTabs.cornerRadius`. |
245
+ | The floating action button | `shapeAppearanceCornerLarge`. | None. |
246
+
247
+ An app with a Material 3 theme changes the theme's part in `styles.xml`. These attributes are the app's own: its other views that use them change too.
248
+
249
+ ```xml
250
+ <style name="AppTheme" parent="Theme.ApexNavigation">
251
+ <item name="android:fontFamily">@font/inter</item>
252
+ <item name="textAppearanceLabelMedium">@style/TabLabel</item>
253
+ <item name="popupMenuBackground">@drawable/menu</item>
254
+ </style>
255
+
256
+ <style name="TabLabel" parent="TextAppearance.Material3.LabelMedium">
257
+ <item name="fontFamily">@font/inter</item>
258
+ <item name="android:fontFamily">@font/inter</item>
259
+ </style>
260
+ ```
261
+
262
+ The title, the large title and the labels of the tabs are in Inter, and so is the search field in the bar from Android 8 on: before it, a plain text field can't load a font resource. The menus of the bar take the shape of `menu.xml`, in the bar's color while the bar is opaque.
263
+
264
+ In an app whose theme isn't Material 3, `Theme.ApexNavigation` lies over the app's theme, so the text appearances, `popupMenuBackground` and the shapes are `Theme.ApexNavigation`'s whatever the app's theme says. `Theme.ApexNavigation` doesn't set `android:fontFamily`, so the app's own still reaches the title, the large title and the search field in the bar. For the rest, the options are the way.
146
265
 
147
266
  ## The context
148
267
 
@@ -84,7 +84,7 @@ The avatar of the row moves and grows into the photo of the profile, and the nam
84
84
 
85
85
  A view turned or scaled by its own `transform`, or by a parent's, flies from where it shows and lands as the other view shows, turning the shorter way. The angle is the one React Native draws, from −180° to 180°: `rotate: '270deg'` turns back a quarter, and no pair spins a full turn; a spin is an animation of the view's own.
86
86
 
87
- Both platforms. (Android) The next screen waits until it is drawn, so its views are there to move into; a `type` other than a fade gives way to a fade. A skew and a tilt (`skewX`, `rotateX`, …) don't fly: the view flies without them and has them again at the end.
87
+ What flies is a copy: pictures of the two views, taken as the transition starts, while the views wait hidden in their places. Both platforms. (Android) The next screen waits until it is drawn, so its views are there to take pictures of; an image flies as its own image, from its fit in one view to its fit in the other; a `type` other than a fade gives way to a fade. A skew and a tilt (`skewX`, `rotateX`, …) don't fly: the view flies without them and has them again at the end.
88
88
 
89
89
  ### Zoom
90
90
 
@@ -148,21 +148,60 @@ navigation.push('Details', {
148
148
 
149
149
  Details rises 300 points as it fades in. Each property animates from a value to a value over a time, with a delay and an interpolation if it needs them; the [API](#api) lists every one.
150
150
 
151
- The same goes for `pop`, `showModal`, `dismissModal`, `setStackRoot` and `setRoot`. The swipe back is the exception: (iOS) it always follows the finger with the system's slide, so a custom `pop` plays for the back button and `pop()` only. (Android) A pop with an animation of its own shows no preview under the swipe: the screen pops when the swipe ends.
151
+ (iOS) Once the move starts, the system draws it on its own, as it draws UIKit's animations: work on the main thread, such as a screen that mounts a long list, doesn't stop it.
152
+
153
+ The same goes for `pop`, `showModal`, `dismissModal`, `setStackRoot` and `setRoot`. The swipe back is the exception: (iOS) by default it follows the finger with the system's slide, so a custom `pop` plays for the back button and `pop()` only. (Android) A pop with an animation of its own shows no preview under the swipe: the screen pops when the swipe ends.
154
+
155
+ ### The swipe back with a pop of your own
156
+
157
+ (iOS) A screen that leaves with a move of its own, such as a card that shrinks away, looks wrong when the swipe back slides it out like any other screen. `interactive` on its pop plays its own move under the finger instead:
158
+
159
+ ```ts
160
+ navigation.push('Details', {
161
+ options: {
162
+ animations: {
163
+ pop: {
164
+ interactive: true,
165
+ content: {
166
+ exit: {
167
+ scaleX: { from: 1, to: 0.8, duration: 400 },
168
+ scaleY: { from: 1, to: 0.8, duration: 400 },
169
+ alpha: { from: 1, to: 0, duration: 400 },
170
+ },
171
+ },
172
+ },
173
+ },
174
+ },
175
+ });
176
+ ```
177
+
178
+ The swipe from the edge now shrinks Details as far as the finger has gone across the screen. The user can stop halfway; letting go past half way, or a flick back, finishes the pop, and letting go before half way, or a flick the other way, grows the screen back. After a quick flick the rest plays faster, never slower. The elements and the shared elements of the pop move with it, so a [shared element](#views) flies home under the finger.
179
+
180
+ - It applies only to a pop with an animation of its own, and `transition` presets get it through `options.animations.pop.interactive`.
181
+ - The swipe moves the time of the animation, not a point: a move with an easing doesn't stay exactly under the finger.
182
+ - On iOS 26 the swipe from the content stays off for a screen with a pop of its own; the swipe from the edge works.
183
+ - The top bar shows the screen under from the start of the swipe, as it does in any move of your own, and comes back if the user gives the swipe up.
184
+ - While the finger is down, the system's animations inside the two screens, such as an activity indicator, hold still.
185
+
186
+ (Android) It doesn't apply: a pop with an animation of its own plays when the back gesture ends.
152
187
 
153
188
  ### The top bar
154
189
 
155
190
  The top bar goes its platform's way in a preset or an animation of your own. (Android) It goes with its screen, as if it were part of it: with `slideUp` the new screen comes up with its bar over the old one and its bar, and on the way back it goes down with its bar, uncovering the bar below. With `fade` or `scale` the bar fades or grows with its screen. (iOS) UIKit keeps the bar in place and changes its title and buttons as the transition starts; only the screens under it move.
156
191
 
192
+ ### When the user reduces motion
193
+
194
+ The system's setting to reduce motion wins over a move of your own, with nothing to set. (iOS) With Reduce Motion on, the move becomes a fade of the same length: the screen that comes fades in on a push, a modal's show and `setRoot`, and the screen that goes fades out on a pop and a dismissal. No element moves. (Android) With Remove animations on, every animation of the app ends at once: the move jumps to its end, no element moves and the push doesn't wait for one, a side menu opens and closes without its slide, and a sheet shows and closes without it.
195
+
157
196
  ## Which to pick
158
197
 
159
198
  | The move | Pick | What it costs |
160
199
  | --- | --- | --- |
161
200
  | Most moves | The platform's | Nothing: it follows the finger and the system. |
162
- | A move that means something else, such as a fading modal | A preset | The swipe back on iOS still slides. |
201
+ | A move that means something else, such as a fading modal | A preset | The swipe back on iOS still slides, unless the pop is [`interactive`](#custom-swipe). |
163
202
  | A view that becomes the next screen | Shared elements, or the zoom on iOS 18+ | A `nativeID` on both views. The zoom shows on iOS 18+ only. |
164
203
  | A look at a screen before opening it | The preview | iOS only. |
165
- | A move no preset has | `options.animations` | Every property spelled out; the swipe back on iOS still slides. |
204
+ | A move no preset has | `options.animations` | Every property spelled out; the swipe back on iOS still slides, unless the pop is [`interactive`](#custom-swipe). |
166
205
 
167
206
  ## Waiting for the content
168
207
 
@@ -152,7 +152,7 @@ navigation.showModal('PhotoActions', {
152
152
  });
153
153
  ```
154
154
 
155
- The content is measured after every render, so the sheet follows it when it grows. It needs (iOS) 16; on (Android) the sheet stops at half the height instead, as the Material sheet has no height of its content. A screen that fills its parent (`flex: 1`) has no height of its own, so give the content of this screen its natural height. On (iOS) UIKit adds the bottom safe area to the height of a `'content'` sheet itself, so don't add `insets.bottom` to its content: the gap would come twice.
155
+ The content is measured after every render, so the sheet follows it when it grows. It needs (iOS) 16. A screen that fills its parent (`flex: 1`) has no height of its own, so give the content of this screen its natural height. The sheet adds the bottom safe area to the height of a `'content'` sheet itself (UIKit on (iOS), the navigation bar on (Android)), so don't add `insets.bottom` to its content: the gap would come twice.
156
156
 
157
157
  ## 8. Close it with a pick
158
158
 
@@ -218,8 +218,10 @@ The sheet is (iOS) UIKit's sheet, and on (Android) the Material bottom sheet. Th
218
218
  | --- | --- | --- |
219
219
  | `'medium'`, `'large'` | Half and full height. | Half height, and full height under the status bar. |
220
220
  | A number | That height, in points. iOS 16+: iOS 15 leaves it out. | That height, in dp. |
221
- | `'content'` | The height of the content. iOS 16+: iOS 15 leaves it out. | Half the height: the Material sheet has no height of its content. |
221
+ | How many stops | Every detent of the list. | Three at most: the lowest, the highest, and one between them (`selectedDetent` when it stands there, else the lowest of the others). |
222
+ | `'content'` | The height of the content. iOS 16+: iOS 15 leaves it out. | The height of the content. |
222
223
  | The screen under it | Shrinks back a little at full height, as UIKit does. | Stays in place. |
224
+ | The keyboard | The sheet rises and stands on it. | The sheet rises and stands on it at every stop, unless the screen in it has `layout.adjustResize: false`. |
223
225
 
224
226
  ## The whole code
225
227
 
@@ -70,7 +70,7 @@ What `screens` takes, from the simplest:
70
70
  | Entry | When |
71
71
  | --- | --- |
72
72
  | A component | A screen without options of its own: `Settings`. |
73
- | `defineScreen({ component, options })` | A screen with its options next to it: see [Options](#options). |
73
+ | `defineScreen({ component, options })` | A screen with its options next to it: see [Options](#options). It returns a component that renders the screen. |
74
74
  | `{ load: () => import('./screens/Settings') }` | A screen whose code runs the first time it shows: see [Loading later](#lazy). |
75
75
  | `nativeScreen('ProfileScreen', …)` | A screen written in SwiftUI, UIKit or Compose: see [Native screens](../architecture/native-screens). |
76
76
  | `defineArrangement({ primary, secondary })` | Two screens shown as one, side by side in a large window: see [Arrangement](../containers/arrangement). |
@@ -172,10 +172,18 @@ export const DetailsScreen = defineScreen({
172
172
  | `props` | The props of this showing, typed from the component. |
173
173
  | `{ theme, context }` | The theme of the navigation and the app's context, as they are when the screen shows. |
174
174
 
175
+ `defineScreen` returns a component of its own, typed `DefinedScreen`, which renders your component and carries the definition; your component stays as it is, for any other use of it. Put the result in `screens` as it is. Rendered alone, in a test, it renders the screen.
176
+
175
177
  The options go to the native side with the command, so they apply **before the screen appears**: the title is there from the first frame, not set after a render.
176
178
 
177
179
  Options come from several places, and the later go over the earlier: the screen's [group](#groups), its `defineScreen`, the `options` of the command, and `setOptions` or `useScreenOptions` from the screen itself. The whole order, with the theme and the defaults of the app: [Screen options](./screen-options).
178
180
 
181
+ ### Fast Refresh
182
+
183
+ An edit of a screen file updates the screen in place, with its state, when the file is a Fast Refresh boundary: it exports only screens and components. A constant or an object exported from it makes it not a boundary. With `withApexNavigation` in `metro.config.js` ([Installation](../start/installation#fast-refresh)) such an edit still doesn't reload the app: it goes up to the module of the app's `createNavigation`, which updates the running app.
184
+
185
+ An edit of `options` reaches the screens of it at once, a tab not rendered yet too. Changed and added options are merged in; an option taken out is cleared. An option the command gave the screen, or one the screen set since (`useScreenOptions`, `mergeOptions`), stays as it is. An edit of `key` or `arrangement` applies from the next show.
186
+
179
187
  ## The life of a screen
180
188
 
181
189
  A screen isn't only mounted or not. Under another screen it is **still mounted but not seen**: its state and scroll stay, and it comes back as it was. So a screen has two lines of life: React's, from mount to unmount, and the user's, each time it appears and disappears.
@@ -107,7 +107,7 @@ A new app doesn't have the package yet, so `init` runs through the package's nam
107
107
 
108
108
  ## setup
109
109
 
110
- Sets the app you have up for the navigation: the native entry points of both platforms, `index.js`, a new `Navigation.ts` with your `App` as the first screen, and `navigation-env.d.ts`, the [typed commands](../start/installation#typed-commands). Each change and why: [What setup changes, and why](../start/installation#why-native).
110
+ Sets the app you have up for the navigation: the native entry points of both platforms, `index.js`, a new `Navigation.ts` with your `App` as the first screen, `navigation-env.d.ts`, the [typed commands](../start/installation#typed-commands). It also wraps `metro.config.js` in `withApexNavigation`, for [Fast Refresh](../start/installation#fast-refresh). Each change and why: [What setup changes, and why](../start/installation#why-native).
111
111
 
112
112
  ```bash
113
113
  npx apex-navigation setup
@@ -169,6 +169,8 @@ npx apex-navigation doctor --fix
169
169
  | `--fix` | Makes the fixes the CLI has, for errors and warnings alike: `setup`, `links`, `icons`, `codegen` and `pod install`, asking before each. It needs a terminal to ask in. |
170
170
  | `--json` | `{ ok, checks: [{ group, title, status, detail, fix }] }`; `status` is `ok`, `warn`, `error` or `info`, and `fix` is `{ label, run }`, where `run` names the command `--fix` runs. Not with `--fix`. |
171
171
 
172
+ It warns under Fast Refresh when the app's `metro.config.js` doesn't use `withApexNavigation`; the fix is `setup`.
173
+
172
174
  It exits with 1 when a check finds an error; warnings leave it at 0.
173
175
 
174
176
  ## info
@@ -564,7 +564,7 @@ More: [Ask before changes are lost](../flows/forms-in-a-modal#prevent).
564
564
 
565
565
  A sheet with the detent `'content'` isn't as tall as what it shows.
566
566
 
567
- **Why:** a screen that fills its parent (`flex: 1`) has no height of its own to measure. On (Android) `'content'` stops at half the height, as the Material sheet has no height of its content.
567
+ **Why:** a screen that fills its parent (`flex: 1`) has no height of its own to measure.
568
568
 
569
569
  **Fix:** give the content of the screen its natural height, without `flex: 1`. More: [A sheet as tall as its content](../flows/bottom-sheets#content).
570
570
 
@@ -87,6 +87,10 @@
87
87
  "heading": "JavaScript files",
88
88
  "anchor": "js-files"
89
89
  },
90
+ {
91
+ "heading": "Fast Refresh",
92
+ "anchor": "fast-refresh"
93
+ },
90
94
  {
91
95
  "heading": "Typed commands",
92
96
  "anchor": "typed-commands"
@@ -276,6 +280,10 @@
276
280
  "heading": "Options",
277
281
  "anchor": "options"
278
282
  },
283
+ {
284
+ "heading": "Fast Refresh",
285
+ "anchor": "fast-refresh"
286
+ },
279
287
  {
280
288
  "heading": "The life of a screen",
281
289
  "anchor": "lifecycle"
@@ -1183,9 +1191,17 @@
1183
1191
  "anchor": "theme-options"
1184
1192
  },
1185
1193
  {
1186
- "heading": "Android's colors",
1194
+ "heading": "Android's theme",
1187
1195
  "anchor": "android"
1188
1196
  },
1197
+ {
1198
+ "heading": "An app with an AppCompat or MaterialComponents theme",
1199
+ "anchor": "android-other-themes"
1200
+ },
1201
+ {
1202
+ "heading": "Fonts and shapes",
1203
+ "anchor": "android-fonts-shapes"
1204
+ },
1189
1205
  {
1190
1206
  "heading": "The context",
1191
1207
  "anchor": "context"
@@ -1335,10 +1351,18 @@
1335
1351
  "heading": "Animations of your own",
1336
1352
  "anchor": "custom"
1337
1353
  },
1354
+ {
1355
+ "heading": "The swipe back with a pop of your own",
1356
+ "anchor": "custom-swipe"
1357
+ },
1338
1358
  {
1339
1359
  "heading": "The top bar",
1340
1360
  "anchor": "custom-bar"
1341
1361
  },
1362
+ {
1363
+ "heading": "When the user reduces motion",
1364
+ "anchor": "reduce-motion"
1365
+ },
1342
1366
  {
1343
1367
  "heading": "Which to pick",
1344
1368
  "anchor": "which"
@@ -1418,6 +1442,10 @@
1418
1442
  "heading": "Typed commands",
1419
1443
  "anchor": "typed-commands"
1420
1444
  },
1445
+ {
1446
+ "heading": "Fast Refresh",
1447
+ "anchor": "fast-refresh"
1448
+ },
1421
1449
  {
1422
1450
  "heading": "UIKit and Android views",
1423
1451
  "anchor": "imperative"
@@ -85,6 +85,8 @@ export const MapScreen = defineScreen({
85
85
  | `'edge'` | From the edge only: a swipe on the content stays with the content. |
86
86
  | `false` | Off. The back button still works. |
87
87
 
88
+ (iOS) The swipe slides the screen out the system's way, also for a screen that leaves with a move of its own. `animations.pop.interactive` plays that move under the finger instead: [the swipe back with a pop of your own](../architecture/transitions#custom-swipe).
89
+
88
90
  (Android) The swipe is the system's back, from either edge, and `popGesture` doesn't apply to it. On Android 14+ it shows the pop before the user lets go: both screens shrink, the screen under comes in behind a dim, and the screen on top follows the finger. The user can stop halfway and stay, as on iOS. On Android 13 and older the screen pops when the swipe ends. When the preview shows and when it doesn't: [the preview of a pop](#pop-preview).
89
91
 
90
92
  ## The system back of Android
@@ -115,7 +117,7 @@ defineScreen({
115
117
  });
116
118
  ```
117
119
 
118
- iOS ignores it: the swipe back there is UIKit's.
120
+ iOS ignores it: the swipe back there is UIKit's, or the pop's own move under the finger with `animations.pop.interactive`.
119
121
 
120
122
  ### The preview of a sheet closing
121
123
 
@@ -148,7 +148,7 @@ useNavigationButton('sort-name', () => setSort('name'));
148
148
  | Platform | The menu |
149
149
  | --- | --- |
150
150
  | (iOS) | The system menu, with SF Symbols, submenus and a check on the picked item. |
151
- | (Android) | The Material popup menu under the button, with the same items, without the `title` of the menu. |
151
+ | (Android) | The Material popup menu under the button, with the same items, without the `title` of the menu. `topBar.menuBackgroundColor` and `topBar.menuTextColor` color it and "⋮"; without them it takes the bar's color and the title's color, in light and dark mode. |
152
152
 
153
153
  ## The overflow menu
154
154
 
@@ -75,6 +75,8 @@ A round button with a pencil floats at the bottom right; a press opens Compose.
75
75
  | `backgroundColor`, `iconColor` | Its colors. Without them, the colors of the app's Material 3 theme. |
76
76
  | `visible: false` | Hides it. |
77
77
 
78
+ The FAB belongs to its screen. A push brings the new screen's FAB in with it and takes the old one out with the screen it covers; a pop does the same the other way, and the back gesture shows the FAB of the screen under the top with that screen. Two screens with the same `id` share one button: it stays in place while the screens move.
79
+
78
80
  ### Actions
79
81
 
80
82
  A FAB with `actions` opens them above it on a press, each a small FAB with its own press:
@@ -129,7 +129,7 @@ In an app as the React Native template makes it, the window shows **one React vi
129
129
  | `MainApplication.kt` | Extends `NavigationApplication`; keeps the template's `loadReactNative(this)` in `onCreate`, or adds it. | It keeps one React host for all screens, made after `loadReactNative(this)` loaded React Native. Native screens made by `codegen` are registered there, before JS opens them. |
130
130
  | `MainActivity.kt` | Extends `NavigationActivity`. | The activity holds the containers of the navigation instead of one React view. It gets the back press, edge-to-edge and configuration changes first. |
131
131
  | `AndroidManifest.xml` | Adds `android:enableOnBackInvokedCallback="true"` to `<application>`. | On the first screen the system plays its own animation back to the home screen as the user swipes back (predictive back). Android 16 turns it on for apps that target API 36; an app that targets less opts in here. A value the app set itself stays. See [Back to the home screen](../screen-ui/back-and-gestures#system-animation). |
132
- | `styles.xml` | `AppTheme` inherits `Theme.ApexNavigation`. | The bars, tabs and search are Material 3 views and take their colors from the app's theme. This one is Material 3 in neutral grays, light and dark; change any color in the same file. A theme of the app's own stays as it is. See [Android's colors](../architecture/theme-and-context#android). |
132
+ | `styles.xml` | `AppTheme` inherits `Theme.ApexNavigation`. | The bars, tabs and search are Material 3 views and take their colors from the app's theme. This one is Material 3 in neutral grays, light and dark; change any color in the same file. A theme of the app's own stays as it is. See [Android's theme](../architecture/theme-and-context#android). |
133
133
 
134
134
  The main activity keeps the template's `android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"`. Without `orientation`, `screenSize`, `smallestScreenSize`, `screenLayout` or `uiMode` Android recreates the activity on a rotation, a resize, a fold or a light/dark switch, and every screen goes with it; `doctor` warns about it under Config changes.
135
135
 
@@ -150,8 +150,23 @@ These files are the same on both platforms.
150
150
  | `index.js` | Starts the app through the navigation. | `AppRegistry` of the package starts `createNavigation` instead of mounting one root component. |
151
151
  | `Navigation.ts` | New: shows `App` as the first screen. | The app keeps working as it was, now as the first screen of a stack. |
152
152
  | `App.tsx` | Takes out the template's ``. | React Native's `StatusBar` logs an error on iOS once the screens set the status bar. |
153
+ | `metro.config.js` | Wraps the exported config in `withApexNavigation`. | Fast Refresh: an edit of a screen updates the running app in place, with its state, instead of reloading it. See [Fast Refresh](#fast-refresh). |
153
154
  | `navigation-env.d.ts` | New: ties the commands to the screens of `Navigation.ts`. | The commands come from the package and can't see the app's screens. This declaration links them, so names and props are checked. See [Typed commands](#typed-commands). |
154
155
 
156
+ ### Fast Refresh
157
+
158
+ `setup` wraps your `metro.config.js` in `withApexNavigation`; in a new app `init` has done it. By hand:
159
+
160
+ ```js
161
+ const { withApexNavigation } = require('@apexnative/navigation/metro');
162
+
163
+ module.exports = withApexNavigation(mergeConfig(getDefaultConfig(__dirname), config));
164
+ ```
165
+
166
+ It does two things. React Native 0.87's transformer doesn't apply React Refresh's Babel plugin (Metro no longer passes it `hot`), and without the plugin a component that its module doesn't export loses its state at every edit. And it makes the module that imports `createNavigation` accept hot updates, so an edit of the `screens` map or of the app's config updates the running app without a reload: the screens, the default options (when `theme` or `options` changed), the roots, the listeners and the link routes. What only the start reads isn't run again: the first root, `willMount`, the splash and the context. The app's module itself runs again, as a component's module does in Fast Refresh: code at its top level (an SDK's `init`, a listener added there) runs again too, so keep such code in a module of its own that doesn't change, or make it safe to run twice. The same holds for a module the app's module imports that isn't a component, such as a store: its edit runs it and the app's module again instead of reloading the app, so a store made at the top of that module is made anew while the screens that didn't run again keep the old one. Reload the app (R) after such an edit.
167
+
168
+ `doctor` warns under Fast Refresh when the Metro config doesn't use it. What to write in a screen file so that its edits update in place: [Fast Refresh](../fundamentals/screens-and-props#fast-refresh).
169
+
155
170
  ### Typed commands
156
171
 
157
172
  `navigation.push('Details', { props })` comes from the package, and the package doesn't know the screens of your app. One declaration links them: `setup` and `init` write it in `navigation-env.d.ts` at the root of the app.