Flutter 1부터 배우기 : 나만의 퀴즈 앱 -5 [named parameter]

개발하자 백조·2023년 1월 5일
1
post-thumbnail

아직 4편 setState 를 못 봤다면? 보러가기 👀


반갑습니다 여러분! 코딩백조입니다🦢

지난 시간에 화면 넘기는 로직에 대해 말하다가, 변수 넘기는 법에서 끊겼죠?

이번 시간에는 그 방법에 대해 소개하고 실제로 적용해보겠습니다.
음.. 이 개념은 끊어서 가기에 애매해서 조금 길게 가도록 하겠습니다.

시간 없으니 바로 시작!!

📍 [ 5 / 7 ] index 사용해서 화면 동작시키기

정답을 공개할게요.
변수나 함수를 다른 위젯으로 "넘길" 수 있는 방법!!

바로 Named Parameter 입니다!

Named Parameter

Parameter(매개변수)는 함수를 정의할 때 사용되는 변수에요.

이 경우, add 라는 함수의 매개변수는 a 와 b 입니다!

다트 언어에서는 세 종류의 parameter 가 존재한답니다! 플러터에서는 위젯에도 매개변수를 받을 수 있어요. 😲

매개변수의 종류로는 positional, named, 그리고 optional 이렇게 세 개가 있습니다. 😌

positional 은 아무 이름 없이 변수를 넣어주는 것, 예를 들어 Text() 위젯에 있는 데이터를 말합니다!

Text(data)
// String data
Text("Hi everyone!")

"Hi everyone!" 을 Text 위젯 안에 바로 넣어주었죠?
들어갈 위치가 정해져있다 해서 positional parameter 라고 불러줍니다. 가장 기본적인 형태죠!

named 는 이름, label 을 붙이고 넣어주는 것입니다. ElevatedButton 에 넣어주는 요소는 named 이죠!

ElevatedButton(onPressed: onPressed, child: child)
//Function? onPressed, Widget? child
ElevatedButton(onPressed: (){}, child: Text("Hello"))

항목 이름 뒤에 : 을 통해서 값을 넣어주는 경우, "이름이 있다" 해서 named parameter 라고 불러줍니다! 😎 저희는 변수를 명확히 보기 위해 이걸 쓸게요!

optional 은 넣어도 되고 안넣어도 되는 변수를 의미합니다.
중요하지 않으니 패스!


왜 named parameter 을 쓰냐구요?
필수 값이 2개 이상인 경우에는 개인적으로 이름을 부여해야 값을 넣을 때 헷갈리지 않더라구요. 😮‍💨

매개변수를 쓰면 값을 다른 위젯으로 넘길 수 있다고 했잖아요,
더 정확히 말하면, 내 위젯 내부에 선언하는 위젯으로 넘길 수 있습니다!

지금 우리의 MyApp 에는 어떤 위젯이 있는지 볼까요? 👀

build 안의 내용을 보니,
Scaffold, AppBar, Text, QuizScreen 이렇게 4개의 위젯이 있네요!

이 위젯들에게는 아까 만든 questionIndex 라는 변수나, 버튼을 눌렀을 때 questionIndex 에게 1을 더해주는 함수를 넘겨줄 수 있게 된거에요! 🤩

그렇다면 바로 적용을 해봅시다!!


지난 번에 만들었던 체크리스트에요.

1. 0 이라는 숫자를 하나 만들고, ( int questionIndex = 0; )
2. 대답 버튼을 누를 때마다 questionIndex 에 1씩 더한 다음에,
3. 우리의 질문/대답 리스트에서 'questionIndex' 번째의 항목을 가져오고,
4. 그걸 기준으로 화면을 다시 그리기

바로 시작할게요!
📍 2번: 대답 버튼을 누를 때 저 숫자에 1씩 더할 차례에요. 🤓

대답 버튼 내부의 onPressed: 에다가 "questionIndex 에 1씩 더하는 동작"을 정의해야겠죠?

그런데 questionIndex는 MyApp 내부에서만 쓸 수 있는 변수기 때문에 QuizScreen 내부에 있는 버튼으로 넘길 수 없어요.

