[Flutter] 특정 값마다 초기화면 다르게 만들어줘엉!(API get방식 받기)

리미·2020년 4월 24일
1

Flutter

목록 보기
2/6

Welcome to hellgate

이제 드디어 올것이 왔다.
내용인 즉슨 API값 get방식으로 받아서 그 값마다 초기화면을 다르게 한다는것.
(post방식은 나중에 할거야.....)
첫번째 강좌인 탭메뉴의 작업탭(WorkScreens)에서 발생하는 이벤트이다.
블로그는 두번째 글이지만 난 저걸하기 위해 인프런의 flutter 강좌를 구매했고 너튜브의 flutter 공식 채널을 구독했고 스택오버플로우를 다크모드로 전환하였다(?). 나중에 내 후임쨩이 봤을때 "이색기 개판으로 짰네"라는 소리는 아무리 열심히해도 듣겠지만 그래도 아주 조금은 덜 들어야되지않을까?
(이래서 똑똑한 아이는 싫다니까)

Bloc과 StreamBuilder의 관계

이제 슬슬 머리가 아파진다. 보통 Flutter에서 API를 사용할때 Bloc패턴을 많이들 사용한다고한다. 이제 구글쨩은 Provider인가 그걸 권장한다고 하지만, 솔직히 찾아봐도 잘 모르겠다. 그래서 Bloc 패턴으로만 일단 해보려고한다. 이때 당시 앱을 일주일내로 하라는 얘기가있어서 딱히 시간이없었다.
(그땐 엄청 간단하게 말하길래 처음해봐도 일주일안으로 될줄알았지......그치만 레이아웃짜고 API연동만 일주일...시발 그리고 신나는 버그파티)
그래서 Bloc으로 짜고 StreamBuilder로 부를려고한다. 그냥 그렇게 생각하자...

1. 일단 구조를 그리자

분기를 어떻게 나눌건데?
기획없는 개발은 개발하는 사람이 일단 로직을 그리고 시작하는것이다.
(물론 기획있는 개발을 해도 그리긴해야된다. 사실 기획있는 개발을 해본적이없음.)
일단 요구 사항을 듣고 내 스스로 로직을 그려본다.(아이패드 개좋다구요!!!!)

(엄청엄청 초반의 그림)

(요구사항도 점점 많아지고 나도 알아보기 힘들어서 다시 그림)

이렇게 대충~ 내가 알아볼 정도로만 그려놓고 분기를 파악한다.
어차피 볼사람이 없다 나 혼자만드니까....ㅠ
지금은 저 그림의 작업 start후 status 분기점을 한꺼번에 처리하는 걸 만들것이다.

2. 백엔드놈한테 API를 받아내자

백엔드한테 API를 받아내자 나도 백엔드하고싶은데 혼자만 백엔드해서 얄미우니까 놈이라고 칭하겠다.
(그 놈이 팀장님인건 안비밀 사실 이 블로그 보고있을것임)
받은 API를 Postman을 써서 response 를 확인한다.
이때, 최대한 데이터가 많이 나오는걸 골라서 봐야된다.

3. 응답 json을 dart로 바꾸자

뭐 바로 api 굴려서 String으로 받아서 data['key'] 이렇게 처리하는 방법도 있지만, 어차피 이 응답값은 재사용도 많고 좀더 클-린하게 처리하기 위해 굳이 class로 지정해서 사용하겠다.
나는 lib안에 models dir을 만들어 이런식으로 따로 정리해놓았다.
(뒤에 model붙은건 내부 db table, post는 api post, get은 api get으로 받은것)
이렇게 정리하는게 맞는건지 잘모르겠다. 그냥 순전히 정리충...

// [workStatus_get.dart]
class WorkStatusGet {
  String result;
  List<Data> data;

  WorkStatusGet({this.result, this.data});

