- 이번에 해 볼 것 -
- onPress Gesture의 이해와 활용
- [Dart] 변수와 함수에 대한 이해
- Gesture에 따라 이미지 변화
- Stateless, Statefull 위젯의 차이
- [Dart] 조건문(if condition)의 이해
지난번 포스팅에 이어서, 버튼을 클릭하면 이미지가 변경되는 기능을 이번에 구현하고자 한다.
클릭(onTab, onPress)에 해당하는 제스처를 처리하는 방법은 다양하지만, 이번에는 Button 위젯을 써보도록 한다.
참고로, https://docs.flutter.dev/ 사이트에서 플러터의 위젯들을 찾아볼 수 있다.
1. onPress Gesture 처리
클릭 기능 구현을 위해 먼저 고민해야 할 사항이 있다. 바로 터치 인식을 어느 영역에 지정하느냐 이다.
화살표 이미지 위젯을 제스처로 감싼다면, 이미지 영역에만 터치가 먹을 것이고 컨테이너 박스 영역을 제스처로 Wrap하면 박스 전체가 터치가 가능한 영역이 될 것이다.
화살표 이미지 영역을 TextButton 위젯으로 감쌌다. 그런데, 위 화면처럼 컴파일 에러가 발생했다.
참고적으로 안드로이드스튜디오에서는 에러를 여러 방법으로 보여준다. 코드 상에 빨간 물결 밑줄로도 보여주고, 스크롤바에서도 에러가 난 영역을 빨간색으로 보여주고, 파일에도 빨간 물결 밑줄을 치는 등 다양하게 알려주고자 한다.
에러메세지를 보아하니 onPressed라는 속성이 없어서 에러가 난 것 같다.
위 사진처럼 onPressed 속성을 넣어주고, 속성값은 일단 괄호로만 넣어주었다. 그러자, 오류가 사라졌다.
그런데?? 왼쪽과 오른쪽의 화살표 크기가 달라졌다.
왜냐하면 텍스트박스 위젯은 기본적으로 패딩값을 갖고 있다.
* 플러터 인스펙터 툴에서 해당 위젯을 누르면 정확한 값을 찾아볼 수 있다
그렇기 때문에 텍스트박스의 패딩값만큼 크기가 줄어들게 된 것이다.
왼쪽 화살표 크기가 마음에 든다면 그대로 두면 되고, 그게 아니라면 컨테이너위젯의 패딩값을 조절해주면 된다.
저대로는 onPressed 함수가 제대로 기능하는지 알 수가 없다. 기능하는지 알기 위해 print 함수를 넣어주면 좋다.
child: TextButton(onPressed: (){
print('뒤로가기 클릭');
},
child: Image(
color: Colors.indigo,
image: AssetImage('assets/images/prev.png'),),
),
위 코드처럼 프린트함수를 넣어주면, 클릭 시 콘솔창에 메세지가 뜨게 된다. 이를 통해 작동 여부를 알 수 있다.
버튼 클릭 기능 구현은 이렇게 해보았다
2. [dart] 변수
이제는 버튼을 클릭하면 이미지가 변경되도록 해 볼 것이다.
이 때, 아래의 개념들을 이해를 하고 넘어가야 기능을 제대로 만들 수 있다.
- 변수
- 함수
- Stateless, Statefull
(1) 변수에 대한 이해
- 변수란, 개발자 마음대로 데이터를 저장하기 위해 사용되는 공간의 이름이다.
예컨대, burpeeNumber라는 변수를 하나 만들었다고 가정해 보자. 이 버피넘버라는 이름을 가진 공간은 개발자가 그 안에 원하는 숫자를 마음대로 저장할 수 있게 되는 것이다.
'변수' 값이 항상 변하는 숫자를 저장할 수 있는 공간이다.
- 변수의 type은 공간의 칸을 몇 칸 주겠는지를 정하는 것이라고 생각하면 된다. 예를 들어 숫자는 공간이 1칸이면 될 것이고, 문자는 더 많은 칸이 필요할 것이다. 타입에는 다음과 같은 종류가 있다.
int | 정수(소수점 x) (ex> 1, 2, 3, -1, -8) |
double | 실수(소수점 포함) (ex> 3.14, -10.23) |
String | 텍스트(문자) (ex> 'Hello Flutter') |
bool | 참, 거짓 (true, false) |
var | 모든 타입의 값 저장 가능 (단, 할당 후 타입 변경 불가) |
dynamic | 모든 타입의 값 저장 가능 (할당 후 타입 변경 가능) |
- 변수를 사용할 때에는 [선언] 과 [초기화] 의 과정이 필요하다.
선언은 변수의 타입과 변수명을 명시하는 것이다.
초기화는 선언된 변수에 최초로 값을 할당하는 것이다.
- 변수명은 lowerCamelCase를 사용한다.(첫글자 소문자, 중간 대문자)
- 초기화 또는 값 할당 시, 아무것도 아닌 값(NULL)을 넣을 수 있다.
이때 Null은 빈칸 또는 0도 아닌 없는 값이다.
타입 뒤에 붙는 ?의 의미는 null을 저장할 수 있다는 의미이다(Nullable 변수)
- 개발자가 Type을 정의하여 사용할 수 있다. 이를 Class라고 부른다.
예를 들어, 성별/나이/키/몸무게 는 각각 개별로 정수형 또는 스트링 등등의 변수지만
이를 하나로 묶어서 '사람' 이라는 타입을 만들 수 있는데 이것이 Class이다.
Class는 여러가지 기능이 있는데 그 중 하나가 Type으로서 역할을 한다는 것이다.
(2) 버튼 클릭 시 이미지가 변하는 기능을 구현함에 있어서 변수의 의미는 무엇인가
Image(image: AssetImage('assets/images/1.png'),)
이 코드에서 1.png는 성인 남성이 서 있는 이미지다.
다음 버튼을 클릭하면 2.png, 성인 남성이 땅을 손으로 짚은 이미지를 출력하고 싶고,
다음 버튼을 클릭하면 3.png, 성인 남성이 발을 뻗은 이미지를 출력하고 싶다.
1.png, 2.png, 3.png 이렇게 클릭할 때마다 사진파일이 1번, 2번, 3번 순차적으로 변하니.
숫자 영역을 변수로 지정해 두고, 클릭할때마다 변하게 만드는 것이다.
Image(image: AssetImage('assets/images/$burpeeNumber.png'),)
1.png를 $burpeeNumer.png 로 바꿔주었다. 버피넘버라는 변수를 하나 만들 예정이다. 저 변수 안에서 숫자 1~3이 왔다갔다 하는 것이다. String 안에서 변수값을 입력할 때에는 변수명 앞에 $ 표시를 붙여주자.
변수를 선언하기 위해, 좀처럼 방문 않던 메인함수와 스테인레스 위젯 상단부로 이동을 했다.
먼저, const MyApp 아랫줄에 int 변수를 선언했다. 변수명은 burpeeNumber이고, 초기값은 1이다.
그런데 아마 이 상태는 에러가 뜰 것이다.
이 때, MyApp 앞에 있는 const를 지워주면 에러가 없어진다. 이유는 나중에...
변수 초기값을 1 ~ 3으로 변경해보고 핫 리스타트를 해보면, 변수가 잘 먹혔는지 확인 점검이 가능하다.
(3) 버튼 클릭 시 사진이 변경되도록
child: TextButton(onPressed: (){
print('앞으로가기 클릭');
},
앞서 우리는, 버튼이 클릭되면 콘솔에 문구가 출력되는 기능을 코딩한 바 있다.
여기에 버튼이 클릭되면 변수가 변경되도록 넣으면 사진이 변경될 것이다.
child: TextButton(onPressed: (){
burpeeNumber=2;
print('앞으로가기 클릭');
},
하지만, 실제 앱 버튼을 눌러보면 사진은 변경되지 않는다. 번개모양의 핫 리로드 아이콘을 누르면 그제서야 사진은 2번으로 변경될 것이다. 이는, 코드에 변수가 변경되는 내용은 있지만, 그 뒤 화면을 새로고침 하라는 명령을 주는 코드가 없기 때문에 벌어지는 일이다.
3. Statefull, Stateless Widget의 차이점
- StatefullWidget : 사용자 입력에 따라 화면이 업데이트 되어야 하는 경우에 주로 사용(화면 갱신 가능)
- StatelessWidget : 갱신 없이 고정된 정보만 보여줌, 단순하고 가볍고 빠름
스테이트풀 위젯은 크게 위젯 / 스테이트 두 가지의 클래스로 나뉜다. 반면 스테이트레스 위젯은 위젯 하나만으로 구성되어 있다.
코드로 돌아가서, 최초에 스테이스레스 위젯으로 만든 것을 스테이트풀 위젯으로 바꿔줘야 한다.
클래스 뒤 내 앱 이름에 커서를 대면, 전구 아이콘이 뜬다.
그 뒤 Convert to StatefullWidget을 누르면 변환할 수 있다.
변환을 클릭하게 되면 이렇게 위젯 부분과 스테이트 부분이 구분되어 변환이 된다.
이제는 아까 버튼을 클릭하면 화면을 갱신할 수 있도록 명령을 부여할 수 있다.
setState(() {
burpeeNumber=2;
});
셋스테이트는 괄호 안이 실행되면 화면을 갱신하라는 의미이다.
아까 버피넘버를 2로 바꾸라는 코드를 이것으로 씌워주면 된다.
버튼 누르면 화면이 갱신되도록 하는 방법은 이렇게 하면 된다.
4. [dart] 함수
함수는 여러 명령(연산과정)을 묶어서 하나의 실행단위로 만드는 것이다. 함수는 코드를 재사용 할 수 있게 만들기(=모듈화)가 좋다.
[함수의 구조]
1 | return type | 함수의 시작. int, string, dynamic 등 타입을 정함. |
2 | 함수이름 | 함수를 뭐라 부를지 개발자가 지정. |
3 | 파라미터(매개함수) | 함수에 들어가는 Input. 소괄호 영역. 비어있거나, 지정되어있기도 함. |
4 | 함수body | 중괄호 영역. 여러 줄의 실행코드를 모아놓는 곳 |
5 | return 값 | 함수의 Output. 마지막 반환점(값). |
6 | 함수호출 | 1~5번을 통해 정의된 값을 함수명을 쓰고 소괄호를 여닫음으로써 함수를 실행 |
1~5번 요소 중 때에 따라, 함수에 따라 생략되는 경우들도 있다. 앞선
onPressed: (){
setState(() {
burpeeNumber=2;
});
이제 우리가 버튼을 누르도록 동작할때 했던 코딩들도 함수임을 알 수 있을 것이다. 하지만 이 함수는 버튼을 암만 눌러도, 2번 화면만 보여줄 뿐이다. 버튼을 누를 때마다 화면이 변경되도록 하려면 함수가 1번 수행될 때 마다, 숫자가 1씩 늘어나도록 만들어주면 버튼을 누를 때 마다 사진이 변경될 수 있다.
(기본값 = 1, 한번 누르면 +1되어서 2, 두번째에는 +1되어서 3번 사진이 나오는 원리이다)
extButton(onPressed: (){
setState(() {
burpeeNumber=burpeeNumber + 1;
});
print('앞으로가기 클릭');
},
이제는 클릭했을 때, 버피넘버 함수에 2번 값을 넣는 것이 아니라, 플러스 1을 하는 방식으로 코드를 변경하였다.
이렇게 바꾸고 나면 클릭하는 족족 화면이 변경될 것이다.
그리고... 그러다가 어느 순간에
우리는 이런 화면을 맞이하게 된다. 왜냐하면 이미지파일이 7번까지밖에 없기 때문에 8.png 부터는 출력할 수 없기 때문이다. 이를 해결하기 위해서는 7 이후에는 암만 버튼을 눌러도 값이 커지지 않게 해야 할 것이다.
5. [dart] 조건문(if condition)
앞선 코드에서 우리는 7 이후로는 숫자가 늘어나지 않는 조건을 주어야만 했다.
엑셀에서 한번쯤은 접해보았을 if함수 조건. 다트에서도 비슷한 개념이다.
[if 조건문의 구조]
if(조건식){
print('조건식이 참이면 이 안의 텍스트가 출력됨')
}
(예시1 : 참,거짓)
int age = 15; //age라는 변수에 15라는 데이터가 들어가 있을 때
if(age<19){
print('미성년자 구매불가') // age가 19 미만일 때, print 안의 내용을 출력
} else{
print('맛있게 드세요');
}
-> if가 true이므로, 미성년자 구매불가 라는 if문의 body가 실행이 된다.
만일 if가 false라면, 맛있게 드세요 라는 else문의 body가 실행된다
(예시2 : 다중조건)
int age = 55; //age라는 변수에 55라는 데이터가 들어가 있을 때
if(age<19){
print('미성년자 구매불가') // age가 19 미만일 때, print 안의 내용을 출력
} else if(age<=50){
print('맛있게 드세요');
} else if(age<=60){
print('요건강주의');
} else{
print('약주는 조금만');
}
-> if가 거짓이므로, 미성년자 구매불가 는 출력되지 않고 다음으로 넘어간다.
첫번째 else if문 age가 50이하도 거짓이므로, body가 실행 없이 다음으로 넘어가며
두번째 else if문이 참이므로, 요건강주의 라는 문구가 출력되며 if문이 종료된다.
만일 else if 모두 false라면 그제서야 약주는 조금만 이라는 else문의 body가 실행된다
위 예시의 비교와 함께 자주 쓰이는 것이 논리연산자이다.
기호 | 명칭 | 내용 |
a && b | and | a 그리고 b. 둘 다 참이어야 참이 된다. (ex> 9>10 && 11>10 is false) |
a || b | or | a 또는 b. 둘 중 하나가 참이면 전체가 참이다. (ex> 9>10 || 11>10 is true) |
!a | Not | a의 반대값. (ex> 7>10 is true) |
(예시)
int? a=3; //?가 있으므로 nullable
int? b=4;
int? c; //null이 초기값이 된다.
bool d=false; //초기값은 false이며 물음표가 없으므로 null이 못들어간다
if(a==b && c== null){
print('a값과 b값이 같고, c가 null이 아닐 경우 실행');
} // 이 구문은 조건이 거짓이므로 실행되지 않는다
TextButton(onPressed: (){
setState(() {
if(burpeeNumber >= 7){
burpeeNumber=7;
}else{
burpeeNumber=burpeeNumber + 1;
}
});
print('앞으로가기 클릭');
},
이 내용을 바탕으로, 본론으로 돌아와서 기존의 코드를 변경했다.
화면을 갱신하는 setState함수에 if조건을 걸어서
버피넘버의 변수가 7이거나 그보다 큰 경우에는
버피넘버의 변수를 7로 지정하게 했다(= 마지막 7번째 사진이 나온다)
그리고 else조건으로 버피넘버가 7보다 같거나 크지 않은 경우(=1~6)
기존 버피넘버에 +1이 되도록 해두었다.
이렇게 함으로써 1.png에서 7.png까지는 버튼을 클릭하면 넘어가도록 설정이 되었고,
그 이후의 클릭은 7.png가 나오도록 작동되게 코드가 작성이 된 것이다.
TextButton(onPressed: (){
setState(() {
if(burpeeNumber<=1){
burpeeNumber=1;
}else{
burpeeNumber = burpeeNumber - 1;
}
});
print('뒤로가기 클릭');
},
뒤로가기 버튼의 경우도 로직은 마찬가지이다.
버피넘버가 1 이하로 떨어지는 경우에는 사진이 없기 때문에, 버피넘버 변수를 1로 지정해서 1번 사진만 나오도록 했고,
그 외의 경우는 기존 변수에서 1씩 빼는 방법으로 했다.
'취미노트 > 코딩공부' 카테고리의 다른 글
[flutter] 09 효율적인 구조의 코드 작성, 함수 활용 (0) | 2024.08.18 |
---|---|
[flutter] 08 빠르고 다양한 앱 제작의 시작, 패키지 (0) | 2024.08.18 |
[flutter] 06 화면 구성의 시작 3, Expanded (0) | 2024.08.17 |
[flutter] 05 화면 구성의 시작 2, 기초 위젯 (0) | 2024.08.17 |
[flutter] 04 화면 구성의 시작 1, 기초 위젯 (0) | 2024.08.15 |
댓글