반응형

0. 들어가기

-. flutter에서 json object를 사용해본다.

-. map 함수를 이용해서 다수의 widget을 추가해본다.

1. json object를 이용한 퀴즈 출력

-. json 형태로 이루어진 퀴즈를 작성해보자. DnD 성향 테스트를 예시로 작성해봤다. 출처링크

1) json object 선언

-. 파이썬의 dict 형에 너무 익숙해져서 js에서 json을 사용하려니 매우 힘들었다. Map으로 구조를 만들어가면서 선언해야 한다..

//dnd Alignment object 선언
  Map<String, Map<String, Object>> dndAlignment = {
    "1": {
      "question":
          "Family elders are expressing disapproval of you to the rest of the family. Do you:",
      "answers": {
        "1": "Accept the criticism and change your ways?",
        "2": "Seek a compromise with them?",
        "3":
            "Besmirch the reputation of those expressing disapproval as you ignore their scorn?",
        "4": "Silence them any way you can?"
      }
    },
    "2": {
      "question":
          "Would you give up a promising career to aid the family in time of need?",
      "answers": {
        "1": "In a heartbeat.",
        "2": "Yes, with some reluctance.",
        "3": "Only if I was certain I'd be able to return to my career soon.",
        "4": "No."
      }
    }
  };

2) 질의응답 만들기

-. 우선 문제 번호를 questionIndex로 설정하고, 버튼 클릭 시 해당 값을 1씩 더하는 함수를 만들었다. (앞에 것 재활용)

//퀴즈번호 생성
  int questionIndex = 1;

  //버튼 클릭 시 다음 번호로
  void buttonClicked() {
    setState(() {
      questionIndex += 1;
    });
  }

-. 다음으로, 문제 텍스트와 답변 텍스트들을 뽑아서 body에 문제와 답변을 출력해야 하는데 json 안의 value로 json이 들어가 있어서 조금은 지저분한 변수 호출이 된다.

body: Column(
            children: [
              Question(dndAlignment["$questionIndex"]?['question'] as String),
              ElevatedButton(
                onPressed: buttonClicked,
                child: Text((dndAlignment["$questionIndex"]?['answers']
                    as Map<String, String>)["1"] as String),
              ),
              ElevatedButton(
                  onPressed: buttonClicked,
                  child: Text((dndAlignment["$questionIndex"]?['answers']
                      as Map<String, String>)["2"] as String)),
              ElevatedButton(
                  onPressed: buttonClicked,
                  child: Text((dndAlignment["$questionIndex"]?['answers']
                  as Map<String, String>)["3"] as String)),
              ElevatedButton(
                  onPressed: buttonClicked,
                  child: Text((dndAlignment["$questionIndex"]?['answers']
                  as Map<String, String>)["4"] as String)),
            ],
          )),

-. 그럭저럭 출력은 된다. (매우 허접해 보이지만)

3) 개선해야 할 점들

-. 당장 몇가지 문제가 보인다.

1) 답변의 길이에 따라 버튼 폭이 달라짐

2) 문제 번호가 넘어가면 에러 발생 (아래 영상 참고)

3) 그리고 코드 측면에서 각 답변 (1~4) 번호를 직접 입력해서 구현했기에 코드가 지저분할 뿐 아니라, 답변 갯수가 달라지거나 하면 에러가 발생할 것이다.

2. map으로 답변 버튼 출력

-. javascript엔 배열로부터 새로운 배열을 만들어주는 map 함수가 있다. 예제링크

-. dart에서도 동일하게 사용할 수 있으니 함 시도해보자.

1) 답변 버튼 위젯 만들기

-. 우선 코드를 정리해서 답변 버튼을 출력하는 answer 위젯을 만들었다. 하는김에 위젯 폭과 색도 조금 수정했다.

answer.dart

import 'package:flutter/material.dart';

class Answer extends StatelessWidget {
  final String answer;
  final VoidCallback quizHandler;

  Answer(this.answer, this.quizHandler);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    // return Text(question);
    return Container(
      width: double.infinity,
      alignment: Alignment.centerLeft,
      margin: const EdgeInsets.all(10),
      // color: Colors.blue[200],
      child: ElevatedButton(
        onPressed: quizHandler,
        style: ButtonStyle(
          minimumSize: MaterialStateProperty.all(const Size(double.infinity, 40)),
          backgroundColor: MaterialStateProperty.all(Colors.red),
          // foregroundColor: MaterialStateProperty.all(Colors.transparent),
        ),
        child: Text(
          answer,
          style: const TextStyle(fontSize: 20),
          textAlign: TextAlign.left,
        ),
      ),
    );
  }
}

----
main.dart

import './answer.dart';

...
body: Column(
            children: [
              Question(dndAlignment["$questionIndex"]?['question'] as String),
              Answer((dndAlignment["$questionIndex"]?['answers']
              as Map<String, String>)["1"] as String, buttonClicked),
              Answer((dndAlignment["$questionIndex"]?['answers']
              as Map<String, String>)["2"] as String, buttonClicked),
              Answer((dndAlignment["$questionIndex"]?['answers']
              as Map<String, String>)["3"] as String, buttonClicked),
              Answer((dndAlignment["$questionIndex"]?['answers']
              as Map<String, String>)["4"] as String, buttonClicked),

            ],
          )

2) map 함수 사용하기

-. 정리된 코드에서는 answers의 키("1" ~ "4") 를 제외하고는 모두 동일한 코드로 Answer 위젯을 호출한다. 조금 더럽지만 깔끔한 (...) 코드가 되었다.

children: [
              Question(dndAlignment["$questionIndex"]?['question'] as String),
              ...(dndAlignment["$questionIndex"]?['answers']
                      as Map<String, String>)
                  .keys
                  .map((answerNum) {
                return Answer(
                    (dndAlignment["$questionIndex"]?['answers']
                        as Map<String, String>)[answerNum] as String,
                    buttonClicked);
              }).toList(),
            ],

-. 이제 답변 갯수가 몇개든 대응 가능한 앱이 만들어졌다. (문제를 3번까지로 늘리고) 테스트를 해보면 2번의 5개 답변 / 3번의 3개 답변에 대해 의도한 대로 대응하는 것을 확인할 수 있다.

-. 하지만 여전히 문제 배열을 벗어나면 에러가 발생하는 문제가 남아있다. 다음 포스트에서 해결하자..

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기