티스토리 뷰

현재 사용자에게 받는 input에 따라 질문은 바뀌는데, 선택지는 바뀌지 않고 있네요

우선은 질문과 선택지들이 서로 묶음으로 있어야겠네요!

방법은 여러가지가 있을수 있겠는데, 가장 바람직한건 model을 따로 분리하는게 좋습니다

다만 지금은 dart라는 언어를 새로 배우는 입장이다보니 map 데이터 구조를 이용하려합니다

https://api.dart.dev/stable/2.10.4/dart-core/Map-class.html

map은 사전입니다. 파이썬에서는 dict라고도 하죠

정말 간단한 데이터를 만들어봤어요

var _questions = [
    {
      "question": "가장 좋아하는 색깔은 무엇인가요?",
      "answer": [
        "빨강",
        "검정",
        "초록",
      ]
    },
    {
      "question": "가장 좋아하는 과일은 무엇인가요?",
      "answer": [
        "사과",
        "수박",
        "딸기",
      ]
    }
  ];

그리고 바뀐 questions를 반영하기 위해

Question(_questions[_questionIndex]["question"]), 로 바꿔줍니다.

이제 질문들은 어느정도 정리되었고, 질문은 Question 위젯으로 나눴던것 처럼, 선택지 역시 위젯으로 분리를 할게요.

answer.dart파일을 만들어주고 (Question을 만들었던과 같은 흐름으로 갈게요)

먼저 stateless 위젯을 상속받는 class를 만들어주시고

import도 해줍니다.

Answer 클래스의 property로는 선택지의 텍스트에 들어갈 String값과, 눌렀을때 일어날 행동인 Function이 필요하겠네요. 이 둘은 한번 정해주면 바뀌는 일이 없을테니 final 키워드를 앞에 붙여줄게요

완성된 클래스의 코드는 다음과 같습니다

import 'package:flutter/material.dart';

class Answer extends StatelessWidget {
  final String answerText;
  final Function answerHandler;

  Answer(this.answerText, this.answerHandler);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: answerHandler,
      child: Text(answerText),
    );
  }
}

이제 새롭게 만든 Answer 클래스를 적용하러 가볼까요?

다시 main.dart파일로 가서 기존에 RaisedButton을 Answer로 바꿔줄게요. IDE의 도움을 받으면 import가 자동으로 이루어집니다.

우선은 answerText parameter는 "선택지!!"로 하드코딩할게요

        body: Column(
          children: [
            Question(_questions[_questionIndex]["question"]),
            Answer("선택지!!", _answerQuestion),
            Answer("선택지!!", _answerQuestion),
            Answer("선택지!!", _answerQuestion),
          ],
        ),

핫 리로드를 해주면...!

이제 질문에 해당되는 선택지를 보여주게 만들어줄게요.

그런데, 지금 당장은 선택지가 3개인데, 4개 혹은 5개일수도 있겠죠?

그래서 선택지의 갯수를 하드 코딩 하지않고 동적으로 선택지가 보여줄수 있게 바꿔줄게요

map

예제

https://api.dart.dev/stable/2.10.4/dart-core/Iterable/map.html

먼저 map에 대해서 간단하게 설명을 해볼게요. Iterable을 받아서 Iterable을 리턴한다고 하는데...

역시 예제 코드를 한번 보는게 좋겠지요?

저에게 사람의 이름이 담겨 있는 String 배열이 있고 (var names = ["foo","bar"];), 모두 Person 이라는 객체로 바꿔주고싶은 상황입니다.

class Person {
  final String name;
  Person(this.name);
}

void main() {
  var names = ["foo","bar"];
  var people = names.map((name){
    return Person(name);
  }).toList();
}

이때 배열 뒤에 map 메소드를 호출해주고, 각 원소를 name이라고 불러줄거에요
그 후 리턴하는값은 Person객체이며 name parameter에 방금 name을 넣어줍는겁니다.

그리고 다 변환시켜주면 Person이 담긴 배열로 바꿔주기위해 .toList()를 뒤에 붙여줍니다.

자 이제 퀴즈앱의 main.dart로 돌아오겠습니다
위의 예제처럼 List에서 map을 쓰고싶은 상황이죠

그런데 _questions[_questionIndex]["answer"] 이렇게하면 해당값이 배열이라는것을 Dart는 추론하지 못합니다.

그래서 (_questions[_questionIndex]["answer"] as List)를 해줘서 "이거는 배열이야!"라고 알려줘야합니다.

(_questions[_questionIndex]["answer"] as List<String>)
.map((answer) {
  return Answer(answer, _answerQuestion);
})

그러면 위의 예제에서는 Person을 return 한것 처럼 이번에는 Answer를 return 해줄게요

(_questions[_questionIndex]["answer"] as List<String>)
.map((answer) {
  return Answer(answer, _answerQuestion);
}).toList()

그리고 마지막으로 .toList()를 붙여줘서 배열로 바꿔줍니다.

 

 

그런데 아직도 오류가 뜨는데요

지금 현 상황을 간단하게 나타내면

children : [
    Question,
    [Answer, Answer, Answer]
]

이렇게 위젯이 배열로 감싸져있습니다..

제가 원하는것은 아래인데...

children : [
    Question,
    Answer,
    Answer,
    Answer
]

이러한 상황에서 유용하게 쓰일수 있는게 spread operator입니다.

https://dart.dev/guides/language/language-tour#spread-operator

...(_questions[_questionIndex]["answer"] as List<String>)
                .map((answer) {
              return Answer(answer, _answerQuestion);
            }).toList()

요렇게 앞에 ...만 붙여주면 배열안에 있는 객체들을 "뿌려"줍니다.

Restart를 한번해주고 앱이 잘되는지 한번 보면..

짠 ! 원하는 바처럼 작동합니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함