Flutter Application/응용 구현

Flutter API 호출 구현(http request, dio) - 로또 번호 가져오기

sdchjjj 2024. 12. 2. 18:34
반응형

안녕하세요.

 

이번 포스팅에서는 dio를 사용한 API request를 구현해 보겠습니다.

 

언어: dart

IDE: Android Studio

Framework: Flutter

Test device: Android

 

Dio는 Flutter에서 네트워크 요청을 수행하기 위해 사용되는 효과적인 HTTP 클라이언트 라이브러리입니다. 유연성, 사용 편의성이나 사용자 지정 헤더, 요청/응답 처리와 같은 고급 기능을 지원하는 점에서 널리 사용됩니다.

 

추가로, 이전에 포스팅했던 bloc pattern을 적용해서 예제를 작성하겠습니다.

https://it-of-fortune.tistory.com/49

 

Flutter 상태 관리 - Bloc pattern

안녕하세요.이번 포스팅에서는 Flutter app 개발 시 상태 관리를 위한 디자인 패턴 중 하나인 Bloc pattern에 대해 알아보겠습니다. 언어: dartIDE: Android StudioFramework: FlutterTest device: Android Bloc(Business

it-of-fortune.tistory.com

 

먼저, 아래 명령어로 dio를 추가해 줍니다.

flutter pub add dio

 

figure 1

 

다음은 아래와 같이 폴더와 파일을 구성해 줍니다(figure 2).

figure 2

 

이제 응답받은 결과를 넣을 model class를 생성합니다.

lotto_model.dart

class LottoResult {
  final String drawDate;
  final int totalSellAmount;
  final int firstWinAmount;
  final int firstPrizeWinners;
  final List<int> winningNumbers;
  final int bonusNumber;
  final int drawNumber;

  LottoResult({
    required this.drawDate,
    required this.totalSellAmount,
    required this.firstWinAmount,
    required this.firstPrizeWinners,
    required this.winningNumbers,
    required this.bonusNumber,
    required this.drawNumber,
  });

  factory LottoResult.fromJson(Map<String, dynamic> json) {
    return LottoResult(
      drawDate: json['drwNoDate'],
      totalSellAmount: json['totSellamnt'],
      firstWinAmount: json['firstWinamnt'],
      firstPrizeWinners: json['firstPrzwnerCo'],
      winningNumbers: [
        json['drwtNo1'],
        json['drwtNo2'],
        json['drwtNo3'],
        json['drwtNo4'],
        json['drwtNo5'],
        json['drwtNo6'],
      ],
      bonusNumber: json['bnusNo'],
      drawNumber: json['drwNo'],
    );
  }
}

 

API 호출을 진행할 service를 생성합니다.

lotto_service.dart

import 'dart:convert'; // Import for JSON decoding
import 'package:dio/dio.dart';

class LottoService {
  final Dio _dio = Dio();

  LottoService() {
    _dio.options.headers = {
      'Content-Type': 'application/json',
    };
  }

  Future<Map<String, dynamic>> fetchLottoData(int drawNumber) async {
    const String baseUrl = 'https://www.dhlottery.co.kr/common.do';
    final params = {
      'method': 'getLottoNumber',
      'drwNo': drawNumber,
    };

    try {
      final response = await _dio.get(baseUrl, queryParameters: params);

      // Decode if the response is a JSON string
      final rawResponse = response.data;
      final data = rawResponse is String ? json.decode(rawResponse) : rawResponse;

      // Check for success
      if (data['returnValue'] == 'success') {
        return data;
      } else {
        throw Exception('Failed to fetch data: ${data['returnValue']}');
      }
    } catch (e) {
      throw Exception('Error: $e');
    }
  }
}

 

데이터 요청과 수신을 위한 준비는 끝났습니다. 여기서 우리는 호출부터 응답을 그려주는 것까지 bloc pattern을 통해 진행되도록 구현해 보겠습니다.

 - lotto_state.dart

 - lotto_event.dart

 - lotto_bloc.dart
위 파일들을 각각 알맞은 폴더 내에 생성해 줍니다.

 

우선 state입니다.

lotto_state.dart

import '../model/lotto_model.dart';

abstract class LottoState {}

class LottoInitial extends LottoState {}

class LottoLoading extends LottoState {}

class LottoLoaded extends LottoState {
  final LottoResult result;

  LottoLoaded(this.result);
}

class LottoError extends LottoState {
  final String message;

  LottoError(this.message);
}

 

event입니다.

lotto_event.dart

abstract class LottoEvent {}

class FetchLotto extends LottoEvent {
  final int drawNumber;

  FetchLotto(this.drawNumber);
}

 

다음은 bloc 부분입니다.

lotto_bloc.dart

import 'package:flutter_bloc/flutter_bloc.dart';
import '../event/lotto_event.dart';
import '../model/lotto_model.dart';
import '../service/lotto_service.dart';
import '../state/lotto_state.dart';


class LottoBloc extends Bloc<LottoEvent, LottoState> {
  final LottoService lottoService;

  LottoBloc(this.lottoService) : super(LottoInitial()) {
    on<FetchLotto>(_onFetchLotto);
  }

  Future<void> _onFetchLotto(FetchLotto event, Emitter<LottoState> emit) async {
    emit(LottoLoading());
    try {
      final data = await lottoService.fetchLottoData(event.drawNumber);
      final result = LottoResult.fromJson(data);
      emit(LottoLoaded(result));
    } catch (e) {
      emit(LottoError(e.toString()));
    }
  }
}

 

이제 준비가 끝났으니 UI 코드를 작성해 보겠습니다.

lotto_result_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/lotto_bloc.dart';
import '../event/lotto_event.dart';
import '../service/lotto_service.dart';
import '../state/lotto_state.dart';


class LottoScreen extends StatelessWidget {
  final LottoService lottoService;

  LottoScreen({Key? key, required this.lottoService}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => LottoBloc(lottoService),
      child: Scaffold(
        appBar: AppBar(
          title: Text(
            'Lotto Results',
            style: TextStyle(color: Colors.white),
          ),
        ),
        body: LottoView(),
      ),
    );
  }
}

