Merhaba,
Bu yazıda React Native ve Movie API ile Film Uygulaması yapımından bahsedeceğim. Uygulamada filmlerin listelendiği bir ana ekranımız ve film detay ekranımız olacak.
İlk olarak bir React Native projesi oluşturuyoruz:
react-native init MovieApp
Uygulamada iki adet ekranımız olacak ve bunlar arasında geçiş yapacağız. Bu yüzden React Navigation kullanacağız. Bununla ilgili gerekli paketleri kuruyoruz:
npm i @react-navigation/native @react-navigation/stack react-native-gesture-handler react-native-screens react-native-safe-area-context
Not: Eğer bu paketlerin kurulumu sırasında @babel/preset-env hatası alırsanız öncelikle şu kurulumu yapmanız gerekiyor:
npm i --save-dev @babel/preset-env
Şimdi projemizin iskeletini oluşturalım. Root dizininde components ve screens klasörleri oluşturuyoruz. components içerisine Header.js, Slider.js ve Footer.js dosyaları; screens içerisine ise HomeScreen.js ve DetailScreen.js dosyaları oluşturuyoruz. Yine root dizininde Navigation.js dosyası oluşturup şu şekilde bir ayarlama yapıyoruz:
Navigation.js:
import React from 'react'
import { createStackNavigator } from '@react-navigation/stack'
import { NavigationContainer } from '@react-navigation/native'
import HomeScreen from './screens/HomeScreen'
import DetailScreen from './screens/DetailScreen'
const Stack = createStackNavigator()
const screenOptions = {
headerShown: false
}
const Navigation = () => (
<NavigationContainer>
<Stack.Navigator
initialRouteName="HomeScreen"
screenOptions={screenOptions}
>
<Stack.Screen name="HomeScreen" component={HomeScreen} />
<Stack.Screen name="DetailScreen" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
)
export default Navigation
- Stack Navigator oluşturduk ve üzerinde bulunacak ekranları belirttik.
- Başlangıçta HomeScreen'in açılacağını belirttik.
App.js dosyasını açarak şu şekilde bir ayarlama yapıyoruz:
import React from 'react'
import Navigation from './Navigation'
const App = () => {
return <Navigation />
}
export default App
Artık projemizin iskeleti hazır. Şimdi componentleri ve ekranları tasarlamaya, sonrasında da Movie API ile etkileşime sokmaya geçebiliriz.
Header.js:
import { View, Text, StyleSheet } from 'react-native'
import React from 'react'
const Header = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Movie App</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
height: 100,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#34495e'
},
text: {
fontSize: 30,
color: '#ffffff'
}
})
export default Header
Footer.js:
import { View, Text, StyleSheet } from 'react-native'
import React from 'react'
const Footer = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Movie App @ 2022</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
height:50,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#34495e'
},
text: {
fontSize: 16,
color: '#ffffff'
}
})
export default Footer
Slider.js:
import { View, Text, FlatList, Image, StyleSheet, TouchableOpacity } from 'react-native'
import React from 'react'
const Slider = ({ title, data, navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{ title }</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={data}
renderItem={({ item, index }) => (
<TouchableOpacity
style={styles.itemContainer}
onPress={() => navigation.navigate('DetailScreen', {
id: item.id
})}
>
<View style={styles.itemTopInfos}>
<Text style={styles.itemRate}>{ item.vote_average }</Text>
<Text style={styles.itemYear}>{ item.release_date.substr(0, 4) }</Text>
</View>
<Image
style={styles.itemImage}
source={{ uri: `https://www.themoviedb.org/t/p/w300_and_h450_bestv2${item.poster_path}` }}
/>
<Text style={styles.itemTitle}>{ item.title }</Text>
</TouchableOpacity>
)}
keyExtractor={item => item.id}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
margin: 10
},
title: {
fontSize: 20,
color: '#000000',
marginBottom: 10
},
itemContainer: {
marginRight: 10,
width: 150
},
itemTopInfos: {
flexDirection: 'row',
justifyContent: 'space-between'
},
itemRate: {
backgroundColor: '#27ae60',
color: '#ffffff',
padding: 3,
borderTopLeftRadius: 3,
borderTopRightRadius: 3
},
itemYear: {
backgroundColor: '#34495e',
color: '#ffffff',
padding: 3,
borderTopLeftRadius: 3,
borderTopRightRadius: 3
},
itemImage: {
height: 225
},
itemTitle: {
fontWeight: 'bold',
color: '#000000'
}
})
export default Slider
- title, data ve navigation verilerini props olarak aldık. Çünkü bu component'i HomeScreen'de hem popüler hem de trend filmler için kullanacağız.
- Verileri yatay şekilde bir FlatList üzerinde göstereceğiz.
- Her bir filme tıklandığında navigation ile detay ekranını açtıracağız. Filmin id'sini de detay ekranına parametre olarak göndereceğiz.
Tabi unutmadan projemizin root dizininde bir env.js dosyası açıp API bilgilerini yazıyoruz:
export const API_URL = "https://api.themoviedb.org/3"
export const API_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI2NWNkMjAwZDFkNWMyOTE1NGIwNTk0YjZkZTllYTA3MSIsInN1YiI6IjVhYTdkYjcyOTI1MTQxNWUzOTAxZGUyMiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.n4H4Ag0mQxg-530d1wFbfUkd_OIiPuUP59OwCDQpH6A"
HomeScreen.js:
import { View, ScrollView, StyleSheet, ActivityIndicator } from 'react-native'
import React, { useState, useEffect } from 'react'
import Header from '../components/Header'
import Slider from '../components/Slider'
import Footer from '../components/Footer'
import { API_URL, API_TOKEN } from "../env"
const HomeScreen = ({ navigation }) => {
const [loadingPopularMovies, setLoadingPopularMovies] = useState(true)
const [loadingTrendMovies, setLoadingTrendMovies] = useState(true)
const [popularMoviesData, setPopularMoviesData] = useState([])
const [trendMoviesData, setTrendMoviesData] = useState([])
const getPopularMovies = async () => {
try {
const response = await fetch(API_URL + '/movie/popular', {
headers: {
'Authorization': 'Bearer ' + API_TOKEN
}
})
const json = await response.json()
setPopularMoviesData(json.results)
} catch (error) {
console.error(error)
} finally {
setLoadingPopularMovies(false)
}
}
const getTrendMovies = async () => {
try {
const response = await fetch(API_URL + '/movie/top_rated', {
headers: {
'Authorization': 'Bearer ' + API_TOKEN
}
})
const json = await response.json()
setTrendMoviesData(json.results)
} catch (error) {
console.error(error)
} finally {
setLoadingTrendMovies(false)
}
}
useEffect(() => {
getPopularMovies()
getTrendMovies()
}, [])
return (
<View style={styles.container}>
<Header />
<ScrollView>
{loadingPopularMovies ? <ActivityIndicator/> : (
<Slider title="Popular Movies" data={popularMoviesData} navigation={navigation} />
)}
{loadingTrendMovies ? <ActivityIndicator/> : (
<Slider title="Trend Movies" data={trendMoviesData} navigation={navigation} />
)}
</ScrollView>
<Footer />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1
}
})
export default HomeScreen
- useState kullanarak 4 tane değişken oluşturduk. loadingPopularMovies ve loadingTrendMovies değişkenleri ile popüler ve trend filmler API'dan gelene kadar ekranda loading işareti göstereceğiz. popularMoviesData ve trendMoviesData değişkenlerinde ise API'dan aldığımız film verilerini tutacağız.
- getPopularMovies ve getTrendMovies fonksiyonlarını tanımlayarak API'a çağrı yaptık. Bu çağrılar sırasında header kısmında API_TOKEN'ı da gönderdik. Gelen verilerin içindeki results dizisini set fonksiyonlarıyla state'de bulunan değişkenlerimize aktardık.
- useEffect ile ekran render edildikten sonra (componentDidMount gibi) getPopularMovies ve getTrendMovies fonksiyonlarının çağrılmasını sağladık.
- Slider component'ine title, data ve navigation verilerini gönderdik.
import { View, Text, ScrollView, ActivityIndicator, Image, FlatList, StyleSheet } from 'react-native'
import React, { useState, useEffect } from 'react'
import Header from '../components/Header'
import Footer from '../components/Footer'
import { API_URL, API_TOKEN } from '../env'
const DetailScreen = ({ route }) => {
const [loadingMovieDetail, setLoadingMovieDetail] = useState(true)
const [loadingCast, setLoadingCast] = useState(true)
const [movieDetailData, setMovieDetailData] = useState({})
const [castData, setCastData] = useState([])
const { id } = route.params
const getMovieDetail = async () => {
try {
const response = await fetch(API_URL + '/movie/' + id, {
headers: {
'Authorization': 'Bearer ' + API_TOKEN
}
})
const json = await response.json()
setMovieDetailData(json)
} catch (error) {
console.error(error)
} finally {
setLoadingMovieDetail(false)
}
}
const getCast = async () => {
try {
const response = await fetch(API_URL + '/movie/' + id + '/credits', {
headers: {
'Authorization': 'Bearer ' + API_TOKEN
}
})
const json = await response.json()
setCastData(json.cast)
} catch (error) {
console.error(error)
} finally {
setLoadingCast(false)
}
}
useEffect(() => {
getMovieDetail()
getCast()
}, [])
return (
<View style={styles.container}>
<Header />
{loadingMovieDetail && loadingCast ? <ActivityIndicator/> : (
<ScrollView style={styles.scrollContainer}>
<View style={styles.topInfos}>
<Image
style={styles.image}
source={{ uri: `https://www.themoviedb.org/t/p/w300_and_h450_bestv2${movieDetailData.poster_path}` }}
/>
<View style={styles.rightInfos}>
<Text style={styles.title}>{ movieDetailData.title }</Text>
<Text style={styles.rate}>{ movieDetailData.vote_average }</Text>
<Text style={styles.year}>{ movieDetailData.release_date.substr(0, 4) }</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
style={styles.genreList}
data={movieDetailData.genres}
renderItem={({ item, index }) => (
<Text style={styles.genreItem}>{ item.name }</Text>
)}
keyExtractor={item => item.id}
/>
</View>
</View>
<Text style={styles.description}>{ movieDetailData.overview }</Text>
<Text style={styles.castTitle}>Cast</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
style={styles.castList}
data={castData}
renderItem={({ item, index }) => (
<View style={styles.castItem}>
<Image
style={styles.castItemImage}
source={{ uri: item.profile_path !== null ? `https://www.themoviedb.org/t/p/w300_and_h450_bestv2${item.profile_path}` : `https://via.placeholder.com/100x150` }}
/>
<Text style={styles.castItemName}>{ item.name }</Text>
<Text style={styles.castItemTitle}>{ item.character }</Text>
</View>
)}
keyExtractor={item => item.id}
/>
</ScrollView>
)}
<Footer />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1
},
scrollContainer: {
margin: 10
},
topInfos: {
flexDirection: 'row'
},
image: {
height: 225,
width: 150
},
rightInfos: {
flex: 1,
marginLeft: 10
},
title: {
fontWeight: 'bold',
fontSize: 16,
color: '#000000'
},
rate: {
backgroundColor: '#27ae60',
color: '#ffffff',
padding: 3,
borderTopLeftRadius: 3,
borderTopRightRadius: 3,
alignSelf: 'flex-start',
marginTop: 10,
marginBottom: 10
},
year: {
backgroundColor: '#34495e',
color: '#ffffff',
padding: 3,
borderTopLeftRadius: 3,
borderTopRightRadius: 3,
alignSelf: 'flex-start',
marginBottom: 10
},
genreList: {
flexShrink: 1
},
genreItem: {
backgroundColor: '#34495e',
color: '#ffffff',
padding: 3,
borderRadius: 3,
alignSelf: 'flex-start',
marginRight: 5,
fontSize: 12
},
description: {
color: '#000000',
marginTop: 10
},
castTitle: {
marginTop: 10,
fontWeight: 'bold',
color: '#000000',
fontSize: 15
},
castList: {
marginTop: 10
},
castItem: {
width: 100
},
castItemImage: {
height: 120,
width: 80
},
castItemName: {
color: '#000000',
fontSize: 13
},
castItemTitle: {
fontSize: 12
}
})
export default DetailScreen
- Ekrana gönderilen id parametresini route props'u üzerinden aldık.
- useState kullanarak 4 tane değişken oluşturduk. loadingMovieDetail ve loadingCast değişkenleri ile film detayı ve oyuncu kadrosu API'dan gelene kadar ekranda loading işareti göstereceğiz. movieDetailData ve castData değişkenlerinde ise API'dan aldığımız verileri tutacağız.
- getMovieDetail ve getCast fonksiyonlarını tanımlayarak API'a çağrı yaptık. Bu çağrılar sırasında header kısmında API_TOKEN'ı da gönderdik. Gelen verilerin içerisinden ihtiyacımız olanları set fonksiyonlarıyla state'de bulunan değişkenlerimize aktardık.
- useEffect ile ekran render edildikten sonra (componentDidMount gibi) getMovieDetail ve getCast fonksiyonlarının çağrılmasını sağladık.
Artık uygulamamızı test edebiliriz:
react-native run-android
Uygulamamızın ekran görüntüleri şu şekilde olacaktır:


Projenin kaynak kodlarına buradan ulaşabilirsiniz.
Umarım yararlı olmuştur.
İyi çalışmalar.
Yorumlar Henüz yorum yapılmamış
Yeni Yorum