Flutter에서 REST API 데이터로 드롭다운 메뉴 만들기 방법 총정리

Flutter에서 REST API 데이터로 드롭다운 메뉴 만들기 방법 총정리
Cozy CodingPosted On Jul 9, 20247 min read

REST API에서 데이터를 드롭다운 메뉴로 로드하는 것은 플러터 앱 개발에서 흔한 작업입니다. 특히 동적 데이터 선택이 필요할 때 유용합니다. 이 프로세스에는 원격 서버에서 데이터를 가져와 구문 분석하여 검색한 데이터로 드롭다운 위젯을 채우는 과정이 포함됩니다. 또한 예외를 신중하게 처리하여 부드러운 사용자 경험을 제공하는 것이 중요합니다.

이 안내서에서는 예외를 처리하는 동안 플러터 어플리케이션에서 REST API로부터 데이터를 로드하여 드롭다운 메뉴에 표시하는 단계를 안내하겠습니다.

소스 코드:

단계 1: 의존성 추가

먼저 Flutter 프로젝트를 생성하고 yaml 파일에 http 패키지를 추가하세요

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3

단계 2: 포스트 모델

표 태그를 Markdown 형식으로 변경해 보겠습니다.

class PostsModel {
  int? userId;
  int? id;
  String? title;
  String? body;

  PostsModel({this.userId, this.id, this.title, this.body});

  PostsModel.fromJson(Map<String, dynamic> json) {
    userId = json['userId'];
    id = json['id'];
    title = json['title'];
    body = json['body'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['userId'] = this.userId;
    data['id'] = this.id;
    data['title'] = this.title;
    data['body'] = this.body;
    return data;
  }
}

단계 3: API 호출

getPostApi()라는 Future 메서드를 정의하여 서버로 HTTP GET 요청을 보냅니다. 이 메서드는 데이터를 반환하고 해당 데이터를 위젯에 표시할 포스트 목록으로 반환할 것입니다.

Future<List<PostsModel>> getPostApi() async {

    try {
      final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
      final body = json.decode(response.body) as List;
      if (response.statusCode == 200) {

        return body.map((dynamic json) {
          final map = json as Map<String, dynamic>;
          return PostsModel(
            id: map['id'] as int,
            title: map['title'] as String,
            body: map['body'] as String,
          );
        }).toList();
      }
    } on SocketException {
      await Future.delayed(const Duration(milliseconds: 1800));
      throw Exception('인터넷 연결 없음');
    } on TimeoutException {
      throw Exception('');
    }
    throw Exception('데이터를 가져오는 중 에러 발생');
}

단계 4: 드롭다운 위젯

newData라는 변수는 드롭다운에서 선택한 값을 저장합니다. ID를 보여주고 있지만 원하는 값으로 변경해도 괜찮습니다.

getPostApi() 메소드는 Future를 반환하므로 FutureBuilder를 사용합니다.

FutureBuilder<List<PostsModel>>(
              future: getPostApi(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return DropdownButton(
                    // 초기 값
                    value: newData,
                    hint: Text('값을 선택하세요'),
                    isExpanded: true,
                    // 아래 화살표 아이콘
                    icon: const Icon(Icons.keyboard_arrow_down),

                    // 아이템 목록 배열
                    items: snapshot.data!.map((item) {
                      return DropdownMenuItem(
                        value: item.id,
                        child: Text(item.id.toString()),
                      );
                    }).toList(),
                    onChanged: (value) {
                      newData = value ;
                      setState(() {

                      });
                    },

                  );
                } else {
                  return Center(child: const CircularProgressIndicator());
                }
              },

완성된 소스 코드:

import 'dart:async';
import 'dart:convert';
import 'dart:io';

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

class DropDownApi extends StatefulWidget {
  const DropDownApi({Key? key}) : super(key: key);

  @override
  State<DropDownApi> createState() => _DropDownApiState();
}

class _DropDownApiState extends State<DropDownApi> {




  Future<List<PostsModel>> getPostApi ()async{

    try {
      final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts')) ;
      final body = json.decode(response.body) as List;
      if(response.statusCode == 200) {

        return  body.map((dynamic json) {
          final map = json as Map<String, dynamic>;
          return  PostsModel(
            id: map['id'] as int,
            title: map['title'] as String,
            body: map['body'] as String,
          );
        }).toList();
      }
    } on SocketException {
      await Future.delayed(const Duration(milliseconds: 1800));
      throw Exception('인터넷 연결 없음');
    } on TimeoutException {
      throw Exception('');
    }
    throw Exception('데이터 불러오기 오류');

  }

  var newData ;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Dropdown Api'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            FutureBuilder<List<PostsModel>>(
              future: getPostApi(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return DropdownButton(
                    // 초기 값
                    value: newData,
                    hint: Text('값을 선택하세요'),
                    isExpanded: true,
                    // 아래 화살표 아이콘
                    icon: const Icon(Icons.keyboard_arrow_down),

                    // 아이템 목록 배열
                    items: snapshot.data!.map((item) {
                      return DropdownMenuItem(
                        value: item.id,
                        child: Text(item.id.toString()),
                      );
                    }).toList(),
                    onChanged: (value) {
                      newData = value ;
                      setState(() {

                      });
                    },

                  );
                } else {
                  return Center(child: const CircularProgressIndicator());
                }
              },
            )
          ],
        ),
      ),
    );
  }
}