  WorkStatusGet.fromJson(Map<String, dynamic> json) {
    result = json['result'];
    if (json['data'] != null) {
      data = new List<Data>();
      json['data'].forEach((v) {
        data.add(new Data.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['result'] = this.result;
    if (this.data != null) {
      data['data'] = this.data.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Data {
  int workId;
  int placeId;
  int uploadId;
  int downloadId;
  int cargoId;
  String placeIdWorkType;
  int placeIdProjectId;

  Data(
      {this.workId,
        this.placeId,
        this.uploadId,
        this.downloadId,
        this.cargoId,
        this.placeIdWorkType,
        this.placeIdProjectId});

  Data.fromJson(Map<String, dynamic> json) {
    workId = json['workId'];
    placeId = json['placeId'];
    uploadId = json['uploadId'];
    downloadId = json['downloadId'];
    cargoId = json['cargoId'];
    placeIdWorkType = json['placeId__workType'];
    placeIdProjectId = json['placeId__projectId'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['workId'] = this.workId;
    data['placeId'] = this.placeId;
    data['uploadId'] = this.uploadId;
    data['downloadId'] = this.downloadId;
    data['cargoId'] = this.cargoId;
    data['placeId__workType'] = this.placeIdWorkType;
    data['placeId__projectId'] = this.placeIdProjectId;
    return data;
  }
}

처음엔 구글보면서 하나하나 쳤는데 고맙게도 json을 넣으면 자동으로 dart로 만들어주는 사이트가 있었다.
그래서 반드시 데이터가 가장 많이 나오는 json응답값을 집어넣어야댐..
이 친구는 복사된 값만 보고 만들어주거등....
https://javiercbk.github.io/json_to_dart/

여기서 생성하고 복붙하면된다.

4. 드디어 Bloc을 만들어보자!

일단 api를 좀더 쉽게 접근하기 위한 패키지 하나 설치해준다
https://pub.dev/packages/http

bloc은 따로 bloc 폴더에 넣어주고 사용하는 점 잊지말자! 모듈모듈모듈화
보기 편하게 실행 순서대로 적었다.

// [workStatus_get_bloc.dart]
import 'dart:convert';

import 'package:b2caremobile/models/workStatus_get.dart';
import 'package:b2caremobile/services/initDB_helpers.dart';
import 'package:http/http.dart' as http;
// pubspec.yaml에 http 패키지 설치하는거 잊지말깅
import 'package:rxdart/rxdart.dart';


class WorkStatusGetBloc {
  final _workStatusSubject = BehaviorSubject<WorkStatusGet>();
  // 3번의 json to dart로 만든 class 타입으로 BehaviorSubject를 만들어주자

  WorkStatusGetBloc() {
  // 다들 이게 생성자인건 RGRG? 모르면 자바 공부하고오센~~
    fetch();
  }
  
  void fetch() async {
  // 그다음 이게 실행되겠지
    var workStatusResult = await getWorkStatusAPIData();
    // 반드시 Future 함수가 값을 반환하고 그 다음으로 넘길려면 await를 써준다
    _workStatusSubject.add(workStatusResult);
  }
  
  Future<WorkStatusGet> getWorkStatusAPIData() async {
    // WorkStatusGet타입으로 반환하는 Future 함수를 만들거애오
    // *** 이 부분은 내부 db값을 가져와서 처리하는 부분인데 모듈화할거임...너무 재사용을 많이함..
    var carId;
    var deviceId;

    var result = await InitDBHelper().getData(1) ?? Null;
    for(var items in result) {
      carId = items.car;
      deviceId = items.device;
    }
    // *** 나중에 알아보도록하자 일단, 내부DB에서 값을 가져온다~ 정도만 알아둔다.
    // API get 방식으로 접근하자
    http.Response response = await http.get(
        Uri.encodeFull('여기에url을넣으시오?deviceId=${deviceId}&carId=${carId}'),
        headers: {'Accept': 'application/json'}
    );
    var data = WorkStatusGet.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
    // WorkStatusGet클래스에 fromJson 함수를 이용하여 값을 저장한다
    return data;
  }

  Stream<WorkStatusGet> get workStatusResult => _workStatusSubject.stream;
  // WorkStatusGet 타입의 stream도 만들어준다
}

5. Widget에 적용해주자(SteamBuilder 사용)

// [work_screens.dart]

final workStatusGetBloc = WorkStatusGetBloc();
// 만들었던 bloc 호출


Widget build(BuildContext context) {
    // bloc이 WorkStatusGet 타입이니 이것도 같은 타입으로 지정해준다
    return StreamBuilder<WorkStatusGet>(
        stream: workStatusGetBloc.workStatusResult,
        // bloc의 stream을 불러주자
        builder: (context, snapshot) {
          if (snapshot.hasData) {
          // api의 데이터가 존재 할 경우
            var status = snapshot.data.result;
            // 상태값을 받아서 분기를 내준다
            if (status == '초기')
              return RegisterWorkLog();
            else if (status == '작업대기' || status == '하차완료')
              return UploadScreen();
            else if (status == '상차상태')
              return DownloadScreen();
            else
              return CircularProgressIndicator(); 
              // 상태값이 해당하는게 없을경우 프로그래스바 보여줌
          } else {
          // api의 데이터가 존재하지 않을경우 (내부 DB의 값이 null일 경우)
            return RegisterInitial();
            // 초기 등록 페이지를 보여준다.
          }
       }
    );
}

완성화면

참고 :
1. https://www.inflearn.com/course/flutter_%EC%A4%91%EA%B8%89/dashboard
2. https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html

profile
Python이 하고싶은데 자꾸 Flutter 시켜서 빡쳐서 만든 블로그

0개의 댓글