그렇기 때문에, MyApp 내부에서 questionIndex 에 1을 더해주는 함수를 만들고,
그걸 named parameter 를 통해서 QuizScreen 으로 넘겨줄게요!


먼저, 함수를 만들어줄게요. 이 동작은 버튼을 눌렀을 때 숫자를 증가시켜 주는 함수니까...
answerPressed 정도로 만들어줄게요! answered, 아니면 buttonPressed 처럼 원하는 이름으로 해도 돼요. 😉

앞으로 플러터에서 내가 정의하는 함수를 만들 일이 정말 많을거에요.
그 중 가장 간단한 형태의 함수 만드는 법을 알려드릴게요! 😎

void 함수 만들기

void 는 빈 공간, 무효 라는 뜻을 가지고 있습니다.
함수가 실행되기만 하고 어떤 결과값을 돌려주지 않는 경우 void 함수를 씁니다.

지금 우리는 어떤 값을 만드는게 아니라 그냥 숫자를 더해주기만 할거라서 void 함수를 만들어 줄게요!

main.dart 안에서, 지난 시간에 만든 questionIndex 밑에다가 적어주세요!

저에게는 20번째 줄이 되었어요.

answerPressed 라는 이름의 void 함수를 만들었고, (매개변수) 없이, {동작}도 없이 만들어 주었습니다.

이제 questionIndex 에 1을 더해줄게요.
어떤 동작을 할 지는 중괄호 내부에 써주시면 돼요!

void answerPressed(){
  questionIndex = questionIndex + 1;
}

//or

void answerPressed(){
  questionIndex++;
}

여러분이 편하신 대로 해주세요! 1을 더해주는 방법은 여러가지가 있지만 뭐든 상관 없답니다. 😉
숫자가 잘 더해지는지 보기 위해 print 문을 더해줄게요!

void answerPressed(){
  questionIndex++;
  print(questionIndex);
}

이렇게 하면 버튼을 누를 때마다 현재의 questionIndex 값을 콘솔에 프린트 해줄거에요.
나름의 디버깅이죠! 에러가 났을 때 뭐가 문제인지 더 쉽게 확인할 수 있습니다 ㅎㅎ


함수를 만들었다면 매개변수로 넘겨주겠습니다.

처음에는 이게 뭘까 싶은데, 하다보면 적응 되니 이해가 되지 않더라도 따라오세요!
이 작업은 앞으로도 많이 할거라서 상관 없습니다. 😎

quiz_screen.dart 로 이동할게요 💨

이 부분을 건드릴겁니다!

일단, 우리가 받고 싶은 값은 뭐죠? 바로 answerPressed 라는 "함수"죠!
6번째 줄에 final Function answerPressed; 라고 써줄게요.
(전 앞뒤로 엔터를 넣어서 보기 좋게 했어요)

이 줄은 변하지 않는 함수를 받아서 answerPressed 라는 이름으로 불러줄거다! 라는 뜻이에요.
우리가 main.dart 에서 만든 함수와 이름이 달라도 상관은 없지만, 전 보기 쉽게 통일해줄게요. 😌

그 다음, 5번째 줄의 ({ Key? key }) 부분에다 매개변수를 넣어줄거에요.

({Key? key, required this.answerPressed})

required : 너 무조건 이거 넣어줘야 돼!!
this.answerPressed : 이 위젯 안에 있는 answerPressed 라는 변수에다 저장해줄게.

라는 뜻입니다!
위젯을 선언할 때 named parameter 로 함수를 받아서 아까 우리가 정의한 final Function answerPressed; 변수에 저장한다는 뜻이에요. 😲

지금까지의 모든 작업이 끝났으면 이런 식으로 보일거에요!

이제 우리가 받은 answerPressed 함수를 우리의 ElevatedButton 에 넣어줘볼까요? 😉
쉬울거에요.
버튼 내부의 onPressed: 부분에다가 넣어주기만 하면 되거든요!

onPressed: () {
  answerPressed();
},

// or

onPressed: () => answerPressed(),

둘 중 하나를 골라서 넣어주시면 됩니다! 🤩
더 편한 방법으로 하시면 돼요.

모든 ElevatedButton의 onPressed: 부분을 바꿔주세요!
Text() 안이 무의미하지만, 일단은 무시할게요.

