react-native-navigation
Version:
React Native Navigation - truly native navigation for iOS and Android
459 lines (373 loc) • 16.5 kB
Markdown
# Android Native Architecture
The Android implementation provides native navigation using Views and ViewGroups (not Fragments), coordinated through a TurboModule that receives commands from JavaScript.
## App Integration
### NavigationApplication
**File**: `NavigationApplication.java`
Abstract Application class that user's MainApplication must extend:
```kotlin
// MainApplication.kt - User extends NavigationApplication
class MainApplication : NavigationApplication() {
override val reactNativeHost: ReactNativeHost
get() = NavigationReactNativeHost(this) // Must use NavigationReactNativeHost
}
```
**What NavigationApplication does:**
- Initializes SoLoader
- Provides `ReactGateway` singleton for React lifecycle
- Offers `registerExternalComponent()` for native screens
### NavigationActivity
**File**: `NavigationActivity.java`
Base Activity that user's MainActivity must extend:
```kotlin
// MainActivity.kt - User extends NavigationActivity
class MainActivity : NavigationActivity() {
// No getMainComponentName() needed - navigation handles root
}
```
**What NavigationActivity does:**
- Creates `Navigator` with root, modal, and overlay layouts
- Manages back press via `OnBackPressedCallback`
- Coordinates React lifecycle with activity lifecycle
### NavigationReactNativeHost
**File**: `react/NavigationReactNativeHost.kt`
Custom ReactNativeHost that integrates with navigation:
- Used instead of `DefaultReactNativeHost`
- Provides proper module registration for navigation
### Autolink Script (`npx rnn-link`)
The `autolink/postlink/postLinkAndroid.js` script automates setup:
| Linker | What it does |
|--------|--------------|
| `ApplicationLinker` | Changes MainApplication to extend `NavigationApplication`, uses `NavigationReactNativeHost`, removes SoLoader.init() |
| `ActivityLinker` | Changes MainActivity to extend `NavigationActivity`, removes `getMainComponentName()` |
| `GradleLinker` | Updates build.gradle if needed |
**ApplicationLinker transformations:**
- `class MainApplication : Application(), ReactApplication` → `class MainApplication : NavigationApplication()`
- `DefaultReactNativeHost(this)` → `NavigationReactNativeHost(this)`
- Removes `SoLoader.init()` (NavigationApplication handles it)
- Removes new architecture entry point load block
**ActivityLinker transformations:**
- `class MainActivity : ReactActivity()` → `class MainActivity : NavigationActivity()`
- Removes `getMainComponentName()` override
- Removes `createReactActivityDelegate()` override
## Directory Structure
```
android/src/main/java/com/reactnativenavigation/
├── NavigationActivity.java # Main Activity entry point
├── NavigationApplication.java # Application class
├── NavigationPackage.kt # React package registration
├── react/ # React bridge layer
│ ├── NavigationTurboModule.kt # TurboModule implementation
│ ├── ReactGateway.java # React lifecycle management
│ └── events/
│ └── EventEmitter.java # Event emission to JS
├── options/ # Options parsing and factories
│ ├── LayoutFactory.java # Creates ViewControllers from layout nodes
│ └── Options.java # Options model
├── viewcontrollers/ # ViewController hierarchy
│ ├── ViewController.java # Base class
│ ├── ChildController.java # With presenter support
│ ├── ParentController.java # Container controllers
│ ├── stack/ # Stack navigation
│ ├── bottomtabs/ # Bottom tabs
│ ├── toptabs/ # Top tabs (lowercase)
│ ├── sidemenu/ # Side menu/drawer
│ ├── modal/ # Modal management
│ └── externalcomponent/ # Native component support
├── views/ # UI View implementations
├── utils/ # Utilities
└── hierarchy/ # Root layout management
```
## Bridge Architecture
```
┌────────────────────────────────────────────────────────────┐
│ JavaScript (TurboModule) │
└─────────────────────────┬──────────────────────────────────┘
│
┌─────────────────────────▼──────────────────────────────────┐
│ NavigationTurboModule.kt │
│ - setRoot, push, pop, showModal, dismissModal, etc. │
└─────────────────────────┬──────────────────────────────────┘
│
┌─────────────────────────▼──────────────────────────────────┐
│ Navigator (Root ViewController) │
│ - Manages root, modals, overlays │
│ - Coordinates LayoutFactory │
└──────┬──────────────┬──────────────┬──────────────┬────────┘
│ │ │ │
┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐ ┌─────▼──────┐
│ModalStack │ │OverlayMgr │ │LayoutFactory│ │EventEmitter│
└────────────┘ └────────────┘ └────────────┘ └────────────┘
```
## Entry Points
### NavigationApplication
Abstract Application class that apps extend:
- Manages `ReactGateway` singleton
- Initializes SoLoader
- Provides `registerExternalComponent()` for native screens
### NavigationActivity
Main Activity containing all navigation:
- Creates `Navigator` with three CoordinatorLayouts:
- `rootLayout` - Main navigation content
- `modalsLayout` - Modal stack
- `overlaysLayout` - Overlays (highest z-order)
- Handles back press via `OnBackPressedCallback`
- Coordinates activity lifecycle with React
### NavigationTurboModule
TurboModule implementation (Kotlin, ~300 lines):
```kotlin
@ReactMethod
fun setRoot(commandId: String, layout: ReadableMap, promise: Promise)
@ReactMethod
fun push(commandId: String, componentId: String, layout: ReadableMap, promise: Promise)
@ReactMethod
fun showModal(commandId: String, layout: ReadableMap, promise: Promise)
```
All commands execute on UI thread via `UiThread.post()`.
## ViewController Hierarchy
**Note**: Android implementation does NOT use Fragments - it's purely View-based.
```
ViewController (abstract)
│ - Base for all navigation controllers
│ - Manages view creation and lifecycle
│ - Handles options and back press
│
├── ChildController
│ │ - Adds Presenter support
│ │
│ ├── ComponentViewController
│ │ - Renders React components via ReactView
│ │
│ └── ExternalComponentViewController
│ - Wraps native Android views
│
└── ParentController (extends ChildController)
- Container for child controllers
│
├── StackController
│ - Push/pop navigation
│ - Uses IdStack<ViewController>
│
├── BottomTabsController
│ - UITabBarController equivalent
│ - Uses AHBottomNavigation
│
├── TopTabsController
│ - Horizontal ViewPager tabs
│
├── SideMenuController
│ - DrawerLayout-based drawer
│
└── Navigator
- Root controller
- Manages modals and overlays
```
## Core Classes
### ViewController (Base)
**File**: `viewcontrollers/viewcontroller/ViewController.java` (~500 lines)
Abstract base for all navigation controllers:
```java
public abstract class ViewController<T extends ViewGroup> {
protected abstract T createView(); // Subclasses create views
public void onViewWillAppear() { }
public void onViewDidAppear() { }
public void onViewDisappear() { }
public void mergeOptions(Options options) { }
public void applyOptions(Options options) { }
public boolean handleBack(CommandListener listener) { }
public void destroy() { }
}
```
### Navigator (Root)
**File**: `viewcontrollers/navigator/Navigator.java`
Root ViewController managing:
- Root content hierarchy
- Modal stack (`ModalStack`)
- Overlay manager (`OverlayManager`)
- Component lookup by ID
### LayoutFactory
**File**: `options/LayoutFactory.java`
Factory creating ViewControllers from LayoutNode. Owned by `NavigationTurboModule` and used to build the view controller hierarchy when commands are received from JavaScript.
```java
public ViewController create(LayoutNode node) {
switch (node.type) {
case Component: return createComponent(node);
case Stack: return createStack(node);
case BottomTabs: return createBottomTabs(node);
case SideMenuRoot: return createSideMenuRoot(node);
case SideMenuCenter: return createSideMenuContent(node);
case SideMenuLeft: return createSideMenuLeft(node);
case SideMenuRight: return createSideMenuRight(node);
case TopTabs: return createTopTabs(node);
// Note: SplitView is iOS-only, not implemented on Android
}
}
```
## Navigation Types
### Stack Navigation (StackController)
**Files**: `viewcontrollers/stack/`
- View: `StackLayout` (CoordinatorLayout with TopBar)
- Internal stack: `IdStack<ViewController<?>>`
- Components: `TopBarController`, `StackAnimator`, `StackPresenter`, `FabPresenter`
Operations:
- `push(viewController, listener)`
- `pop(options, listener)`
- `popTo(target, options, listener)`
- `popToRoot(options, listener)`
- `setRoot(children, listener)`
### Bottom Tabs (BottomTabsController)
**Files**: `viewcontrollers/bottomtabs/`
- View: `BottomTabsLayout` (CoordinatorLayout with BottomTabs)
- Uses `AHBottomNavigation` library
- Components: `BottomTabsPresenter`, `BottomTabPresenter`, `BottomTabsAnimator`
Attachment Modes:
- `TogetherAttacher` - Load all tabs immediately
- `OnSwitchToTabAttacher` - Lazy load on selection
- `AfterInitialTabAttacher` - Load initial + others in background
### Side Menu (SideMenuController)
**Files**: `viewcontrollers/sidemenu/`
- View: `SideMenuRoot` (DrawerLayout-based)
- Children: Center, Left, Right controllers
- `SideMenuPresenter` for styling
### Top Tabs (TopTabsController)
**Files**: `viewcontrollers/toptabs/`
- View: `TopTabsViewPager` (ViewPager-based)
- `TopTabsLayoutCreator` for view creation
### Modals (ModalStack)
**Files**: `viewcontrollers/modal/`
Not a ViewController - manages modal presentation:
- `ModalPresenter` - Show/dismiss with animations
- `ModalAnimator` (Kotlin) - Animation handling
- Stack of modals with lifecycle management
### Overlays (OverlayManager)
**File**: `viewcontrollers/overlay/OverlayManager.kt`
Registry of active overlays:
- Show/dismiss by component ID
- Configuration change handling
- Host pause/resume lifecycle
## React Component Rendering
### ComponentViewController
Manages React component lifecycle:
- Creates `ComponentLayout` view
- Initializes `ReactView` with ReactSurface
- Emits lifecycle events to JS
- Handles scroll events and status bar
### ReactView
**File**: `react/ReactView.java`
Extends FrameLayout, wraps `ReactSurface`:
- `start()` - Attach surface to React instance
- `destroy()` - Clean up React surface
- `sendComponentWillStart()` - Emit event to JS
- `isReady()` - Check surface attachment
## Event Emission
### EventEmitter
**File**: `react/events/EventEmitter.java`
Events sent to JavaScript:
| Event | Description |
|-------|-------------|
| `RNN.AppLaunched` | App initialization complete |
| `RNN.ComponentDidAppear` | Component visible |
| `RNN.ComponentWillAppear` | About to become visible |
| `RNN.ComponentDidDisappear` | Component hidden |
| `RNN.NavigationButtonPressed` | TopBar button tapped |
| `RNN.BottomTabSelected` | Tab selection changed |
| `RNN.BottomTabPressed` | Tab pressed (even if selected) |
| `RNN.ModalDismissed` | Modal was dismissed |
| `RNN.ScreenPopped` | Screen removed from stack |
| `RNN.CommandCompleted` | Navigation command finished |
## Options System
### Options Class
**File**: `options/Options.java`
```java
public class Options {
public TopBarOptions topBar;
public TopTabsOptions topTabs;
public BottomTabsOptions bottomTabsOptions;
public BottomTabOptions bottomTabOptions;
public OverlayOptions overlayOptions;
public FabOptions fabOptions;
public AnimationsOptions animations;
public SideMenuRootOptions sideMenuRootOptions;
public ModalOptions modal;
public StatusBarOptions statusBar;
public NavigationBarOptions navigationBar;
public LayoutOptions layout;
public HardwareBackButtonOptions hardwareBack;
}
```
### Options Flow
1. **Parse**: JSON from JS → Options object via JSONParser
2. **Merge**: Default → Screen → Component options
3. **Apply**: Presenter applies to views
## Presenter Pattern
Each ViewController has a Presenter:
| ViewController | Presenter |
|---------------|-----------|
| StackController | StackPresenter |
| BottomTabsController | BottomTabsPresenter + BottomTabPresenter |
| ComponentViewController | ComponentPresenter |
| SideMenuController | SideMenuPresenter |
| Navigator | RootPresenter |
### Presenter Base
**File**: `viewcontrollers/viewcontroller/Presenter.java`
Handles:
- Orientation changes
- Status bar styling
- Navigation bar styling
- Layout parameters
## Animation System
### Animators (Kotlin)
- `StackAnimator.kt` - Push, pop, setRoot animations
- `ModalAnimator.kt` - Modal show/dismiss
- `BottomTabsAnimator.kt` - Tab transitions
- `TopBarAppearanceAnimator.kt` - Top bar changes
### Element Transitions
Via `TransitionAnimatorCreator.kt`:
- Position, scale, rotation animations
- Color transitions
- Corner radius animations
- Shared element support
## Activity Lifecycle
```java
onCreate() // Create Navigator, initialize React
onPostCreate() // Set content layout
onResume() // React host resume, event emitter setup
onPause() // React host pause
onDestroy() // Clean up Navigator
onConfigurationChanged() // Handle orientation, locale
onNewIntent() // Process deep links
onActivityResult() // Handle permissions
```
## ViewController Lifecycle
```
View creation
↓
onViewWillAppear() // Register with registry
↓
onViewDidAppear() // Emit event to JS
↓
[visible]
↓
onViewWillDisappear()
↓
onViewDisappear() // Unregister from registry
↓
destroy() // Complete cleanup
```
## Key Files Reference
All paths relative to `android/src/main/java/com/reactnativenavigation/`:
| File | Path | Purpose |
|------|------|---------|
| NavigationActivity.java | `.` | Main Activity |
| NavigationApplication.java | `.` | Application class |
| NavigationTurboModule.kt | `react/` | JS bridge |
| Navigator.java | `viewcontrollers/navigator/` | Root controller |
| ViewController.java | `viewcontrollers/viewcontroller/` | Base class |
| StackController.java | `viewcontrollers/stack/` | Stack navigation |
| BottomTabsController.java | `viewcontrollers/bottomtabs/` | Tab navigation |
| LayoutFactory.java | `options/` | Controller factory |
| Options.java | `options/` | Options model |
| Presenter.java | `viewcontrollers/viewcontroller/` | Base presenter |
| EventEmitter.java | `react/events/` | JS events |
| ReactView.java | `react/` | React component wrapper |
| StackAnimator.kt | `viewcontrollers/stack/` | Stack animations |
| ModalStack.java | `viewcontrollers/modal/` | Modal management |