React Native ve Hooks ile Haber Uygulaması Yapımı – Bölüm 2
Önceki yazımızda React Native ile haber kategorilerinin listelendiği bir uygulama yapmıştık. Bu yazımızda haber listesini çekecek ekranı yapacağız ve kategori listesi ile bağlamak için React Navigation Library’i kullanacağız.
Öncelikle haber başlıklarının yer alacağı Headlines
bileşenini oluşturalım. Proje dizini içerisine gelip Headlines.js isimli dosyayı oluşturalım ve içerisini aşağıdaki gibi dolduralım:
import { SafeAreaView, Text } from ‘react-native’;
const Headlines = () => {
return (
<SafeAreaView>
<Text>Headlines</Text>
</SafeAreaView>
);
};
export default Headlines;
Başlangıçta App.js yerine bu ekranın açılması için index.js dosyasının içerisine gidelim ve Headlines
bileşenini import ederek, registerComponent()
fonksiyonuna bağlayalım.
import App from ‘./App’;
import Headlines from ‘./Headlines’;
import {name as appName} from ‘./app.json’;
AppRegistry.registerComponent(appName, () => Headlines);
index.js dosyasını kaydettikten sonra Simulator üzerinde Cmd + R
ile kodu ‘reload’ ettiğinizde Headlines.js dosyasının çalıştığını göreceksiniz:
Şimdi haber başlıklarını nasıl newsapi.org üzerinden nasıl alacağımıza bakalım.
News API ve Kullanımı
Haber listesinin alınması için News API açık kaynaklı proje geliştiriminde ücretsiz bir çözüm sunuyor. İçerisinde 30 binin üzerinde haber kaynağı barındırıyor. Kullanmak için ise sağ üst köşede Get API key
butonuna tıklayıp siteye kaydolarak bir API anahtarı alabilirsiniz.
Haber başlıklarının alınması için Top headlines endpoint’ini kullanabilirsiniz. Bu servis, ülke ve kategori bazında haber başlıklarını getirebiliyor. Örneğin Türkiye’de yer alan teknoloji kategorisine sahip haberlerin getirilmesi için aşağıdaki url yeterli olacaktır:
- Not 1:
category
paremetresibusiness
,entertainment
,general
,health
,science
,sports
,technology
değerleri alabilir. - Not 2: Burada
<API_KEY>
yerine News API üzerinden aldığınız kendi API Key’inizi yazmanız gerekiyor.
Şu an <Headlines>
bileşeni fonksiyonel bir bileşen olduğu için state kullanımı ve veri çekim işlemlerini yalnızca hook metodları ile yapabiliriz. Öncelikle useState
ve useEffect
metotlarını import edelim:
Devamında sunucudan dönen sonucu bileşenin state’inde barındıracak headlines objesini ve bunu atayacak setHeadlines metodunu oluşturalım:
setHeadlines
metodunu kullanabilmek için React’in lifecycle metodu olan componentDidMount()
‘u kullanmamız gerekiyor. Fakat bileşenimiz fonksiyonel olduğu için bunun yerine useEffect()
hook’u ile bu işi çözeceğiz. Örnek olması açısından serverResponse
adında bir özellik oluşturup, değerine Top Headlines
‘ı atayalım:
setHeadlines({ serverResponse: ‘Top Headlines’ }, []);
});
Son olarak return fonksiyonunun içeriğini aşağıdaki gibi headlines değişkenini basacak şekilde değiştirelim:
<SafeAreaView>
<Text>{JSON.stringify(headlines)}</Text>
</SafeAreaView>
);
Ekranda JSON metni olarak { serverResponse: 'Top Headlines' }
yazacak olan kodun son hali aşağıdaki gibi olacaktır:
import { SafeAreaView, Text } from ‘react–native’;
const Headlines = () => {
const [headlines, setHeadlines] = useState({});
useEffect(() => {
setHeadlines({ serverResponse: ‘Top Headlines’ });
}, []);
return (
<SafeAreaView>
<Text>{JSON.stringify(headlines)}</Text>
</SafeAreaView>
);
};
export default Headlines;
Şimdi haber başlıklarının getirileceği URL’i parçalı bir şekilde oluşturalım:
const API_KEY = ‘<API_KEY>’;
const url = `https://newsapi.org/v2/top-headlines?country=${country}&category=${category}&apiKey=${API_KEY}`;
Kod içerisinde bu url’e istek yapabilmek için JavaScript’te yer alan Fetch API‘ı kullanabiliriz. useEffect()
fonksiyonu içerisinde asenkron olarak verileri getirecek şekilde kodlayalım:
fetchData();
}, []);
async function fetchData() {
(await fetch(url))
.json()
.then(res => setHeadlines(res));
}
Kodun son hali aşağıdaki gibi olacaktır:
import { SafeAreaView, Text } from ‘react-native’;
const Headlines = () => {
const [headlines, setHeadlines] = useState({});
const category = ‘technology’;
const country = ‘tr’;
const API_KEY = ‘<API_KEY>’;
const url = `https://newsapi.org/v2/top-headlines?country=${country}&category=${category}&apiKey=${API_KEY}`;
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
(await fetch(url))
.json()
.then(res => setHeadlines(res))
}
return (
<SafeAreaView>
<Text>{JSON.stringify(headlines)}</Text>
</SafeAreaView>
);
};
export default Headlines;
JSON metnini ekrana basabildik. Şimdi içerisindeki elemanları ekrana basmaya geldi. Sunucudan dönen cevapta articles
isminde bir array bulunuyor. Bu array’in içerisinde ihtiyacımız olacak alanlar aşağıdaki gibidir:
- source.name: Haberin kaynağının url’ini bildirir. Örneğin Shiftdelete.net, Donanimhaber.com vb.
- title: Haberin başlığıdır.
- url: Haberin url’ini belirtir. Bunu daha sonra WebView’a basmak için kullanacağız.
- urlToImage: Haberin küçük görselidir. Bunu listeleme görseli olarak kullanacağız.
- publishedAt: Haberin yayım tarihini belirtir.
Öncelikle liste oluşturup haber başlığını(title) listeye basalım. Bunun için sadece return fonksiyonunu aşağıdaki gibi <FlatList>
ekleyip değiştirmek yeterlidir:
<SafeAreaView>
<FlatList
data={headlines.articles}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={(item) => item.title}
/>
</SafeAreaView>
);
Şimdi haber görseli ve diğer özellikleri de eklemek için renderItem()
‘ı bir metoda bağlayalım ve o metot üzerinden devam edelim:
return (
<View style={{ flex: 1, flexDirection: ‘row’, padding: 10, borderBottom: 1, borderBottomWidth: 1, borderBottomColor: ‘#eee’ }}>
<Image style={{ width: 100, height: 100 }} source={{ uri: item.urlToImage }} />
<View style={{ flex: 1, paddingLeft: 10 }}>
<Text style={{ flexWrap: ‘wrap’ }}>{item.title}</Text>
<View style={{ flex: 1, flexDirection: ‘row’, justifyContent: ‘space-between’, alignItems: ‘flex-end’ }}>
<Text>{item.source.name}</Text>
<Text>{item.publishedAt}</Text>
</View>
</View>
</View>);
}
return (
<SafeAreaView>
<FlatList
data={headlines.articles}
renderItem={renderItem}
keyExtractor={(item) => item.title}
/>
</SafeAreaView>
);
Bu tasarımdaki temel problem tarih alanının okunaklı bir halde olmamasıdır. Bunun için sosyal medya uygulamalarında olduğu gibi “x saat önce” şeklinde bir tarih gösterimi daha şık olacaktır. Bunun için proje dizininizde PrettyTime.js
isimli bir dosya oluşturup içerisine aşağıdaki kodları yapıştırın:
if (!time) return;
var now = new Date();
var releasedDate = new Date(time);
var seconds = ((now.getTime() – releasedDate) * .001) >> 0;
var minutes = seconds / 60;
var hours = minutes / 60;
var days = hours / 24;
var years = days / 365;
return templates.prefix + (
seconds < 45 && template(‘seconds’, seconds)
|| seconds < 90 && template(‘minute’, 1)
|| minutes < 45 && template(‘minutes’, minutes)
|| minutes < 90 && template(‘hour’, 1)
|| hours < 24 && template(‘hours’, hours)
|| hours < 42 && template(‘day’, 1)
|| days < 30 && template(‘days’, days)
|| days < 45 && template(‘month’, 1)
|| days < 365 && template(‘months’, days / 30)
|| years < 1.5 && template(‘year’, 1)
|| template(‘years’, years))
+ templates.suffix;
};
var templates = {
prefix: “”,
suffix: ” önce”,
seconds: “saniyeler”,
minute: “1 dakika”,
minutes: “%d dakika”,
hour: “1 saat”,
hours: “%d saat”,
day: “1 gün”,
days: “%d gün”,
month: “1 ay”,
months: “%d ay”,
year: “1 yıl”,
years: “%d yıl”
};
var template = function (t, n) {
return templates[t] && templates[t].replace(/%d/i, Math.abs(Math.round(n)));
};
export default prettyTime;
- Not: Burada tarih fonksiyonunu kendimiz yazdık. Daha geniş kapsamlı ve dil destekli bir kütüphane kullanmak isterseniz react-timeago kütüphanesinden yararlanabilirsiniz.
Şimdi Headlines.js dosyasına gelip prettyTime
fonksiyonunu import edelim ve item.publishedAt
özelliğini sarmalayacak şekilde kullanalım:
import prettyTime from ‘./PrettyTime’;
const Headlines = () => {
…
function renderItem({ item }) {
return (
…
<Text>{prettyTime(item.publishedAt)}</Text>
…
)
};
export default Headlines;
Bu hali daha iyi oldu. Fakat haberin başlığında da haber kaynağı yer alıyor. Bunu temizlemek için haber başlığını ' - '
karakteri bazında split edip, pop()
ettikten sonra tekrar oluşan diziyi birleştiren removeSource(title)
fonksiyonunu yazalım ve projede kullanalım:
function removeSource(title){
if(title == null || title.indexOf(‘ – ‘) < 0) return title;
var parts = title.split(‘ – ‘);
parts.pop();
return parts.join(‘ – ‘);
}
function renderItem({ item }) {
return (
…
<Text style={{ flexWrap: ‘wrap’ }}>{removeSource(item.title)}</Text>
);
}
…
Şimdi bunlara ek olarak bir de ikonlar ekleyerek daha da güzelleştirelim. Bunun için GitHub’da 11 bin yıldız almış react-native-vector-icons kütüphanesini kullanacağız. Öncelikle bu kütüphaneyi projemize yükleyelim:
Bu kütüphane native özellikler içerdiği için link etmemiz gerekiyor:
Daha sonra projenin tekrar derlenmesi için VSCode üzerinde projeyi debug etmeyi durduralım. Sol alttaki status bar kısmında yer alan React Native Packager (Metro Bundler)’ı da durduralım. Devamında tekrar debug butonuna basarak projeyi derleyip cihazda çalıştırabiliriz.
İkon eklemek için öncelikle eklemek istediğiniz ikonun ailesini ve adını bilmek gerekiyor. Bunun için https://oblador.github.io/react-native-vector-icons/ adresini ziyaret edebilirsiniz. Daha sonra ilgili ikon ailesini import etmek gerekiyor. Örneğin MaterialCommunityIcons
ailesi aşağıdaki şekilde import edilebilir:
Icon bileşeni en sade haliyle aşağıdaki gibi kullanılabilir:
Icon’un size
ve color
gibi alanları var. İsteğe göre o alanlar da set edilebilir. Haber kaynağı ve yayın tarihi metinlerinin sol tarafına ikon eklemek için, <Text>
ve <Icon>
bileşenlerini bir <View>
ile sarmalamamız gerekiyor:
<Icon name=“newspaper” size={15} style={{paddingRight: 5}}/>
<Text>{item.source.name}</Text>
</View>
<View style={{ flexDirection: ‘row’, alignItems:‘center’ }}>
<Icon name=“clock-outline” size={15} style={{paddingRight: 5}}/>
<Text>{prettyTime(item.publishedAt)}</Text>
</View>
Şimdi ise liste elemanına tıkladığımızda bir alert bastıralım. renderItem({ item })
‘dan return edilen bileşenin tamamını <TouchableHighlight>
ile sarmalayıp, onPress
fonksiyonunda alert fonksiyonunu çalıştırabiliriz:
return (
<TouchableHighlight onPress={() => { alert(item.title) }}>
…
</TouchableHighlight>);
}
Kodun son hali aşağıdaki gibi olacaktır:
import { Image, View, SafeAreaView, Text, FlatList, TouchableHighlight } from ‘react-native’;
import prettyTime from ‘./PrettyTime’;
import Icon from ‘react-native-vector-icons/MaterialCommunityIcons’;
const Headlines = () => {
const [headlines, setHeadlines] = useState({});
const category = ‘technology’;
const country = ‘tr’;
const API_KEY = ‘<API_KEY>’;
const url = `https://newsapi.org/v2/top-headlines?country=${country}&category=${category}&apiKey=${API_KEY}`;
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
(await fetch(url))
.json()
.then(res => setHeadlines(res))
}
function removeSource(title) {
if (title == null || title.indexOf(‘ – ‘) < 0) return title;
var parts = title.split(‘ – ‘);
parts.pop();
return parts.join(‘ – ‘);
}
function renderItem({ item }) {
return (
<TouchableHighlight onPress={() => { alert(item.title) }}>
<View style={{ flex: 1, flexDirection: ‘row’, padding: 10, borderBottom: 1, borderBottomWidth: 1, borderBottomColor: ‘#eee’ }}>
<Image style={{ width: 100, height: 100 }} source={{ uri: item.urlToImage }} />
<View style={{ flex: 1, paddingLeft: 10 }}>
<Text style={{ flexWrap: ‘wrap’ }}>{removeSource(item.title)}</Text>
<View style={{ flex: 1, flexDirection: ‘row’, justifyContent: ‘space-between’, alignItems: ‘flex-end’ }}>
<View style={{ flexDirection: ‘row’, alignItems: ‘center’ }}>
<Icon name=“newspaper” size={15} style={{ paddingRight: 5 }} />
<Text>{item.source.name}</Text>
</View>
<View style={{ flexDirection: ‘row’, alignItems: ‘center’ }}>
<Icon name=“clock-outline” size={15} style={{ paddingRight: 5 }} />
<Text>{prettyTime(item.publishedAt)}</Text>
</View>
</View>
</View>
</View>
</TouchableHighlight>);
}
return (
<SafeAreaView>
<FlatList
data={headlines.articles}
renderItem={renderItem}
keyExtractor={(item) => item.title}
/>
</SafeAreaView>
);
};
export default Headlines;
Artık 2 ekran da hazır olduğuna göre şimdi haber içeriğinin görüntüleneceği NewsWebView.js
‘i oluşturabiliriz.
WebView ile haber içeriğinin görüntülenmesi
WebView bileşeni React Native içerisinden çıkarılarak ayrıldığı için öncelikle WebView kütüphanesini indirmemiz ve link etmemiz gerekiyor:
react-native link react-native-webview
Artık haber içeriğinin görüntüleneceği NewsWebView
bileşenini oluşturabiliriz. Bunun için proje dizini içerisine gelip NewsWebView.js
isimli dosyayı oluşturalım ve içeriğini devnot.com’u görüntüleyecek şekilde değiştirelim:
import React from ‘react’;
import { SafeAreaView } from ‘react-native’;
import { WebView } from ‘react-native-webview’;
const NewsWebView = () => {
return (
<SafeAreaView style={{ flex: 1 }}>
<WebView source={{ uri: ‘http://devnot.com’ }} />
</SafeAreaView>
);
};
export default NewsWebView;
Uygulamanın direkt olarak bu sayfayı açması için index.js
dosyasının da için aşağıdaki gibi değiştirelim:
import {AppRegistry} from ‘react-native’;
import App from ‘./App’;
import Headlines from ‘./Headlines’;
import NewsWebView from ‘./NewsWebView’;
import {name as appName} from ‘./app.json’;
AppRegistry.registerComponent(appName, () => NewsWebView);
Bu kadar basit. Şimdi 3 ekranı birbiriyle bağlamak için react-navigation
kütüphanesini kullanabiliriz.
React Navigation Kütüphanesi Kullanımı
Öncelikle react-navigation
ve react-native-gesture-handler
kütüphanelerini projemize indirelim ve link edelim:
npm install –save react-navigation
npm install –save react-native-gesture-handler
react-native link react-native-gesture-handler
Daha sonra index.js
dosyasının içerisine gelelim ve aşağıdaki şekilde değiştirelim:
import { AppRegistry } from ‘react-native’;
import { createStackNavigator, createAppContainer } from ‘react-navigation’;
import { name as appName } from ‘./app.json’;
import App from ‘./App’;
import Headlines from ‘./Headlines’;
import NewsWebView from ‘./NewsWebView’;
const MainNavigator = createStackNavigator({
Home: { screen: App },
Headlines: { screen: Headlines },
NewsWebView: { screen: NewsWebView },
});
const MainContainer = createAppContainer(MainNavigator);
AppRegistry.registerComponent(appName, () => MainContainer);
Buradaki MainNavigator
, ekranlar için oluşturulan rotaların, aynı bir stack yapısındaki gibi yığın halinde üst üste binecek şekilde tutacak stackNavigator
tipindeki nesneyi oluşturur. Nesne içerisinde yer alacak ekranlar burada tanımlanabilir. Home
değişkeni, ilk açılacak ekranı belirler. Fakat adının Home olması zorunlu değildir. createStackNavigator
‘da listelenen ilk bileşen ne ise o başlangıç ekranını oluşturur. MainContainer
ise uygulamanın barınacağı state’i yönetir ve MainNavigator
‘ın bağlanmasını sağlar.
Uygulamayı çalıştığınızda haber kategorilerinin yer aldığı App
bileşenini göreceksiniz. Fakat üstten de bir miktar margin (boşluk) bulunuyor. İşte bu margin aslında kategoriler sayfasının NavBar
elemanıdır. İçerisinde sayfa başlığı ve üst menüleri barındırır. Şimdi App.js
sayfamızın NavBar
elamanına stil verelim:
import { … Platform, StatusBar } from ‘react-native’;
…
App.navigationOptions = ({ navigation }) => ({
title: ‘Haber Kategorileri’,
headerStyle: {
backgroundColor: ‘#2196F3′,
},
headerTintColor: ‘#FFF’,
headerTitleStyle: {
fontFamily: Platform.OS === ‘ios’ ? ‘Futura’ : ‘Roboto’,
},
});
StatusBar.setBarStyle(‘light-content’, true);
…
Sayfa başlığını Haber Kategorileri
olacak şekilde ayarlayıp, mavi üstüne beyaz yazı olması için gerekli renk ayarlamalarını yaptık. StatusBar’ın metin renginin de beyaz olması için StatusBar API
kullandık. Bunun haricinde başlığın yazı tipini de değiştirebiliriz. iOS ve Android’de halihazırda bulunan font listesi için https://github.com/react-native-training/react-native-fonts sayfasına bakabilirsiniz. iOS’te olan yazı tipi Android’de olmadığı için platform spesifik kod yazmamız gerekiyor. Bunu da react-native
‘den import ettiğimiz Platform API
ile yapabiliyoruz.
- Not: NavBar eklenildiğinden dolayı ekran artık StatusBar’a taşmadığı için bileşenin
<SafeAreaView></SafeAreaView>
içerisine yazılmasına gerek yoktur. Bu kısımları silebilirsiniz.
Kategoriye tıklandığında kategori adını Alert
ile ekrana basıyorduk. Bunun yerine kategoriyi diğer sayfaya taşıyacak kodu yazabiliriz:
…
const App = ({ navigation }) => {
…
renderItem = ({ item }) => {
return (
<TouchableHighlight onPress={() => navigation.navigate(‘Headlines’, { category: item.category })}>
…
Burada, App adlı fonksiyon bileşenine parametre olarak gelen navigation
nesnesini alabilmiş olduk. Sonra bu değişkeni onPress
metodunda navigation.navigate()
içerisinde kullandık. navigate()
fonksiyonu temelde 2 adet parametre alır:
- Gidilecek rota/bileşen adı. Bunu
index.js
‘te tanımlamıştık. - Bileşene geçirilecek nesne bilgisi. Burada kategori adına göre haberleri getireceğimiz için kategori adını alıyoruz.
App.js dosyasının son hali aşağıdaki gibi olmaldır:
import React from ‘react’;
import { Text, FlatList, ImageBackground, Dimensions, TouchableHighlight, Platform, StatusBar } from ‘react-native’;
const App = ({ navigation }) => {
const numColumns = 3;
const tileWidth = Dimensions.get(‘window’).width / numColumns;
const imageBaseUrl = “https://images.unsplash.com/photo-“;
const imageParameters = “?auto=format&fit=crop&w=375&q=80″;
const dataSource = [
{ category: 'Teknoloji', imageId: '1478358161113-b0e11994a36b' },
{ category: 'Spor', imageId: '1521412644187-c49fa049e84d' },
{ category: 'Sağlık', imageId: '1526256262350-7da7584cf5eb' },
{ category: 'Ekonomi', imageId: '1542222024-c39e2281f121' },
{ category: 'Eğitim', imageId: '1503676260728-1c00da094a0b' },
{ category: 'Müzik', imageId: '1511671782779-c97d3d27a1d4' },
{ category: 'Tiyatro', imageId: '1507924538820-ede94a04019d' },
{ category: 'Sinema', imageId: '1478720568477-152d9b164e26' },
{ category: 'Hava', imageId: '1530908295418-a12e326966ba' },
{ category: 'Seyahat', imageId: '1473625247510-8ceb1760943f' },
{ category: 'Astroloji', imageId: '1532968961962-8a0cb3a2d4f5' },
{ category: 'Otomobil', imageId: '1537041373298-55dbb337e651' },
{ category: 'Galeri', imageId: '1500051638674-ff996a0ec29e' },
{ category: 'Video', imageId: '1524253482453-3fed8d2fe12b' },
];
renderItem = ({ item }) => {
return (
<TouchableHighlight onPress={() => navigation.navigate(‘Headlines’, { category: item.category })}>
<ImageBackground source={{ uri: imageBaseUrl + item.imageId + imageParameters }}
style={{
width: tileWidth,
height: tileWidth,
justifyContent: ‘center’
}}>
<Text style={{
textAlign: ‘center’,
color: ‘#fff’,
fontSize: 15
}}>{item.category}</Text>
</ImageBackground>
</TouchableHighlight>
);
}
return (
<FlatList
data={dataSource}
renderItem={this.renderItem}
keyExtractor={(item) => item.category}
numColumns={numColumns}
/>
);
};
App.navigationOptions = ({ navigation }) => ({
title: ‘Haber Kategorileri’,
headerStyle: {
backgroundColor: ‘#2196F3′,
},
headerTintColor: ‘#FFF’,
headerTitleStyle: {
fontFamily: Platform.OS === ‘ios’ ? ‘Futura’ : ‘Roboto’,
},
});
StatusBar.setBarStyle(‘light-content’, true);
export default App;
Şimdi Headlines.js
dosyasına gelelim ve kategori adını navigation nesnesinden çeken kodu yazalım:
const Headlines = ({ navigation }) => {
const query = navigation.state.params && navigation.state.params.category;
…
Burada && operatörünü kullanmamızın amacı, kategori adı gelmediğinde uygulamanın patlamaması içindir. Ayrıca gelen kategori adının “Teknoloji”, “Spor”, “Sağlık” gibi Türkçe olduğu ve uygulamamızda bulunan kategorilerin News Api’de bulunmadığı için /top-headlines
endpoint’i işimizi görmeyecektir. Bunun yerine News Api’deki /everything
endpoint’ini kullanabiliriz. Bu endpoint’ten dönen JSON metni, /top-headlines’daki ile aynı yapıda olduğu için sadece URL yapısının değişikliği ile uyum sağlanabilir:
const query = navigation.state.params && navigation.state.params.category;
const language = ‘tr’;
const API_KEY = ‘<API_KEY>’;
const url = `https://newsapi.org/v2/everything?language=${language}&q=${query}&apiKey=${API_KEY}`;
Şimdi Headlines bileşeninin NavBar’ına da stil verebiliriz:
Headlines.navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params && navigation.state.params.category} Haberleri`
headerStyle: {
backgroundColor: ‘#2196F3′,
},
headerTintColor: ‘#FFF’,
headerTitleStyle: {
fontFamily: Platform.OS === ‘ios’ ? ‘Futura’ : ‘Roboto’,
},
});
StatusBar.setBarStyle(‘light-content’, true);
Sizin de göreceğiniz gibi buradaki kodun, App bileşenindeki NavBar tasarımındaki koddan tek farkı title
özelliğidir. Headlines.navigationOptions
içerisinde sadece title’ı bırakalım ve diğer her şeyi index.js
içerisine taşıyalım:
import { AppRegistry, StatusBar } from ‘react-native’;
import { createStackNavigator, createAppContainer } from ‘react-navigation’;
import { name as appName } from ‘./app.json’;
import App from ‘./App’;
import Headlines from ‘./Headlines’;
import NewsWebView from ‘./NewsWebView’;
const navigationConfig = {
initialRouteName: ‘Home’,
defaultNavigationOptions: {
title: ‘Haber Kategorileri’,
headerStyle: {
backgroundColor: ‘#2196F3′,
},
headerTintColor: ‘#FFF’,
headerTitleStyle: {
fontFamily: Platform.OS === ‘ios’ ? ‘Futura’ : ‘Roboto’,
},
},
}
StatusBar.setBarStyle(‘light-content’, true);
const MainNavigator = createStackNavigator({
Home: { screen: App },
Headlines: { screen: Headlines },
NewsWebView: { screen: NewsWebView },
}, navigationConfig);
const MainContainer = createAppContainer(MainNavigator);
AppRegistry.registerComponent(appName, () => MainContainer);
Burada ortak stilleri navigationConfig
değişkenine taşıdık ve MainNavigator
‘e bunu tanıttık. Artık tüm ekranlarda aynı NavBar
ekranı görünümü uygulanacaktır.
Şimdi habere tıklandığında ilgili haberin detayının açılması için Headlines
bileşeninde aşağıdaki değişikliği yapalım:
const Headlines = ({ navigation }) => {
…
<TouchableHighlight onPress={() => { navigation.navigate(‘NewsWebView’, { url: item.url, title: item.title })}}>
…
WebView’da haberin url’inin basılması ve NavBar’da da haber başlığının görüntülenmesi için geçirilecek nesneyi uygun şekilde düzenledik. Headlines bileşeninin son hali aşağıdaki gibi olacaktır:
import React, { useState, useEffect } from ‘react’;
import { Image, View, Text, FlatList, TouchableHighlight } from ‘react-native’;
import prettyTime from ‘./PrettyTime’;
import Icon from ‘react-native-vector-icons/MaterialCommunityIcons’;
const Headlines = ({ navigation }) => {
const [headlines, setHeadlines] = useState({});
const query = navigation.state.params && navigation.state.params.category;
const language = ‘tr’;
const API_KEY = ‘<API_KEY>’;
const url = `https://newsapi.org/v2/everything?language=${language}&q=${query}&apiKey=${API_KEY}`;
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
(await fetch(url))
.json()
.then(res => setHeadlines(res))
}
function renderItem({ item }) {
return (
<TouchableHighlight onPress={() => { navigation.navigate(‘NewsWebView’, { url: item.url, title: item.title }) }}>
<View style={{ flex: 1, flexDirection: ‘row’, padding: 10, borderBottom: 1, borderBottomWidth: 1, borderBottomColor: ‘#eee’ }}>
<Image style={{ width: 100, height: 100 }} source={{ uri: item.urlToImage }} />
<View style={{ flex: 1, paddingLeft: 10 }}>
<Text style={{ flexWrap: ‘wrap’ }}>{item.title}</Text>
<View style={{ flex: 1, flexDirection: ‘row’, justifyContent: ‘space-between’, alignItems: ‘flex-end’ }}>
<View style={{ flexDirection: ‘row’, alignItems: ‘center’ }}>
<Icon name=“newspaper” size={15} style={{ paddingRight: 5 }} />
<Text>{item.source.name}</Text>
</View>
<View style={{ flexDirection: ‘row’, alignItems: ‘center’ }}>
<Icon name=“clock-outline” size={15} style={{ paddingRight: 5 }} />
<Text>{prettyTime(item.publishedAt)}</Text>
</View>
</View>
</View>
</View>
</TouchableHighlight>);
}
return (
<FlatList
data={headlines && headlines.articles}
renderItem={renderItem}
keyExtractor={(item) => item.title} />
);
};
Headlines.navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params && navigation.state.params.category} Haberleri`
});
export default Headlines;
Not: Burada <API_KEY>
yerine NewsAPI’deki kendi Api key’inizi yazmayı unutmayınız.
Şimdi NewsWebView
içerisine gelelim ve aşağıdaki şekilde düzenleyelim:
import React from ‘react’;
import { WebView } from ‘react-native-webview’;
const NewsWebView = ({ navigation }) => {
const url = navigation.state.params && navigation.state.params.url;
return (
<WebView source={{ uri: url }} />
);
};
NewsWebView.navigationOptions = ({ navigation }) => ({
title: navigation.state.params && navigation.state.params.title
});
export default NewsWebView;
Sonuç olarak
Bu yazımızda React Navigation kütüphanesini, WebView kullanımını, platform spesifik kod yazmayı ve NavBar’a tema vermeyi öğrendik. Projenin GitHub üzerindeki haline buradan ulaşabilirsiniz. Eğer bu proje hakkında sorularınız veya görüşleriniz varsa yorum kısmından bize yazabilirsiniz. Bir sonraki yazımızda görüşmek üzere.