순서
- 의존성 추가하기 및 import 하기
- 객체 만들기
- 데이터 Fetch 하기
- Future로 api 호출하기
의존성 추가하기
dependencies:
http: <latest_version>
# 받고자 하는 버전은 모르겠고 최신 버전으로 받고 싶다면 http: 만 입력해두 된다.
패키지 import하기
import 'package:http/http.dart' as http;
객체 만들기
JSON 형태로 넘어오는 데이터를 받기 위해 객체를 생성하자
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
데이터 Fetch 하기
class _MyAppState extends State<MyApp> {
Future<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
fetch는 initState() 또는 didChangeDependencies() 에서 호출하세요.
initState() 메소드는 정확히 한 번 호출 된 다음 다시 호출 되지 않습니다.
변경에 대한 응답으로 API를 다시 로드하는 옵션을 원하면 didChangeDependencies()에 호출을 넣으세요.
Future로 api 호출하기#1
Future<Album> fetchAlbum() async {
final response = await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load album');
}
}
Future는 비동기로 동작하는 자료형으로 데이터가 없을 때 보여줄 화면을 지정 했다가 ‘미래에’ 데이터가 들어오게 되면 채우는 형식이다. 자세한 내용은 밑에서 한번 더 설명할 것이다.
Future로 api 호출하기#2
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
);
FutureBuilder에 future는 실행 할 메서드를 적고 builder에서 데이터를 받을 때 어떻게 처리할지 기입하면 된다.
첫 화면 진입 시 통신하는데 딜레이가 생겨 futureAlbum 함수에서는 에러는 뱉어낼 것이고 비동기 통신으로 나중에 데이터가 들어오면 builder 부분이 재실행되어 snapshot.hasData 의 부분이 리턴된다.
왜 fetchAlbum()을 initState()에서 호출할까?
편리하지만 build() 메소드에 API 호출을 넣지 않는 것이 좋습니다.
Flutter build()는 뷰에서 무엇이든 변경해야 할 때마다 메서드를 호출하며 이는 놀라 울 정도로 자주 발생합니다.
메서드에 fetch 호출을 남겨두면 build() 불필요한 호출로 API가 넘쳐 앱 속도가 느려집니다.
전체 코드
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(json.decode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
출처 : https://flutter.dev/docs/cookbook/networking/fetch-data