To-Do List 앱 Step 6. 항목 완료 체크 기능 추가

2024. 12. 3. 12:07같이 공부합시다 - Flutter/Flutter로 To-Do 앱 만들기

728x90
반응형

 
To-Do List 앱에서 빠질 수 없는 기능이 있죠.
 
바로 항목 완료 체크 !
 
맞습니다. 할 일이 완료되면,
할 일을 삭제하는 것이 아닌,
완료 체크가 가능하도록 하는 것이죠.
 
이 기능을 통해 리스트의 항목을 완료 상태로 표시하거나,
완료된 항목과 미완료 항목을 구분할 수 있습니다.
 
할 일 목록입니다. 😁


3 . 화면에 할 일 리스트 표시.
4 . 할 일 삭제 기능 추가
5 . 데이터 저장 추가
6 . 항목 완료 체크 기능 추가
7 . 완료/미완료 분리 표시 및 필터 기능 추가
8 . 할 일 목록 수정 기능 추가
9 . 완료된 항목을 자동으로 삭제하는 옵션 추가



 

[ Step 6 의 변경사항 요약 ]

1 . _todoList 를 Map 형태로 변경하기
2 . 할 일 추가 메서드 수정하기
3 . 완료/미완료 할 일 목록 표시하기
4 . 토글 변경 메서드 만들기
5 . 저장 메서드 수정하기
6 . 로드 메서드 수정하기






1 . _todoList 를 Map 형태로 변경하기

기존에는 할 일 목록에 대한
정보(장보기, 강아지 밥주기 등)만 필요했는데,
이제는 하나의 리스트 항목에 완료가
되었는지에 대한 구분값도 필요해 졌습니다.
 
그래서 이 List 에 변화가 필요하죠.



<_todoList 를 Map 형태로 변경>



final List<String> _todoList = []; // 할 일 목록
기존에는 String 타입만을 저장하는 리스트 객체였습니다.
 
이제 이 List 는
<String, dynamic> 타입의 Map 객체를
요소로 갖도록 거듭났어요!
(* Map 은 key - value 쌍으로 저장하는 컬렉션입니다. )
 
후.. 완료 여부 상태 하나 추가하는데
리스트 만드는 거 하나부터 어렵네요. 



<내용이 어려운 스테디빌더>

내용이 어려운 스테디빌더


그래도 가봅시다.
List 와 Map 에 대해서는
나중에 한 번 따로 정리해 볼게요.





2 . 할 일 추가 메서드 수정하기


우선 할 일을 추가하는 메서드를 수정하면서,
할 일 목록의 List 객체에
어떤 형태의 값이 저장되는지 봅시다.


<할 일 추가 메서드 수정>



task 라는 항목에 task 매개변수가 설정되고,
isCompleted 항목에는 기본 false 값이 설정됩니다.
true 가 되면 할 일 완료 상태가 되는 거죠 : )
 
이전 List<String> 그러니까
할 일의 제목만 저장했던 리스트는,
이제 task 할 일의 제목과
isCompleted 라는 속성값을 갖는 겁니다.
 
좋아요.
이해 됐으니 다음으로 넘어가도 되겠습니다.





3 . 완료/미완료 할 일 목록 표시하기


상태값을 다룰 수 있게 되었으니,
이제 할 일 목록에서 완료로
체크할 수 있는 부분을 만들어볼게요.


<완료 상태 체크박스 UI 추가>



먼저, UI 에서 쓰일 지역 변수
_todoList[index] 를 todoItem 으로
만들어 둘 겁니다.

이는 가독성 , 코드 중복 방지를 위함입니다.


<지역 변수 선언하기>



전역에 설정한 리스트 _todoList 에
직접 접근할 수도 있지만,

그렇다면 아래처럼 매번
Full 경로를 써야 하거든요.

_todoList[index]['task']
_todoList[index]['isCompleted']

지금이야 이거 하나지만,
만약 관리해야 할 List 가 많다면
경로가 헷갈릴 수도 있습니다.

그럴바에
_todoList[index] 줄이는게 낫겠죠?

좋아요!
다음!



다음은 ListTile 의 변화죠.


<할 일 목록에 스타일이 생겼어요>


이제는 할 일 목록이
완료 여부에 따라 스타일이 달라집니다.
 
할 일 목록은 todoItem[’task’] 로 뿌려주는데,
완료 여부에 따라서 lineThrough 그러니까,
요렇게 표기되는 걸 스타일로 나타내는거죠.
 
아참 폰트 색상도 완료 체크 시
회색으로 바뀝니다.
 
좋습니다 !





<체크 박스 추가>



다음은 할 일 목록에 체크박스를 추가해 봅시다.
 
leading 속성으로 나타낼 수 있어요.
앞에서 스치듯 연습했던 적이 있죠.
휴지통 아이콘 추가할 때요. (ㅎ)
 
https://steadybuilder.tistory.com/16

To-Do List 앱 Step 4. 할 일 삭제 기능 추가

아니 끝난 줄 알았는데 ?! 할 일 추가 기능까지만 있다면,진정한 To-Do List 앱이 아니죠 ! 새로운 할 일 목록입니다. 😆 3 . 화면에 할 일 리스트 표시.4 . 할 일 삭제 기능 추가5 . 데이터 저장 추가6 .

steadybuilder.tistory.com

 

leading 속성으로 할 일 목록 앞쪽에
체크박스를 둘 건데, 이 체크박스는
완료 상태를 나타내도록 하는 값이죠.
 
