Mobile

[Flutter] 다이얼로그 내부에서 체크박스 사용하기 | StatefulBuilder 추가 | 상태 정보 관리 및 저장 방법 | FlutterSecureStorage

민갱스터 2024. 10. 15. 18:29

문제상황

 

AlertDialog 위젯 내부에 있는 Checkbox에 체크를 했는데,

체크 표시가 반영 안되는 문제가 있었다.

 

 

Scaffold 위젯 body 내부의 Container에 있는 체크박스는

위의 Checkbox 위젯의 코드와 다를게 없었지만 동일한 문제가 일어나지 않았다.

 

 

아마 Checkbox 위젯 코드 자체의 문제는 아닌 듯 했는데...

 

 

 

여기서 특이한 점은 체크박스 표시가 동작 자체를 아예 안하는 것이 아닌,

x 표시를 눌러 다이얼로그를 닫았다가 다시 다이얼로그를 열면 체크가 반영이 되어있었다.

 

즉, 바뀐 체크 상태를 반영하기 위한 새로고침이 필요한 것이라고 판단했고 해결 방법을 찾아 보았다.

 

 


원인 

[로그인 정보 저장 체크박스 위치]

: Scaffold 내부의 Container 내부

onChanged: (value) {
  setState(() {
    rememberMe = value ?? false;  // value가 null일 경우 false를 기본으로
  });
},

 

기본적으로 setState는 UI를 다시 그려야 한다고 알리는 역할을 한다.

setState 안에 있는 코드가 실행될 때, 상태가 변경되었음을 Flutter에 알리고, 이를 기반으로 UI를 업데이트한다.

 

따라서 onChanged 메소드 내부에 setState 메소드를 추가한

기존의 로그인 정보 저장 체크박스는 체크 표시가 잘 반영이 되었던 것이다.

 

 

 

[다음 문서로 이동 체크박스 위치]

: AlertDialog 내부

onChanged: (value) {
  setState(() {
    nextDocCheck = value ?? false;
  });
}

 

하지만 AlertDialog 내부의  Checkbox 위젯은 동일한 방법으로 체크 표시 반영이 안된다.

 

 

그 이유는 AlertDialog, SimpleDialog 등 Dialog 류의 위젯들은

StatelessWidget으로 만들어졌기 때문이다.

 

Dialog에서 상태 변경을 하려면 Stateful 위젯으로 감싸야한다.

 

 

- StatefulBuilder를 이용하는 방법

- StatefulWidget을 이용하는 방법

...

 


해결

나는 StatefulBuilder을 이용했다.

 

AlertDialog 바깥에 StatefulBuilder 위젯을 씌우고 

builder: (BuildContext context, StateSetter setState) 를 추가했다.

 

 

[변경 전]

showDialog<String>(
  context: context,
  builder: (BuildContext context) {
    return AlertDialog(...);
  }
);

 

[변경 후]

showDialog<String>(
  context: context,
  builder: (BuildContext context) {
    return StatefulBuilder(    // 이 부분 추가
      builder: (BuildContext context, StateSetter setState) {
        return AlertDialog(...);
      }
    );
  }
);

 

체크박스 부분의 코드 변경 없이 해결 가능하다.

 

다이얼로그에서도 체크 상태 바로 반영 가능해짐

 

 


+) 상태 정보 유지

[로그인 정보 저장]과 마찬가지로 나는 계속 체크 상태로 두고 싶은데,

매번 다이얼로그를 열 때마다 [다음 문서로 이동]을 체크하게 하면 유저에게 번거로움을 줄 수 있다.

 

따라서 이전에 체크 했다면 체크 상태를 유지, 안했다면 안한 상태를 유지하는 기능을 추가로 넣었다.

 

 

final storage = FlutterSecureStorage();

 

FlutterSecureStorage는 기기에 저장을 하는 것이기 때문에 앱을 종료했다가 실행해도 저장한 정보가 남아있다.

 

 

onChanged: (value) {
  setState(() {
    nextDocCheck = value ?? false;
  });
  if (nextDocCheck) {
    storage.write(key: 'nextDocCheck', value: 'true');
  } else {
    storage.write(key: 'nextDocCheck', value: 'false');
  } 
}

 

내가 체크를 했다면 true 값을 아니면 false값을 스토리지에 저장해두고

(bool값이 저장이 안돼서 string으로 저장했다.)

 

Future<void> _loadCheckboxStatus() async {
  final nextDocCheckStatus = await storage.read(key: 'nextDocCheck') ?? 'false';
  setState(() {
    nextDocCheck = nextDocCheckStatus == 'true';
  });
}

 

다음 문서로 이동할 건지 체크 상태를 나타내는 변수에 옮긴다.

(string으로 저장되어있기에 'true'랑 같으면 true를 반환하니까 그걸 변수에 담도록 했다.)

 

@override
void initState() {
  super.initState();
  fetchDocument();
  fetchMypage();
  _loadCheckboxStatus();
}

 

initState 에 넣어 nextDocCheck 값을 시작할 때 설정할 수 있도록한다.

 

 

 

 

체크한 상태로 닫았다가 다시 열면 이전 상태가 유지되는 것을 확인할 수 있다!

 

 


++) 여담

다음 문서로 이동하는 것 관련해서 기획 수정이 필요할 수도 있다고 해서

껍데기만 만들어두었던 것인데 이 글을 작성하다가 뭔가 어색한 것을 발견할 수 있었다.

 

 

이 사진에 이상한 점을 찾아보세요~~~

 

 

 

정답은 바로 X 아이콘 근처 공백인데,

공백 때문에 의견 입력 박스, 확인버튼과 같은 위치가 아닌 살짝 안쪽으로 들어와있다.

 

이는 내가 이전에

> [Flutter] 버튼 공백 지우기 | ElevatedButton와 GestureDetector 비교 | 위젯 분석

글을 포스팅하기 전에 작성해둔 코드였으므로

 

 

 

여기도 저 공백을 없애고 위치를 맞출 수 있도록

IconButton -> GestureDetector로 수정사항을 반영해두었다.

 

그래서 이 포스팅은 처음부터 이 부분이 수정된 채로 시작되었다...ㅋㅋㅋㅋ

 

 

블로그를 작성한 덕분에 그냥 신경 안쓰고 지나칠 뻔 했던 부분도 발견할 수 있었던 것 같다!