자, 모두 바꿨으면...

오잉? 갑자기 main.dart 에 에러가 떴어요! 😱

한 번 확인하러 가볼게요.
커서를 올려보니 이런 문구가 뜨네요!

named parameter 가 없어서 생긴 에러네요!
그쵸, 우리가 required 라고 말 했는데 없으니까 당연한 에러일거에요. 😮‍💨

여기에 함수를 넘겨주면 되겠죠?
이 영상을 따라해보세요!

에러 메세지에서 "Add required argument ~~" 부분을 눌러서 매개변수 label 을 띄워주고, 우리가 넘길 함수 이름인 answerPressed 를 적어줄게요!

여기서 뭔가 다른걸 눈치 챈 사람 계신가요? 😝
제 영상 보면 항상 엔터를 쳐서 전체를 다 받았는데, 이번엔 엔터를 치지 않았어요.

엔터 치면 이렇게 되거든요.

자동으로 answerPressed 뒤에 () 가 붙게 되고, 그러면서 오류가 생겨요.

우리는 answerPressed: 에다가 함수를 넘겨줘야 하잖아요,
근데 함수 이름 뒤에 () 를 붙여주면 "실행한다" 라는 뜻이 추가돼서 결국 그 함수의 결과값이 넘어가게 되어있어요.

answerPressed() 함수는 어떤 결과값을 주죠? 네, void 라서 아무것도 없어요.
그래서 저 에러는

엥! 너 함수 준다며!! 왜 void 를 주는거야!!! 😡

라고 외치고 있는거죠. 😅

아무튼! 저 괄호를 지워주면 문제 해결입니다.

여기까지 했으면 Run ▶️ 해볼까요?
앱 화면에 있는 버튼을 하나씩 눌러볼게요!

똑같진 않아도 이런 식으로 버튼을 누를 때마다 1 2 3 4 ... 가 출력되면 정상입니다!! 🥳

아직 Text() 의 내용을 수정하지 않아 화면이 변하지는 않지만,
print(questionIndex); 의 값이 1씩 증가하는 동작은 잘 작동하는게 보이네요!!

우리의 체크리스트에서 절반이 끝났어요!!

1. 0 이라는 숫자를 하나 만들고, ( int questionIndex = 0; )
2. 대답 버튼을 누를 때마다 questionIndex 에 1씩 더한 다음에,
3. 우리의 질문/대답 리스트에서 'questionIndex' 번째의 항목을 가져오고,
4. 그걸 기준으로 화면을 다시 그리기

다음 시간에는 3번과 4번을 통해 다음 질문으로 넘기는 기능을 만들겠습니다.

오늘이 제일 긴 거 같네요.. 여기까지 오시느라 수고 많으셨습니다! 🥰


main.dart

import 'package:flutter/material.dart';
import 'package:personal_quiz/screens/quiz_screen.dart';

void main() {
  runApp(
    const MaterialApp(home: MyApp()),
  );
}

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

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int questionIndex = 0;

  void answerPressed() {
    questionIndex++;
    print(questionIndex);
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("나만의 퀴즈 만들기"),
      ),
      body: QuizScreen(
        answerPressed: answerPressed,
      ),
    );
  }
}

quiz_screen.dart

import 'package:flutter/material.dart';
import 'package:personal_quiz/question_list.dart';

class QuizScreen extends StatelessWidget {
  const QuizScreen({
    Key? key,
    required this.answerPressed,
  }) : super(key: key);

  final Function answerPressed;

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: [
          const SizedBox(height: 100),
          Text(questionList[0]["questionText"]),
          const SizedBox(height: 150),
          ElevatedButton(
            onPressed: () => answerPressed(),
            child: Text(questionList[0]["answers"][0]["text"]),
          ),
          ElevatedButton(
            onPressed: () => answerPressed(),
            child: Text("대답 2"),
          ),
          ElevatedButton(
            onPressed: () => answerPressed(),
            child: Text("대답 3"),
          ),
          ElevatedButton(
            onPressed: () => answerPressed(),
            child: Text("대답 4"),
          ),
        ],
      ),
    );
  }
}
profile
개발자로서 100가지 일을 해보고 싶은 조경현의 개발 블로그

0개의 댓글