Flutter’da harici JSON API kullanımı
Selamlar, 24 Eylül akşamına DSC Edirne’nin yayınına, “Flutter’da harici API kullanımı” başlığı adı altında konuşma yapmak için davet edildim. Ben de, kalıcı olması adına, neden bu konuşmayı yazıya çevirmeyeyim diyerekten bu yazıyı hazırlamaya karar verdim. Umarım sizler için faydalı bir yazı olur. Hadi kolları sıvayalım :)
Öncelikle projemizi oluşturalım;
flutter create medium_json_kullanimi
Varsayılan olarak gelen Counter kodlarını silip kendimiz bir StatefulWidget oluşturalım:
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return SafeArea(
child:Scaffold(
appBar:AppBar(title:Text('Currency API'))
,
body:Container()
));
}
}
Evet, boş bir sayfa oluşturduk. Sırada verileri çekmek için gerekli methodları yazalım. Bu yazıda seçtiğim API, güncel döviz kurlarını bize JSON formatında sağlıyor. Bu API’ı seçme nedenim ise üyelik gerektirmemesi ve apiKey istememesi, bu sayede verileri direkt çekebileceğiz.
Verilerimizi çekmek için ben dio paketini kullanıyorum.
pubspec.yaml dosyamızda dependecies altına:
dio: ^3.0.10
satırını ekleyip
flutter pub get
komutu ile paketimizi uygulamamıza dahil edelim. Artık veri çekmeye hazırız!
home_page.dart dosyamıza dio’yu import edelim :
import 'package:dio/dio.dart';
Ardından dio nesnemizi oluşturup gerekli ayarları verelim. Burada dio’ya base url’imizin https://api.frankfurter.app/ olduğunu söylüyoruz:
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {
Dio dio; @override
void initState(){
super.initState();
BaseOptions options = BaseOptions();
options.baseUrl = 'https://api.frankfurter.app/';
dio = new Dio(options);
} @override
Widget build(BuildContext context) {
return SafeArea(
child:Scaffold(
appBar:AppBar(title:Text('Currency API'))
,
body:Container()
));
}
}
Artık dio nesnemiz hazır. Hazırladığım senaryoda uygulamamız açıldığında /currencies endpoint’inden API’nin sağladığı tüm kurları çekip bir DropDownButton kullanarak bunları listeleyeceğiz.
Buraya kadar gelmişken Future’dan bahsetmemek olmaz. İnternet işlemleri gibi zaman alan işlemlerde uygulamamızın kilitlenmemesi adına asenkron programlama yapmamız gerekir. Future kavramı ise bir işlemin sonucunun (internetten veri çekmek gibi) zaman alabileceği durumlarda kullanılır. Aynı şekilde, geriye Future döndüreceğimiz bir fonksiyonda async belirtecini kullanmamız gerekir.
Kurları çekeceğimiz url’i incelediğimizde kurların tek JSON olarak geldiğini görüyoruz. Yani istek attığımızda gelen cevabı Map’e çevirip tüm key’lerini bir listeye doldurmamız gerekiyor. Kodu incelediğimizde daha iyi anlayacağız:
List<String> currencies=[];
Future<List> getCurrencies()async{
Response result=await dio.get("currencies");
if(result.statusCode==200){
(result.data as Map).forEach((key,value){
currencies.add(key);
});
}
}
İlk önce kurları tutacağımız bir liste tanımladık. Ardından getCurrencies adındaki methodumuzun async olduğunu ve geriye Future<List> döndüreceğini belirttik. Az önce de bahsettiğim gibi bu methodumuzun çalışması zaman alacağı için Future döndürmemiz gerekiyor. dio.get(“currencies”) diyerek /currencies endpointine GET isteği atmış oluyoruz. Bu satırın önünde kullandığımız await deyimi ile bu işlemin bitmesini beklemek istediğimizi belirtiyoruz.
Ardından gelen cevabın başarılı olup olmadığını statusCode’una bakarak kontrol ediyoruz. Ayrıca bir kontrol daha yapıp gelen verinin içerisinde error olup olmadığını da kontrol edebiliriz.
Kontrollerimizi de yaptıktan sonra gelen veriyi Map’e çevirip foreach ile dönerek tüm kurların kısaltmalarının tutulduğu keyleri listemize atıyoruz. Artık kurlarımız elimizde! ( Mecazi anlamda:) ) İlerde bu kur kısaltmaları ile güncel dövizleri çekeceğiz.
Bu methodun başlangıç ve bitişine veri yüklenirken ekranda CircularProgressIndicator göstermek için isLoading adında bir bool değişken tanımlayabiliriz. Methodun son hali şu şekilde :
List<String> currencies=[];
Future<List> getCurrencies()async{
setState(() {
isLoading = true;
});
Response result=await dio.get("currencies");
if(result.statusCode==200){
(result.data as Map).forEach((key,value){
currencies.add(key);
});
}
setState((){
isLoading = false;
});
}
Tabii kurları sayfamız açıldığında çekmek istediğimiz için initState’de methodumuzu çağırmamız gerekiyor:
@override
void initState(){
super.initState();
BaseOptions options = BaseOptions();
options.baseUrl = 'https://api.frankfurter.app/';
dio = new Dio(options);
getCurrencies();
}
Şimdi sıra geldi kurlarımızı ekranda göstermeye. Verileri çekme işlemi sırasında ekranda CircularProgressIndicator göstereceğiz. Tamamlandığında ise kurlarımızı DropDownButton ile listeleyeceğiz. build methodumuzun son hali şu şekilde:
@override
Widget build(BuildContext context) {
return SafeArea(
child:Scaffold(
appBar:AppBar(title:Text('Currency API'))
,
body:Container(
alignment: Alignment.topCenter,
height: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 50),
Text('Currencies : '),
isLoading
? CircularProgressIndicator()
: DropdownButton<String>(
value: selectedCurrency,
onChanged: (value) {
setState(() {
selectedCurrency = value;
});
},
items: currencies
.map((value) => DropdownMenuItem<String>(
value: value, child: Text(value)))
.toList())
],
),
),
)),
);
}
Bu kodu açıklayacak olursak, isLoading true olduğu zaman, yani verilerimiz çekilirken, ekranda CircularProgressIndicator göreceğiz, yükleme tamamlandığında ise DropDownButton Widget’ında her kurumuzu DropdowMenuItem ile gösteriyoruz. Bu sınıf içerisinde ekstradan selectedCurrency değişkeni tanımladım. Bu değişkene ilk değeri kurlarımızı çektiğimiz getCurrencies() methodunda listemizi dönmeden hemen önce kur listemizin ilk elemanını atadım:
selectedCurrency=currencies[0];
DropdownButton Widget’ının value özelliğine de yine selectedCurrency değişkenini veriyoruz. Bu sayede selectedCurrency değiştiğinde DropdownButton’da gösterilen eleman da değişmiş olacak. Son olarak onChanged özelliğine ise setState içerisinde selectedCurrency’i yeni gelen değere eşitliyoruz. Bu sayede seçilen değer dropdown’da gözükecek.
Uygulamamızın şu şekilde çalışması gerekiyor:
Şimdi sıra geldi seçtiğimiz kurun güncel döviz bilgilerini çekmeye. Türk lirasını çekerken isteğimiz bir miktar uzun sürebilir :)
Öncelikle bu adrese atacağımız istekten dönen cevabımızı bir Model dosyasına çevirelim. Bu sayede verilere erişmek daha rahat olacaktır. Korkmayın, tüm değişkenleri tek tek yazmak zorunda değiliz, JSON ı Dart kodumuza dönüştüren siteler mevcut. Benim kullandığıma şuradan ulaşabilirsiniz. API’ımızdan gelen cevabı direkt o siteye yapıştıralım. Name kısmına ben Currency dedim, bu modelimizin adı olacak. Sağ taraftaki language kısmından da Dart seçtiğimizden emin olalım. Ta ta! Tüm modelimiz hazır. Kodu kopyalayıp currency.dart içerisine direkt yapıştırabilirsiniz.
Not: Modeli incelediğimizde amount değişkeninin int olduğunu görüyoruz. Bu şekilde çalıştırdığınızda muhtemelen hata alacaksınız. API’dan gelen amount değeri 1.0 yani double olarak geliyor. O yüzden amount değişkenini double olarak değiştirip bu hatadan kaçınabilirsiniz.
Bizim için aslında en önemli kısmı Currency.fromJson methodu. Bu methoda API’dan gelen veriyi verdiğimizde bize Currency modeli döndürecektir. Biz de artık istediğimiz yerde değişkenlerini ekranda gösterebiliriz.
Şimdi listeden seçilen kura tıkladığımızda ilgili kurun güncel değerlerini çekeceğiz. İstek attığımız url’i incelediğimizde ?from= parametresinde kur kısaltmasını vermemiz gerektiğini görüyoruz. Mesela Türk Lirası için atağımız istek : “https://api.frankfurter.app/latest?from=TRY” olacaktır.
Öncelikle home_page.dart dosyamızda bir adet Currency modelinden değişken tanımlayalım. Bu değişkende güncel olarak çektiğimiz kuru tutacağız.
import 'currency.dart';Currency currentCurrency;
bool isCurrencyLoading = false;
Burda tanımladığımız isCurrencyLoading değişkenimizi ise güncel kuru çekerken ekranda CircularProgressIndicator göstermek için kullanacağız. Bunun için ayrı bir değişken tanımlamamızın sebebi ise güncel kuru çekerken diğer Indicator’ımızın da ekranda gözükmemesi.
Şimdi gelelim güncel kurumuzu çekeceğimiz methoda:
Future<void> getCurrency(String code) async {
setState(() {
isCurrencyLoading = true;
});
final response = await dio.get("latest?from=$code");
if (response.statusCode == 200) {
currentCurrency = Currency.fromJson(response.data);
}
setState(() {
isCurrencyLoading = false;
});
}
Kodumuzu incelediğimizde, methodumuzun String bir değişken aldığını görüyoruz. Anlayacağınız üzere bu değer DropdownButton’dan seçtiğimiz değer, yani kurumuz olacak. Methodumuz yine geriye Future<void> döndürecek. Burda void yerine Currency de döndürebilirdik. ama ben methodun içerisinde API’den gelen veriyi daha önce tanımladığım currentCurrency değişkenine atadığım için ekstradan bunu geriye döndürmeye gerek duymadım. Methodumuzun başlangıcında ve sonunda isCurrencyLoading değişkenini sırasıyla true ve false yapıyoruz. Bu sayede veriler çekilirken ekranda CircularProgressIndicator göstereceğiz.
final response = await dio.get("latest?from=$code");
if (response.statusCode == 200) {
currentCurrency = Currency.fromJson(response.data);
}
Asıl önemli kısmımız burası. İsteği attıktan sonra gelen cevap başarılı ise (burada ekstradan gelen datanın içerisinde error var mı diye de kontrol edebiliriz) az önce de bahsettiğim gibi bize gelen cevabı, yani response.data’yı, Currency.fromJson methoduna vererek geriye Currency değişkeni döndürmesini sağlıyoruz. Bunu da daha önce tanımladığımız currentCurrency değişkenine eşitliyoruz. Artık modelimizi ekranda gösterebiliriz!
Ekranda göstereceğimiz Widget’ı ayrı bir method ile build edelim :
Kodu incelediğimizde, ilk currentCurrency null mı diye kontrol ediyoruz. currentCurrency değişkeni, kullanıcı henüz bir kur çekmediyse null olacaktır. Null ise boş bir Container gösteriyoruz. Null değil ise kontrol ettiğimiz değişken isCurrencyLoading olacak. Bu değişken true ise verilerimiz yüklenme durumunda olduğu için ekranda CircularProgressIndicator göstereceğiz, yükleme tamamlandığında bir ListView gösterebiliriz. ListView’ın seperated özelliğini kullanarak kurları Divider ile çizgi çekerek ayırıyoruz. itemCount özelliğimiz ise çektiğimiz kurun döviz bilgilerinin sayısı olacak. Bunun sayısını ise currentCurrency değişkenimizin rates özelliğindeki entrylerin length ine bakarak öğrenebiliriz.
Ardından ListView’ını builder özelliğinde geriye ListTile döndürelim. Her ListTile’da da kur ve oran bilgisini göstereceğiz.
currentCurrency.rates.entries.toList()[index].value
diyerek kurların oranlarına erişiyoruz.
currentCurrency.rates.entries.toList()[index].key
koduyla kurun adına erişmiş oluyoruz. Bunları Text Widget içerisinde trailing ve leading özelliklerine veriyoruz.
Son olarak DropdownButton’ın onChanged özelliğinde getCurrency methodunu çağırmamız gerekiyor. Bu sayede seçtiğimiz kurun oranlarını çağırabileceğiz:
onChanged: (value) async {
setState(() {
selectedCurrency = value;
});
await getCurrency(value);
},
Ve hazır mıyız, tüm bunların sonucunda çıktımızın şu şekilde olması gerekiyor:
Sonuç
Sonuç olarak, herhangi bir API’dan verileri çekip ekranda gösterdik. Tavsiyem sadece bununla da kalmamanız, bunların haricinde kullanabileceğiniz daha gelişmiş API’lar var. İlginizini çekeni uygulamanızda kullanıp, hatta ileride kendi API’ınızı geliştirip uygulamanızda haberleştirebilirsiniz.
Tüm kodlara buradan ulaşabilirsiniz. Umarım faydalı bir yazı olmuştur.
Görüşmek üzere, sağlıkla kalın!