데이터 통신을 구현하려면 서버와 데이터가 필요하다. 서버를 직접 구축하고 통신으로 주고받을 데이터도 직접 생성할 수 있겠지만, 공개된 데이터를 활용하면 시간을 좀 더 아낄 수 있다.
카카오 API
여기서 이용하려는 데이터는 책 정보로, 카카오가 제공하는 검색 API를 이용해 얻는다. 카카오는 국내에서 네이버와 함께 손꼽히는 기업으로서 다양한 분야의 많은 데이터를 가지고 있다. 이들은 자사의 데이터를 개발자들이 사용할 수 있도록 여러 가지 API를 제공한다.
카카오 API를 이용하려면 API 키를 얻어야 한다.
로그인 후 메뉴에서 "내 애플리케이션" 클릭 > "애플리케이션 추가하기" 클릭
이름에 httpTest 입력 후, 각자 이름 입력
httpTest 애플리케이션 클릭해서 정보를 확인
검색 api에서 활용할 REST API 키를 기억해둘것.
https://developers.kakao.com/docs/latest/ko/daum-search/dev-guide#search-book
카카오 api 정보
패키지
플러터에서 http api를 이용할 수 있도록 http패키지를 사용하자.
- pubspec.yaml
dependencies:
flutter:
sdk: flutter
http: ^0.12.2
- main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.deepPurple,
),
home: HttpApp(),
);
}
}
class HttpApp extends StatefulWidget {
@override
_HttpAppState createState() => _HttpAppState();
}
class _HttpAppState extends State<HttpApp> {
String result = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Http Example"),
),
body: Container(
child: Center(
child: Text('$result'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
var url = Uri.parse('http://www.google.com');
var response = await http.get(url);
setState(() {
result = response.body;
});
},
child: Icon(Icons.file_download),
),
);
}
}
이대로 앱을 실행하고 플로팅 버튼을 누르면
에러가 뜬다.
Unhandled Exception: SocketException: Failed host lookup: 'www.google.com' (OS Error: No address associated with hostname, errno = 7)
cloudflare dns인 1.1.1.1은 들어가지나 google.com이 들어가지지 않으므로 dns에러임을 알 수 있다.
windows라면 android dns settings을 해야한다.
> emulator.exe -list-avds
Pixel_4_API_30
> emulator.exe -avd Pixel_4_API_30 -dns-server 1.1.1.1
emulator: Android emulator version 30.6.5.0 (build_id 7324830) (CL:N/A)
emulator: WARNING: Another emualtor is still running, wait for a sec...
handleCpuAcceleration: feature check for hvf
added library vulkan-1.dll
Failed to open /qemu.conf, err: 2
Windows Hypervisor Platform accelerator is operational
emulator: INFO: GrpcServices.cpp:315: Started GRPC server at 127.0.0.1:8554, security: Local
emulator: INFO: EmulatorAdvertisement.cpp:93: Advertising in: C:\Users\SunMyeong Lee\AppData\Local\Temp\avd\running\pid_4308.ini
이제 앱을 키면 잘 된다.
책 정보 가져오기
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.deepPurple,
),
home: HttpApp(),
);
}
}
class HttpApp extends StatefulWidget {
@override
_HttpAppState createState() => _HttpAppState();
}
class _HttpAppState extends State<HttpApp> {
String result = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Http Example"),
),
body: Container(
child: Center(
child: Text('$result'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
getJSONData();
// var url = Uri.parse('http://www.google.com');
// var response = await http.get(url);
// setState(() {
// result = response.body;
// });
},
child: Icon(Icons.file_download),
),
);
}
Future<String> getJSONData() async {
var url = Uri.parse('https://dapi.kakao.com/v3/search/book?target=title&query=doit');
var response = await http.get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});
print(response.body);
return "Successful";
}
}
이제 본격적으로 api를 json으로 파싱해 활용해보자.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.deepPurple,
),
home: HttpApp(),
);
}
}
class HttpApp extends StatefulWidget {
@override
_HttpAppState createState() => _HttpAppState();
}
class _HttpAppState extends State<HttpApp> {
String result = '';
List data = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Http Example"),
),
body: Container(
child: Center(
child: data.length == 0
? Text(
"데이터가 없습니다",
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
)
: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Container(
child: Column(
children: [
Text(data[index]['title'].toString()),
Text(data[index]['authors'].toString()),
Text(data[index]['sale_price'].toString()),
Text(data[index]['status'].toString()),
Image.network(
data[index]['thumbnail'],
height: 100,
width: 100,
fit: BoxFit.contain,
)
],
),
),
);
},
itemCount: data.length,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
getJSONData();
// var url = Uri.parse('http://www.google.com');
// var response = await http.get(url);
// setState(() {
// result = response.body;
// });
},
child: Icon(Icons.file_download),
),
);
}
Future<String> getJSONData() async {
var url = Uri.parse(
'https://dapi.kakao.com/v3/search/book?target=title&query=doit');
var response = await http
.get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});
setState(() {
var dataConvertedToJSON = json.decode(response.body);
List result = dataConvertedToJSON["documents"];
data.addAll(result);
});
return response.body;
}
}
카드 위젯을 조금 더 꾸며서,
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.deepPurple,
),
home: HttpApp(),
);
}
}
class HttpApp extends StatefulWidget {
@override
_HttpAppState createState() => _HttpAppState();
}
class _HttpAppState extends State<HttpApp> {
String result = '';
List data = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Http Example"),
),
body: Container(
child: Center(
child: data.length == 0
? Text(
"데이터가 없습니다",
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
)
: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Container(
child: Row(
children: [
Image.network(
data[index]['thumbnail'],
height: 100,
width: 100,
fit: BoxFit.contain,
),
Column(
children: [
Container(
width: MediaQuery.of(context).size.width - 150,
child: Text(data[index]['title'].toString()),
),
Text(data[index]['authors'].toString()),
Text(data[index]['sale_price'].toString()),
Text(data[index]['status'].toString()),
],
),
],
),
),
);
},
itemCount: data.length,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
getJSONData();
// var url = Uri.parse('http://www.google.com');
// var response = await http.get(url);
// setState(() {
// result = response.body;
// });
},
child: Icon(Icons.file_download),
),
);
}
Future<String> getJSONData() async {
var url = Uri.parse(
'https://dapi.kakao.com/v3/search/book?target=title&query=doit');
var response = await http
.get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});
setState(() {
var dataConvertedToJSON = json.decode(response.body);
List result = dataConvertedToJSON["documents"];
data.addAll(result);
});
return response.body;
}
}
검색 기능도 넣어보자.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.deepPurple,
),
home: HttpApp(),
);
}
}
class HttpApp extends StatefulWidget {
@override
_HttpAppState createState() => _HttpAppState();
}
class _HttpAppState extends State<HttpApp> {
String result = '';
List data = [];
TextEditingController _editingController = new TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
controller: _editingController,
style: TextStyle(color: Colors.white),
keyboardType: TextInputType.text,
decoration: InputDecoration(icon: Icon(Icons.search), hintText: '검색어를 입력하세요'),
),
),
body: Container(
child: Center(
child: data.length == 0
? Text(
"데이터가 없습니다",
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
)
: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Container(
child: Row(
children: [
Image.network(
data[index]['thumbnail'],
height: 100,
width: 100,
fit: BoxFit.contain,
),
Column(
children: [
Container(
width: MediaQuery.of(context).size.width - 150,
child: Text(data[index]['title'].toString()),
),
Text(data[index]['authors'].toString()),
Text(data[index]['sale_price'].toString()),
Text(data[index]['status'].toString()),
],
),
],
),
),
);
},
itemCount: data.length,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
getJSONData();
// var url = Uri.parse('http://www.google.com');
// var response = await http.get(url);
// setState(() {
// result = response.body;
// });
},
child: Icon(Icons.file_download),
),
);
}
Future<String> getJSONData() async {
var url = Uri.parse(
'https://dapi.kakao.com/v3/search/book?target=title&query=${_editingController.text}');
var response = await http
.get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});
setState(() {
var dataConvertedToJSON = json.decode(response.body);
List result = dataConvertedToJSON["documents"];
data.addAll(result);
});
return response.body;
}
}
스크롤로 책 정보를 가져와보자.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.deepPurple,
),
home: HttpApp(),
);
}
}
class HttpApp extends StatefulWidget {
@override
_HttpAppState createState() => _HttpAppState();
}
class _HttpAppState extends State<HttpApp> {
String result = '';
List data = [];
TextEditingController _editingController = new TextEditingController();
ScrollController _scrollController = new ScrollController();
int page = 1;
@override
void initState() {
// TODO: implement initState
super.initState();
_editingController.addListener(() {
if (_scrollController.offset >= _scrollController.position.maxScrollExtent && !_scrollController.position.outOfRange) {
// 리스트의 마지막일때.
print('bottom');
page++;
getJSONData();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
controller: _editingController,
style: TextStyle(color: Colors.white),
keyboardType: TextInputType.text,
decoration: InputDecoration(icon: Icon(Icons.search), hintText: '검색어를 입력하세요'),
),
),
body: Container(
child: Center(
child: data.length == 0
? Text(
"데이터가 없습니다",
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
)
: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Container(
child: Row(
children: [
Image.network(
data[index]['thumbnail'],
height: 100,
width: 100,
fit: BoxFit.contain,
),
Column(
children: [
Container(
width: MediaQuery.of(context).size.width - 150,
child: Text(data[index]['title'].toString()),
),
Text(data[index]['authors'].toString()),
Text(data[index]['sale_price'].toString()),
Text(data[index]['status'].toString()),
],
),
],
),
),
);
},
itemCount: data.length,
controller: _scrollController,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
page = 1;
data.clear();
getJSONData();
// var url = Uri.parse('http://www.google.com');
// var response = await http.get(url);
// setState(() {
// result = response.body;
// });
},
child: Icon(Icons.file_download),
),
);
}
Future<String> getJSONData() async {
var url = Uri.parse(
'https://dapi.kakao.com/v3/search/book?target=title&page=$page&query=${_editingController.text}');
var response = await http
.get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});
setState(() {
var dataConvertedToJSON = json.decode(response.body);
List result = dataConvertedToJSON["documents"];
data.addAll(result);
});
return response.body;
}
}
Uploaded by Notion2Tistory v1.1.0