그래서 value 값으로
todoItem['isCompleted']
그러니까,
_todoList[index] 의
[’isCompleted’] 가 됩니다.
 
이 값에 따라서 체크박스에
체크가 되고 안되고가 되는 거죠.
 
isCompleted 값이 true 면 체크되고,
false 면 체크가 안되어 있도록 하는겁니다.
 
onChanged 속성을 사용하면
사용자가 완료 상태로 바꾸는 걸
바로 감지할 수 있겠죠?
 
그럼 여기서 변경될 때마다,
토글 상태값이 변하도록
매서드를 호출해 줍시다.





4 . 토글 변경 메서드 만들기


앞에서 토글 상태값이 변하도록 호출하였으니,
이제 처리할 수 있는 메서드를 만들어 둡시다.


<완료 상태 변경 메서드>


저는 할 일 삭제 메서드 아래 두었습니다.
(위치는 상관없지만 모아두면 좋아요 😄 )
 
여기에서는 _todoList 의 isCompleted 값을
NOT 연산으로 해서 true / false 값을
변환해 주면 되는거죠.
 
체크를 하면 메서드가 호출되고,
체크박스에 체크되면서 미완료 상태는 NOT !
완료 상태로 바뀌어서 들어가는거죠.
 
처리하고 나면 꼭 저장도 호출해 주자구요.
 
굿 ! 아직까진 괜찮네요 !
다음!





5 . 저장 메서드 수정하기


리스트의 저장 타입이 바뀌었으니,
이제 todoList 키워드에 _todoList 리스트를
그대로 저장할 수는 없게 되었습니다.




<데이터 저장 메서드 수정>


저장할 때 단순히 String 이 아닌,
JSON 포맷을 사용할 겁니다.
 
JSON 형식은 저장되고 로드될 때의 데이터 변환이
더 안전하고 간단하기 때문이예요.
 
jsonEncode 함수로 _todoList 리스트 정보를
JSON 포맷으로 변경합니다.
 
변경하면 이런 형태로 됩니다.


<리스트 형태>

_todoList = [
  {'task': 'Flutter 공부', 'isCompleted': false},
  {'task': '운동하기', 'isCompleted': true},
];

 
<JSON 형태>

'[{"task":"Flutter 공부","isCompleted":false},{"task":"운동하기","isCompleted":true}]'


이후 prefs.setString() 메서드로,
todoList 키워드에 저장합니다.
( 기존에는 setStringList() 였어요, 주의 ! )





6 . 로드 메서드 수정하기


이제 마지막 로드 메서드만 수정하면 되네요 !
바로 가봅시다 !


<로드 메서드 수정>


우선 데이터를 로드할 때에도,
prefs.getString (기존에는 getStringList)
메서드로 변경되었어요.
 
저장도 setString 이므로,
아예 문자열로만 저장/로드 되는거죠.
편하네요 !
 
다음은 dynamic 속성을 갖는
jsonData List<dynamic> 을 만듭니다.
 
로컬 todoList 저장된
jsonString 을 List 화 할 거예요.
( _todoList 가 List 이기 때문입니다. )
 
이렇게 변환하는 과정의 정확한 표현은,
‘JSON 문자열 → Dart 객체 변환’ 입니다.


<json>

'[{"task":"Flutter 공부","isCompleted":false},{"task":"운동하기","isCompleted":true}]'

<dart>

[
  {'task': 'Flutter 공부', 'isCompleted': false},
  {'task': '운동하기', 'isCompleted': true}
]


이제 _todoList 에 담기만 하면 됩니다.
addAll 메서드로 추가하면 되죠.
 
addAll 의 대상은 바로,
jsonData.cast<Map<String, dynamic>>() 입니다.
 
jsonDecode로 변환된 리스트의 각 항목을
Map<String, dynamic> 타입으로
명시적으로 캐스팅 하는 거죠.
 
이렇게 캐스팅을 하는 이유는
타입 안정성을 보장하기 위해서입니다.
 
Map의 key는 항상 String이고,
value는 dynamic 타입이 될 수 있기 때문이죠.
 
이제 할 일 목록의
완료 체크 기능이 완성되었습니다! 🎉



 
테스트 해봐야죠 ? 두근두근..


<테스트 결과 : 오류>


Exception has occurred.
_TypeError (type 'List<dynamic>'
is not a subtype of type 'String?' in type cast)
 
이 오류는 SharedPreferences에서
데이터를 가져올 때
데이터 타입이 예상과 다를 때 발생합니다.
 
생각해보니 기존에 저장된 값은
List<String> 이기 때문이네요.

기존 저장된 정보를 날리고 다시 저장합시다.


<저장된 데이터 초기화 설정>


_clearOldData 메서드를 만들었습니다.
 
앱이 호출되면 무조건 실행되는 initState() 에
넣어 두었으니까 바로 호출되겠죠.
 
SharedPreferences 의 저장 객체를 불러와야죠.
todoList 키워드의 저장 객체 정보를
remove 메서드로 날립니다.
 
거기다 _todoList 객체의 정보도 지워야 하죠.
clear 메서드로 깨끗하게 !
 
이제 다시 테스트 진행 !
드디어 정상적으로 앱이 실행되네요 !
 
자, 이제 _clearOldData 메서드는 지울게요.
혹시 모르니 initState 의 목록에서만 주석처리 해둡시다.

다시 호흡을 가다듬고,
마지막 테스트 땅 !


<테스트 결과>



다양하게 테스트 해보시면 됩니다.
정말 고생 많으셨습니다 !
 
끝 !
 

 

728x90
반응형