'플러터 리스트뷰 예제'에 해당되는 글 1건

  1. 2023.07.18 플러터, REST API를 이용한 리스트 만들기

카카오 REST API를 이용한 리스트 만들기

  1. REST API 키 발급
  2. http 통신 패키지 설정
  3. http 통신 확인
  4. jsondata ListView로 생성
  5. Scroll을 이용한 추가 가져오기

1. 카카오 API를 이용하기 위해, 개발자 사이트 등록 및 발급키 확인

카카오 개발자 사이트 : https://developers.kakao.com 

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

개발자 사이트에 가입후 개발할 어플리케이션까지 등록을 하면, 아래와 같이 4개의 키가 자동발급됩니다. 우리는 여기서 REST API 키를 이용할 계획입니다. 

개발자  사이트에서 REST API 키 확인

2. http 통신 패키지 설정

pubspec.yaml 파일의 dependencies 아래에 http를 추가합니다. 만약 null safety 지원 등으로 오류가 발생하면, https://pub.dev/packages/http/versions 에 접속해서 PC에 설치된 SDK 버전에 맞는 http버전을 입력하시면 됩니다. 참고로, 제 SDK 는 2.16.XX입니다. 

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3

버전에 따라, Null safety 가 존재하고, 존재하지 않는 버전이 있음.

3. http 통신 확인

main.dart 파일에서 http를 import한 후, 플로팅 버튼을 클릭하면, 특정 페이지를 읽어 화면에 출력하는 페이지(골격)을 만들어봅니다. 비동기(async)를 이용해서 http.get(url)을 수행하면 됩니다. 

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(
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => _HttpApp();
}

class _HttpApp 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 {
          String url = 'http://www.google.com';
          var response = await http.get(Uri.parse(url));
          setState(() {
            result = response.body;
          } );
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

 

4. JsonData를 ListView로 생성

먼저 jsonData를 가져올 함수를 비동기함수로 생성하고, url을 처음 발급받은 REST API 키를 이용해서 카카오 api 서버를 호출합니다. 

Future<String> getJSONData() async {
    var query = _editingController?.value.text;
    String url = "https://dapi.kakao.com/v3/search/book?target=title&query=${query}&page=${page}";
    var response = await http.get(Uri.parse(url),
                   headers: {"Authorization" : "KakaoAK cf********************6"});
    setState(() {
      var dataConvertedToJSON = json.decode(response.body);
      List result = dataConvertedToJSON['documents'];
      data.addAll(result);
    });
    return response.body;
  }

카카오 개발문서에 보면, Request할 때, 헤더값으로 Authorization에 Authorization: KakaoAK ${REST_API_KEY} 을 입력합니다. 도서, 비디오, 이미지 등 조회 내용에 따라 질의 항목, 결과 항목이 다르니 확인하시고 이용하시기 바랍니다.
카카오 개발사이트 > Docs > Daum Search >  REST API 

카카오 도서 관련 REST API, 조회건수는 default 값이 10

ListView.builder를 이용해서, Card() 안에 리스트 건에 대한 내용을 표시합니다. 개발하시다가 이미지가 화면을 벗어나면 오류 표시가 나는데, MediaQuery.of(context).size.width- 150 으로 하면, 화면에 모두 표시됩니다. 여기서 -150은 디바이스 전체 width에 들어가는 내용이, "이미지 + 글박스"로 구성되는데, 여기서 이미지 사이즈보다 큰 값을 입력하면 됩니다.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

< 중략>

class _HttpApp extends State<HttpApp> {

  String result="";
  List data = [];
  TextEditingController? _editingController;    

  @override
  void initState() {
    super.initState();
    _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(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: <Widget>[
                        FadeInImage.assetNetwork(
                          height: 100,
                          width: 100,
                          fit: BoxFit.contain,
                          placeholder: 'images/spinner.gif',
                          image: data[index]['thumbnail'].toString(),
                        ),
                        //Image.network(data[index]['thumbnail'].toString(), height: 80, width: 80, fit:BoxFit.contain,),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.start,
                            children: <Widget>[
                              //Text(data[index]['title'].toString(), overflow: TextOverflow.ellipsis,),
                              Container(
                                width: MediaQuery.of(context).size.width -120,
                                child: Text(data[index]['title'].toString(),  textAlign: TextAlign.left,),
                              ),
                              Text(data[index]['authors'].toString()),
                              Text(data[index]['sale_price'].toString()),
                              Text(data[index]['status'].toString()),
                            ]
                        )
                      ],
                    ),
                  ),
                );
          }, itemCount:data.length)
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: ()  {         
          data.clear();
          getJSONData();
        },
        child: Icon(Icons.file_download),
      ),
    );
  }
 
< 생략 >
  1. Scroll을 이용한 추가 가져오기

5. Scroll 을 이용한 추가 가져오기

책 소스에는 page를 추가하지 않아 다음 페이지를 가져오지 않는데, page가 없으면 항상 처음 10개만 가져옵니다. 그래서 새로운 검색어를 입력할 때마다 page를 초기화하고 계속 추가 조회할 때 마다 +1을 해줘야 합니다. 

import 'package:flutter/cupertino.dart';
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(
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => _HttpApp();
}

class _HttpApp extends State<HttpApp> {

  String result="";
  List data = [];
  TextEditingController? _editingController;
  ScrollController? _scollController;
  int page = 1;

  @override
  void initState() {
    super.initState();
    _editingController = new TextEditingController();
    _scollController = new ScrollController();

    _scollController?.addListener(() {
      if(_scollController!.offset >=
         _scollController!.position.maxScrollExtent &&
          !_scollController!.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(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: <Widget>[
                        FadeInImage.assetNetwork(
                          height: 100,
                          width: 100,
                          fit: BoxFit.contain,
                          placeholder: 'images/spinner.gif',
                          image: data[index]['thumbnail'].toString(),
                        ),
                        //Image.network(data[index]['thumbnail'].toString(), height: 80, width: 80, fit:BoxFit.contain,),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.start,
                            children: <Widget>[
                              //Text(data[index]['title'].toString(), overflow: TextOverflow.ellipsis,),
                              Container(
                                width: MediaQuery.of(context).size.width -120,
                                child: Text(data[index]['title'].toString(),  textAlign: TextAlign.left,),
                              ),
                              Text(data[index]['authors'].toString()),
                              Text(data[index]['sale_price'].toString()),
                              Text(data[index]['status'].toString()),
                            ]
                        )
                      ],
                    ),
                  ),
                );
          }, itemCount:data.length
          , controller: _scollController)
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: ()  {
          page = 1;
          data.clear();
          getJSONData();
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

  Future<String> getJSONData() async {
    var query = _editingController?.value.text;
    String url = "https://dapi.kakao.com/v3/search/book?target=title&query=${query}&page=${page}";

    var response = await http.get(Uri.parse(url),
                   headers: {"Authorization" : "KakaoAK cf2***************************6"});

    print(response.body);

    setState(() {
      var dataConvertedToJSON = json.decode(response.body);
      List result = dataConvertedToJSON['documents'];
      data.addAll(result);
    });

    return response.body;
  }
}
요약
• REST API로 Json데이터를 가져와 ListView로 생성할 수 있다.
• REST API 호출시 Scroll을 이용하면서 page를 갱신해 추가 조회를 할 수 있다.
• 패키지를 가져올 때, 플러터 SDK 버전에 맞는 패키지 버전을 설정해야 한다.

 

Posted by 목표를 가지고 달린다
,