<3-1. 상태관리>
- 상태
- 고정되어 있지 않고 변화되는 데이터, 정보
- 상태관리
- 상태를 효율적으로 관리하여 UI와 동기화하는 과정
- UI 동기화
- 변화되는 상태를 시각적으로도 즉시 표시
- 플러터의 상태 관리
- StatefulWidget의 setState 방식
- 서드 파트 라이브러리를 이용하여 상태 관리하는 방식
<3-2. StatefulWidget의 setState 상태관리>
- 프로젝트 생성
- 기본제공 예제소스 - StatefulWidget
- FloatingActionButton을 누르면 가운데 숫자 상태의 값을 올리면서 UI가 동기화되어 변화되는 상태를 사용자가 바로 확인할 수 있는 아주 기본이 되는 상태 관리 소스코드
- 상태변경 O / UI 동기화 X
-> 더하기 버튼을 아무리 눌러도 화면에 변화 X
-> 상태(데이터)는 변경됨
-> 변경되는 데이터에 대해서는 debug console에서 확인가능
-> 상태 관리가 잘되지 않는다면 서비스에도 좋지 못한 영향 + 사용자에게 좋지 않은 사용경험 - 상태가 변경되고 있는데 화면에 동기화가 이루어지지 않는 이유 (debug console에서는 변화중)
-> 변경된 상태를 UI에 동기화 과정을 거쳐야 화면에 반영
-> 동기화하는 방법은 setState 함수를 호출 - setState 함수가 값을 동기화 시켜주는 이유
-> build 함수가 호출이 되어야 변경된 상태를 UI로 동기화 가능
-> setState 함수는 build 함수를 다시 불러주는(호출) 역할
-> 상태를 변경 후에 setState만 호출해 주면 상태 동기화
<3-3. setState의 한계>
- 별도의 상태 관리 라이브러리를 사용해야 하는 이유
- StatefulWidget의 목적은 컨포넌트별 상태 관리가 목적이므로 전체적인 상태 관리에는 목적에 맞지 않음
- setState의 한계
- 상태 공유의 어려움:
- setState는 개별 StatefulWidget 내부에서만 상태를 관리
- 여러 위젯 간에 상태를 공유해야 할 때, 상태를 전달하는 것이 복잡해지고 비효율적일 수 있음 - 규모가 커질 때 복잡도 증가:
- 애플리케이션이 커지고 상태를 관리해야 하는 위젯이 많아질수록, setState를 통한 상태 관리가 복잡함
- 모든 상태 변경이 중앙에서 관리되지 않기 때문에 코드가 산만해지고 유지 보수가 어려워짐 - 성능 문제:
- setState는 해당 StatefulWidget 전체를 다시 빌드함
- 복잡한 UI에서 작은 상태 변경이 전체 위젯 트리를 다시 빌드 하게 만들어 성능 저하를 초래 가능 - 전역 상태 관리의 부재:
- setState는 위젯 트리의 특정 부분에서만 동작하기 때문에, 전역 상태를 관리하거나 앱 전체에서 상태를 접근하기 어려움
<3-4. 소스코드로 setState의 한계 살펴보기>
- 프로젝트 내에 필요한 라이브러리를 설치해야 오류가 발생하지 X
- pubspec.yaml - 다운로드 아이콘 클릭
- 프로젝트 내용 : 상품을 추가하고 추가된 상품을 좌측에 담을지 우측에 담을지를 분배하고 넣었던 상품을 다시 제거하는 단순한 앱
- 소스파일 하나로 만든 경우 setState로 상태관리 가능
- 가독성이 좋지 않아 서비스 품질 저하 및 유지보수 어려움
- 소스코드가 깊어지면 함수로 위젯 분리 또는 클래스 생성
- 상태변경 이벤트 - 클래스 간의 데이터 교환처리 필요
-> 사용자가 FloatingActionButton을 눌러 즐겨 찾는 상품을 추가하는 상태가 변경이 되면 DefaultItem 이 추가되면서 그 자녀 위젯 중 Text 위젯에 상품 이름 표시 하려면
-> setState로는 Text 위젯에만 다시 그려지는 것 불가
-> 두 위젯은 서로 관계가 없고 연결되어있지 않아 전달 불가능
-> Key 접근법은 Key를 관리해야 하며 이를 통한 이벤트 발생방식은 좋은 접근방식 X
-> FloatingActionButton의 이벤트가 발생되어 Text 위젯에 데이터를 표시하려면 부모 위젯인 StatefulWidgetSample (연결점) 활
-> setState는 build 함수를 호출시키기 때문에 연결된 모든 위젯들이 다시 그려지는 UI 동기화가 이루어짐
-> 최상위 위젯인 StatefulWidgetSample에 상태를 변경하여 setState를 호출하면 해결
-> 상품의 상태를 전달하는 방식으로 해결이 가능
-> 추가 버튼 클릭하여 해당 상품을 ItemTag 위치 Text까지 상태 전달해야 함
-> 이벤트를 부모로 전달하고 상태도 전달해야 함
-> 불편하고 번거로움
-> 부모의 상태변경에 따라 불필요한 위젯까지 모두 그려야 하는 성능적 이슈 발생
=====>>>>> 이런 한계들을 해결하고자 중앙 집중적인 상태 관리 라이브러리들이 개발됨
- 대표적인 중앙 집중적 상태 관리 라이브러리
- Provider
- Getx
- Bloc/Cubit
- Riverpod
-> 중앙 집중 상태관리 - 상태 관리 및 이벤트 전달받기, 원하는 위치로 상태전달 가능.
-> 불필요한 전달과정 X
-> 화면갱신 필요 영역만 다시 그려짐 - 강력한 성능
<3-5. Getx 상태관리>
- 가장 인기있는 라이브러리
- 사용이 매우 쉬움
- 부수기능이 초심자에게 적합
- 간단한 API
- 매우 간단하고 직관적인 API (학습 곡선을 줄이고 빠른 개발 가능)
- 상태 관리를 위해 복잡한 코드를 작성할 필요가 없음
- 성능 최적화
- 리액티브 프로그래밍을 지원하여 상태 변화 시에만 UI를 갱신
- 불필요한 리빌드를 최소화 / 애플리케이션의 성능을 최적화
- 의존성 주입
- 의존성 주입 기능을 제공하여 객체의 생명주기를 관리
- 의존성 관리와 객체 재사용성을 높여줌
- 라우팅 관리
- 내장된 라우팅 기능을 통해 간편하게 네비게이션을 관리
- 코드의 가독성을 높이고 네비게이션을 보다 쉽게 처리가능
- 상태 관리 방법
- 중앙집중 상태(컨트롤러) 등록 - 최상위 위젯에 속하도록 상태등록
- 컨트롤러 등록
- StatefulWidgetSample 위젯보다 상위에 컨트롤러를 등록
- GetxController 클래스 상속 - 상태 관리 클래스로 변화
- main.dart - build 함수 밑에 등록
- 상품 추가 이벤트 개발
- 기존에 StatefulWidgetSample에서 관리중이던 상태 변수를 옮겨오기
- 위젯으로 발생하는 이벤트에서 호출될 부분 만들기
- 상품 추가 이벤트
- FloatingActionButton을 누르면 발생되는 이벤트를 통해 상품을 생성하는 로직을 만들기
- 부모인 GetxController에는 update라는 이벤트가 존재
- ProductController를 통해 화면을 갱신되어야 하는 부분을 UI 동기화 이벤트를 발동시키는 트리거의 역할을 수행
- 상품 추가 이벤트 연결
- 화면 UI에 버튼 클릭 시 controller의 이벤트를 연결
- 이미 ProductController를 등록해 줬기 때문에 어느 곳에서든 Get.find 함수를 통해 원하는 컨트롤러에 접근
- 상품 추가에 대한 상태값 UI동기화
- 상품 리스트 화면에 productController에 update 함수가 호출될 때 화면 갱신이 되도록 하려면 GetBuilder 위젯을 사용
- GetBuilder는 Generic 타입을 원하는 참조 controller의 클래스를 등록해 주면 해당 컨트롤러의 상태가 변경되고 update 함수가 호출이 되면 builder가 호출되어 변경된 상태의 값에 따라 화면을 변화
- 좌/우 측 버튼 이벤트처리
- StatefulWidgetSample 위젯에서 DefaultItem에 이벤트인 pushZone를 옵션으로 넣어서 자녀의 버튼을 통해 부모로 이벤트를 전달하는 방식으로 되어있는데 이 부분을 제거하고 DefaultItem 내부에서 바로 ProductController로 접근해서 이벤트 처리
- 상태에 따른 UI 동기화 처리를 해주고 있지 않아 상품이 좌우로 이동되지 X
-> 불필요하게 상태와 이벤트들을 넘겨주고 받아서 처리하던 작업 방식을 Getx를 통해 수정을 해보면서 너무나도 손쉽고 간단하게 상태 관리를 할 수 있음
-> 소스코드 역시 깔끔하게 작업가능
-> 원하는 부분만 골라서 상태에 따른 UI 동기화가 이루어지기 때문에 성능적인 부분도 강점
<3-6. GetX 활용 알람앱 만들기>
- 기능 명세 정의
- 알람 추가는 숫자 입력을 통해 설정할 수 있다.
- 알람 시간에 따라 자동으로 오전/오후가 설정이 바뀐다.
- 저장을 통해 홈 화면에 추가된 알람이 표시가 된다.
- 알람 홈 화면에 편집 버튼을 통해 추가된 알람을 건별로 삭제 할 수 있다.
- 삭제시 컨펌 알럿은 생략한다.
- 알람 홈 화면에 편집 상태에 별개로 알람 건별로 켜고/끌 수 있는 스위치가 상태 관리가 된다.
- 특정 알람을 선택 시 알람 추가 페이지로 이동되어 수정 모드로 변환 된다.
- 수정 시 변경된 알람 시간으로 표시된다.
- 2개의 TextField - 숫자만 입렵 / 시간에 따라 오전 및 오후 자동변경 / 전역 상태관리에 알람시간 추가 / 홈화면에서 리스트 보기
- 할일 정리
- 업무를 마치 미션을 수행하는 느낌도 나면서 개발을 조금이라도 재미있게 할 수 있다.
- 내가 무엇을 개발해야 하고 무엇을 완료했는지 시각적으로 볼 수 있어서 빠른 업무 전환 및 보람을 느낄 수 있다.
- 할 일 별로 개발 소요시간을 파악하기 쉬워진다.
- 알람앱 기능명세에 따른 할일 목록
- 알람 등록 화면 Get route 설정
- 알람 등록 화면 구성(위젯 구성)
- 알람 등록 기능 개발
- 홈 화면 등록된 알람 리스트
- 홈 화면 알람 편집 이벤트로 편집화면 구성
- 알람 삭제 기능 개발
- 알람 선택 시 기존 알람 등록 화면 > 알람 편집 화면으로 변경
- 개별 알람에 활성화/비활성화 기능 적용
<3-8 알람 등록 화면 Get route 설정>
- AppBar - 상단 Header 구성
- body - Center / Row - Text, TextField 구성
- AppBar
- 취소버튼 - Get.back
- GetX 에서 제공되는 Route를 설정하도록 설정했기 때문에 페이지 전환에 따른 페이지 history가 관리
- 단순히 뒤로 가기 위해서는 Get.back이라는 함수를 호출
- title
- iOS - default 중앙 정렬
- android - 좌측 정렬
-> 중앙정렬 : appbar - centerTitle (true)
- 취소버튼 - Get.back
- body
- keyboardType: TextInputType.number - 키보드가 숫자만 노출
-> 숫자가 아닌 값 입력차단 - inputFormatter
-> FilteringTextInputFormatter.digitsOnly - custom Formatter - 시간 및 분의 범위 설정
-> RangeTextinputFormatter 클래스 생성
-> TextinputFormatter를 상속받음으로 인해 이전 값과 현재 입력된 값을 전달받아서 어떤 값으로 전달해 줄지에 따라 TextField에 입력되고 안되고를 설정
- keyboardType: TextInputType.number - 키보드가 숫자만 노출
<3-9. 알람 등록 기능 개발>
- 알람 상태 관리 controller 생성
- main.dart - build 함수 내에 작성
- 어디서든지 Get.find 를 통해 해당 컨트롤러에 접근가능
- 알람 등록에 필요한 상태 정의 및 이벤트 등록
- 알람 등록 페이지에 상태/이벤트 연결
- GetBuilder - 상태가 변경될 때마다 오전/오후 문자만 변경 (12시를 기준으로 삼항 연산자 사용)
- onChanged - 시간, 분 입력필드 값 update 되도록 controller에 각각 setHour / setMinute 연결
-> TextField 에서 전달되는 입력값은 String - int 타입으로 형 변환
- 저장 기능 개발
- 저장 : 홈 화면에 알람 목록에 표시하도록 하는 행동으로 홈 화면에서 보여줄 알람 목록에 대한 상태 값을 가짐
- List 타입 정의
- 활성화/비활성화 처리 - 확장성 고려하여 알람모델 설계관리
- alarmList에 데이터 넣기
- 추가페이지 : 저장버튼 - controller 저장이벤트로 연결
- GetX 자동 최적화 - AlarmController 사용페이지 생성 / 미사용시 자동삭제
-> 앱 종료 전까지 controller를 사용해야 하는 경우 자동삭제 설정해제
<3-10. 홈화면 등록된 알람 리스트>
- 기타 리스트 - alarmController의 alarmList를 바탕으로 데이터를 불러오기
- map - 반복문이지만 반복문을 통해 새로운 결괏값으로 반환
-> 새로운 위젯을 반환하는데 alarm의 모델을 활용한 위젯을 반환
- map - 반복문이지만 반복문을 통해 새로운 결괏값으로 반환
- alarm 객체로 알람 목록 적용
- alarmList - map으로 돌리는 부분 이동
<3-11. 홈 화면 알람 편집 이벤트로 편집화면 구성>
- 편집모드 상태 및 이벤트 추가
- 편집 - toggleEditMode 이벤트 호출
- isEditMode 에 따라 버튼 변경
- 편집 완료 - 원래상태 복귀
- 색상값, 문구만 변경
- 기타알람목록 좌측 알람 삭제버튼 생성
- 전체를 Row로 감싸고 isEditMode 에 따라 icon 넣기
<3-12. 알람 삭제 기능 개발>
- AlarmModel 아이디 값 추가
- 알람을 삭제 및 수정을 하기 위해서는 모델에 고유 id가 있어야 알람 목록에서 판별
- 예) 저장된 시간과 분으로 판별해서 목록에서 삭제를 한다고 한다면 동일한 시간으로 설정된 알람이 동시에 삭제
-> 이를 방지하기 위해서 알람을 생성할 때 고유 id 값을 추가해 주면서 앞으로 id 값에 따라 관리될 수 있도록 설계
-> 고유 id를 자동으로 생성을 해주는 라이브러리 활용
-> uuid라는 라이브러리를 추가
- 설치 완료시 모델 수정
- AlarmModel 생성시 자동으로 id값 부여
- 앱 새로고침 후 알람 추가
- 아이디 부여 확인 - _etcAlarm print
- 고유 id생성 - 알람 삭제 개발
- AlarmController - removeAlarm 이벤트 생성
- 함수 호출시 alarm id 넘겨줌
- 해당 id값으로 알람목록에서 삭제
- 테스트용 알람 등록
<3-13. 알람 선택시 기존 알람 등록 화면 -> 알람 편집 화면으로 변경>
- 알람 목록에서 수정하여 시간 변경 update
- 알람목록에서 아이템 클릭시 이벤트처리
- 알람 생성과 동일한 페이지로 랜딩
- AlarmWritePage로 보내줄 때 대신 alarm 객체 넘겨줌
-> 화면에서 alarm 객체 유무에 따라 편집모드/알람생성 분기
- AlarmWritePage 수정
- AlarmModel을 nullable로 설정하여 생성
- alarm 값이 넘어오지 않을 것이니 생성 mode로 하고 수정 시에는 alarm 값을 보내주기 때문에 수정 mode로 처리
- AlarmWritePage를 StatelessWidget에서 StatefulWidet으로 변경
-> TextField에 초기값을 설정하기 위해서는 Controller를 사용해야 하고 위젯이 생성될 때 단 한 번만 초기화를 해줘야 하기 때문에 initState 함수가 필요
- 수정모드
- widget.alarm의 값이 있으며 initState에서 각각 TextEditingController의 text 값에 시간과 분 넣기
- editAlarm이라는 함수로 AlarmController에 이벤트를 넣어주는 이유 : hour 값에 따라 오전/오후를 표시
- appBar에 alarm 값이 있다는 것은 알람 편집이고 alarm 값이 없으면 알람 추가를 보여주므로 인해 사용자가 현재 어떤 상태인지를 인지시켜주기 위해 처리
- 저장 시 id 값을 넘겨주는 이유 : id 값이 있을 경우는 알람 목록에서 id에 해당되는 값의 모델을 수정
-> 새로운 알람을 생성에는 알람이 없기 때문에 null이 전달
- AlarmModel을 nullable로 설정하여 생성
- AlarmController 수정
- 화면이 그려지기 전에 alarm 모델을 통해 화면에서 사용되고 있는 hour과 minute 값을 수정
-> 시간에 따라 자동으로 오전/오후를 표시 - id 값의 유/무를 통해 수정을 할지 생성을 할지 로직을 분기
- 모든 요소를 return 해주고 있지만 id 값과 같은 알람의 경우 hour, minute를 갱신된 데이터로 변경해 주고 return 해주고 있음
- 화면이 그려지기 전에 alarm 모델을 통해 화면에서 사용되고 있는 hour과 minute 값을 수정
- 개별 알람에 활성화/비활성화 기능 적용
- 알람 요소별 스위치 위젯을 켜고 끌 때 실제 알람 모델의 isOn 상태 변경
<3-14. Getx활용 스레드 앱 만들기>
- 기능 명세 정의
- 스레드 등록 시 글은 반드시 입력되어야 한다.
- 이미지는 선택은 옵셔널이다.
- 스레드 등록이 완료되면 홈 화면에 등록된 피드를 확인할 수 있다.
- 피드 등록 시간이 timeago 라이브러리를 통해 표시한다.
- 피드 별 우측의 … 버튼을 통해 bottomsheet가 활성화되고 삭제 및 수정 버튼이 노출된다.
- 삭제 버튼을 통해 피드를 삭제할 수 있다.
- 수정 버튼을 누르면 수정 피드 페이지로 이동되어 수정이 가능하다
- 수정은 글과 이미지를 추가 및 삭제할 수 있다.
- 할일 정리
- 기능명세에 따른 할일 목록
- 스레드 등록 페이지 route 설정
- 스레드 등록 화면 구성(위젯 구성)
- 이미지 선택 라이브러리를 통한 이미지 선택 개발
- 스레드 등록 기능 개발
- 홈 화면 저장된 피드 리스트
- timeago 라이브러리를 통한 피드 시간 표기
- 피드 우측 버튼을 통해 bottomSheet 활성화 및 삭제/수정 버튼 배치
- 피드 삭제 개발
- 기능명세에 따른 할일 목록
<3-15. 스레드 등록 페이지 route 설정>
- GetX 상태 관리 라이브러리 설치
- main.dart - MaterialApp 변경
- GetX Route 설정 셋업
- ThreadWritePage로 route 처리
- page 파일 만든 후 lib - new file (thread_write_page.dart)
- home.dart 파일에서 _quickFeedWriteView 위젯에 어디를 눌러도 AlarmWritePage로 이동되도록 수정
<3-16. 스레드 등록 화면 구성(위젯 구성)>
- Appbar - Header
- body
- 피드 입력창 구성
- TextField 활용
- 이미지 선택영역 만들기
- 이미지 선택 개수만큼 반복 위젯 구성
- Container - 이미지 변경부분
- Stack - 이미지 삭제할 경우 Icons.close 를 이미지 위에 올려줘야 함
- 이미지 개수만큼 PageView 사용
-> 스크롤 시 자동으로 자석효과 스냅핑
-> 필요 없을 시 SingleChildScrollView 사용이 더 편리
<3-17. 이미지 선택 라이브러리를 통한 이미지 선택 개발>
- 스레드 등록시 이미지 선택기능 - 라이브러리 설치
- iOS - 접근 권한 요청
- ios - Runner - Info.plist 가장 하단부에 코드 삽입
- 이미지 선택완료 후 return된 images 개수만 출력
-> 라이브러리 설치 후 앱 재실행 필수
<3-18. 스레드 등록 기능 개발>
- 상태 관리 적용
- thread_feed_write_controller.dart 파일을 만들어서 피드 등록에 해당되는 상태 관리 controller를 만들기
- 피드 문구, 이미지 선택 데이터를 상태관리할 수있는 변수 만들기
- 상태변수들이 변경될수 있도록 함수 이벤트 만들기
- 등록화면에서 각 이벤트 연결
-> 피드 등록 페이지가 열릴 때부터 피드 등록 페이지가 종료될 때까지만 유지되면 되는 컨트롤러 - 페이지 route 시에 해당 controller를 등록
- 이미지 선택시 화면표시
- 이미지 없는 경우 - 빈 Container 반환
- Image 위젯
- asset
- network
- 파일에서 path (이미지 경로) 추출 후 File 데이터 활용
- 등록 버튼 배치
- Sacffold의 bottomNavigation - 등록버튼 처리
- 저장 기능
- 피드 모델 만들고 저장 누르면 해당 모델로 데이터 넣기
- uuid 라이브러리 설치
- 저장프로세스
-> selectedImages의 경우 XFile 모델이기 때문에 File로 변환해 줘야 합니다. 이때 map 함수를 사용 - Get.back (뒤로가기)이벤트 함수에 result라는 파라미터로 FeedModel을 담아서 넘겨줌
-> result - 홈화면에서 저장된 FeedModel 받음
<3-19. 홈 화면 저장된 피드 리스트>
- 등록피드 홈화면에서 보기 - result model을 홈피드 리스트 상태관리에 추가
-> 실제로는 피드 등록 페이지에서 저장 시 서버로 데이터를 저장시키고 홈 필드로 돌아왔을 때 피드를 새로고침하여 서버로부터 피드 리스트를 새롭게 받아오도록 처리 - 홈화면 피드 컨트롤러 생성
- controller를 화면에서 사용하고자 의존성 주입
- HomeFeedListController에 접근이 가능
- 피드 리스트 상태와 피드를 등록했을 때 추가하는 함수를 추가
-> controller의 feedList가 없을 경우를 대비해서 메세지 위젯으로 대응 - 피드 등록 후 전달받은 result를 homeFeedListController로 상태를 추가하는 것을 연결
-> 직접 입력한 피드가 아닌 고정된 피드가 계속적으로 보임 : feddList의 직접적인 데이터로 화면 위젯을 배치하지 않음 - 위젯 배치
- 이미지 선택 등록
- 피드별 높이가 고정값으로 잡혀있어 피드 하나당 영역이 너무 큼
- 피드 등록 방식으로 이미지 보여주도록 수정
- 고정된 height 제거
- 좌측 선 표시 위젯 제거
- 이미지 표시 방법 변경
- 이미지 정렬 PageView 소스코드를 재사용
- 컴포넌트로 별도 위젯 만들기
- image_view_widget 컴포넌트 만들기
<3-20. timeago라이브러리를 통한 피드 시간 표기>
- 피드 시간표시 - 스레드와 같도록 처리 : 라이브러리 설치
- 필요한 시간 포맷 - timeago
<3-21. 피드 우측 버튼을 통해 bottomSheet 활성화 및 삭제/수정 버튼 배치>
- bottomSheet를 열어서 수정, 삭제를 할 수 있도록 메뉴를 활성화
- BuildContext - BottomSheet가 띄워질 위치를 최상위로 찾아가서 위젯을 띄워야 함
- StatelessWidget의 경우 BuildContext가 위젯 내에서 참조불가
- 별도의 함수에서 context에 접근하기 위해서는 build 함수에 파라미터로 전달이 되는 BuildContext를 함수로 전달해 줘서 처리
- GetX를 사용하면 손쉽게 context를 사용
<3-22. 피드 삭제 개발>
- bottomSheet 메뉴 중 삭제 버튼을 누른다면 해당 feed의 id로 HomeFeedListController에 전달하여 feedList에서 해당되는 id의 피드를 삭제
- bottomSheet 메뉴 클릭 시 해당 controller의 remove 함수를 호출
- feedId를 전달
'내일배움캠프 (Flutter 5기)' 카테고리의 다른 글
Flutter 앱 개발 온보딩 주차 마무리 소감 (0) | 2024.10.25 |
---|---|
Flutter 앱 개발 4, 5주차 강의내용 정리 (2) | 2024.10.24 |
Flutter 앱 개발 2주차 강의내용 정리 (0) | 2024.10.22 |
Flutter 앱 개발 1주차 강의내용 정리 (3) (0) | 2024.10.21 |
Flutter 앱 개발 1주차 강의내용 정리 (2) (6) | 2024.10.10 |