class LottoView extends StatefulWidget {
  @override
  _LottoViewState createState() => _LottoViewState();
}

class _LottoViewState extends State<LottoView> {
  final TextEditingController _drawNumberController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          TextField(
            controller: _drawNumberController,
            keyboardType: TextInputType.number,
            decoration: InputDecoration(
              labelText: '회차를 입력하세요.',
              labelStyle: TextStyle(color: Colors.grey[600]),
            ),
          ),
          const SizedBox(height: 10),
          ElevatedButton(
            onPressed: () {
              final drawNumber = int.tryParse(_drawNumberController.text);
              if (drawNumber != null && drawNumber > 0) {
                context.read<LottoBloc>().add(FetchLotto(drawNumber));
              } else {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('유효한 회차를 입력하세요.')),
                );
              }
            },
            child: Text('결과 불러오기'),
          ),
          const SizedBox(height: 20),
          Expanded(
            child: BlocBuilder<LottoBloc, LottoState>(
              builder: (context, state) {
                if (state is LottoInitial) {
                  return Center(
                    child: Text(
                      '결과 없음.',
                      style: TextStyle(color: Colors.grey[600], fontSize: 16),
                    ),
                  );
                } else if (state is LottoLoading) {
                  return Center(child: CircularProgressIndicator());
                } else if (state is LottoLoaded) {
                  final result = state.result;
                  return SingleChildScrollView(
                    child: Card(
                      elevation: 5,
                      margin: EdgeInsets.all(10),
                      child: Padding(
                        padding: const EdgeInsets.all(16.0),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text('Draw Number: ${result.drawNumber}', style: TextStyle(fontSize: 18)),
                            Text('Draw Date: ${result.drawDate}', style: TextStyle(fontSize: 18)),
                            Text('Total Sell Amount: ${result.totalSellAmount}', style: TextStyle(fontSize: 18)),
                            Text('First Prize Amount: ${result.firstWinAmount}', style: TextStyle(fontSize: 18)),
                            Text('First Prize Winners: ${result.firstPrizeWinners}', style: TextStyle(fontSize: 18)),
                            Text('Winning Numbers: ${result.winningNumbers.join(', ')}', style: TextStyle(fontSize: 18)),
                            Text('Bonus Number: ${result.bonusNumber}', style: TextStyle(fontSize: 18)),
                          ],
                        ),
                      ),
                    ),
                  );
                } else if (state is LottoError) {
                  return Center(
                    child: Text(
                      'Error: ${state.message}',
                      style: TextStyle(color: Colors.red),
                    ),
                  );
                }
                return Container();
              },
            ),
          ),
        ],
      ),
    );
  }
}

 

마지막으로 main 부분입니다.

main.dart

import 'package:flutter/material.dart';
import 'package:for_practice/src/screen/lotto_result_screen.dart';
import 'package:for_practice/src/service/lotto_service.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.lightBlue,
        appBarTheme: AppBarTheme(
          color: Colors.lightBlue,
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.lightBlue, // Button color
          ),
        ),
        inputDecorationTheme: InputDecorationTheme(
          filled: true,
          fillColor: Colors.grey[200],
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(10.0),
            borderSide: BorderSide.none,
          ),
        ),
        scaffoldBackgroundColor: Colors.grey[100],
      ),
      home: LottoScreen(lottoService: LottoService()),
    );
  }
}

 

사용자에게 번호를 입력받아 해당하는 회차의 데이터를 불러와 UI에 표기해 주는 어플입니다.

 

테스트해 보겠습니다.

result

정상적으로 받아오고 있습니다.

 

이상 포스팅을 마치겠습니다.

 

감사합니다.

728x90
반응형