Implementing the Details Screen in TV Apps
🎬
The details screen is probably the last screen your user will see before watchin the movie in any content-focused TV app. Because of this it’s important this screen provides users with clear information about a selected content and includes the primary call-to-action (like “Watch Now”).
Component Structure
🏗️
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { StyleSheet, View, Image, Pressable, Text } from 'react-native';
import { SpatialNavigationFocusableView, SpatialNavigationRoot } from 'react-tv-space-navigation';
import { scaledPixels } from '@/hooks/useScale';
export default function DetailsScreen() {
const { title, description, headerImage } = useLocalSearchParams();
const styles = useDetailsStyles();
const router = useRouter();
const isFocused = useIsFocused();
return (
<SpatialNavigationRoot isActive={isFocused}>
<Stack.Screen options={{ headerShown: false }} />
<View style={styles.container}>
<Image source={headerImage} style={styles.backgroundImage} />
<View style={styles.contentContainer}>
<View style={styles.topContent}>
{/* Title and Description */}
</View>
<View style={styles.bottomContent}>
{/* Crew Information and Watch Button */}
</View>
</View>
</View>
</SpatialNavigationRoot>
);
}
Key Features:
- Uses
useLocalSearchParams
for screen data - Implements
SpatialNavigationRoot
for focus management - Organizes content in top and bottom sections
Implementation Steps
1. Background Image Setup
🖼️
<Image source={headerImage} style={styles.backgroundImage} />
// Styling
backgroundImage: {
position: 'absolute',
width: '100%',
height: '100%',
opacity: 0.3,
},
The background image uses low opacity to ensure content readability.
2. Title and Description Layout
📝
<View style={styles.topContent}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.description}>{description}</Text>
</View>
// Styling
title: {
fontSize: scaledPixels(48),
fontWeight: 'bold',
color: '#fff',
marginBottom: scaledPixels(20),
textShadowColor: 'rgba(0, 0, 0, 0.75)',
textShadowOffset: { width: -1, height: 1 },
textShadowRadius: 10,
},
description: {
fontSize: scaledPixels(24),
color: '#fff',
marginBottom: scaledPixels(20),
width: '60%',
lineHeight: scaledPixels(32),
},
Note the use of scaledPixels
for responsive text sizing.
3. Crew Information Section
👥
<View style={styles.crewContainer}>
<View style={styles.crewMember}>
<Text style={styles.crewRole}>Director</Text>
<Text style={styles.crewName}>Chris Traganos</Text>
</View>
{/* Additional crew members */}
</View>
// Styling
crewContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
marginBottom: scaledPixels(30),
},
crewMember: {
marginRight: scaledPixels(40),
marginBottom: scaledPixels(15),
},
Creates a horizontal layout for crew information.
4. Watch Button Implementation
▶️
<SpatialNavigationFocusableView onSelect={() => {router.push('/player')}}>
{({ isFocused }) => (
<Pressable
style={[styles.watchButton, isFocused && styles.watchButtonFocused]}
>
<Text style={isFocused ? styles.watchButtonTextFocused : styles.watchButtonText}>
Watch now
</Text>
</Pressable>
)}
</SpatialNavigationFocusableView>
Implements focus states for remote control navigation.
Check the Focus Management
🎯
The detail screen follow our specific focus management:
- Wrapping screen in
SpatialNavigationRoot
- Using
SpatialNavigationFocusableView
for interactive elements
Implementation Exercise
💻
Try this exercise to enhance the details screen:
Add a focusable back button
You can use what you learned up to now