프로그래밍/Flutter

[Flutter] TextFormField의 suffixIcon에 IconButton 사용시 문제점과 해결방안 - 비밀번호 가리기

쿨쿨라이언 2021. 4. 1. 19:30

목차

I. TextFormField와 suffixIcon
II. 문제발생
III. 해결방법

미완성된 로그인 페이지

I. TextFormField와 suffixIcon

Flutter에서 기본적인 공부를 한 후 kream이라는 어플리케이션의 UI가 맘에 들어서 클론 코딩을 도전 중이다. 클론 코딩을 하면서 마주쳤던 문제들과 그 해결과정을 잊지 않기 위해 블로그를 시작하기로 결심했고, 첫 번째 글이다.
로그인 페이지를 구현해 보면서 TextFormField 라는 위젯을 사용했다. 사진에서와 같이 로그인 페이지에는 이메일 입력란과 비밀번호 입력란이 있는데 이들이 TextFormField위젯이다.

이메일 TextFormField부분 코드

validator 파라미터를 보면 RegExp라는 정규표현식을 이용했는데 이 내용은 나중에 기회가 되면 써 볼 것이다.

validator를 이용해 미리 이메일 형식을 준수했는지 확인하는 모습

감쪽같은 클론을 만들기 위해 비밀번호 입력란의 눈모양을 표시하는 과정에서 TextFormField의 InputDecoration에 suffixIcon이라는 파라미터를 이용했다. _obscureText는 bool 타입의 변수이고 비밀번호가 사용자에게 가려서 표시되는지 여부를 정하기 위해 사용했다.

이메일과 마찬가지로 validator에서 정규표현식을 이용한 모습

 

II. 문제발생

플러터 초보자로서 이런 간단한 코딩이라도 스스로 결과물을 만들어낸 것에 만족하던 중 문제가 발생했다. 클론코딩으로 UI뿐만 아니라 UX까지 똑같이 만드려는 나의 계획(욕심) 때문에 지나칠 수 없는 문제였는데 suffixIcon을 클릭하면 TextFormField를 클릭한 것과 마찬가지로 TextFormField가 Focus를 요청하게 되는 상황이 발생했다. (그림과 같이 키보드가 내려가 있는 상황에서 비밀번호를 안보이게 숨기는 눈 모양의 suffixIcon을 클릭하면 키보드가 올라오는 상황)
이러한 상황이 발생하는 이유는 suffixIcon의 IconButton이 TextFormField를 조상으로 두고 있기 때문에 IconButton이 클릭되면 IconButton의 onPressed이벤트와 TextFormField의 onTap이벤트가 모두 발생하기 때문인 것 같다.(다른 이유라면 알려주세요!)
구글링을 해보니 이를 해결하기 위해서 suffixIcon 대신에 Stack위젯에 TextFormField와 IconButton을 넣는 방법이 있는 것 같지만, suffixIcon의 존재를 몰라서 힘들게 찾은 나로서는 suffixIcon을 버리고 싶지 않았다.

눈모양의 아이콘을 클릭했는데 Focus가 TextFormField로 이동하여 키보드가 올라오는 모습

처음에는 Focus가 TextFormField로 이동했다고 생각을 못하고 단순히 키보드가 올라왔다는 사실에 불쾌하여 키보드를 컨트롤하는 방법을 찾고자 하였다. 구글링한 결과 keyboard가 올라왔는지 안올라왔는지 알 수 있는 방법이 있었는데 다음과 같다.(나중에 다른 곳에서 써먹었다)

키보드가 보이면 true 키보드가 보이지 않으면 false를 keyboardIsVisible파라미터에 넘겨주는 모습

키보드가 보이는지 안보이는지는 확인하겠는데 키보드를 어떻게 컨트롤하는지는 모르겠다. 찾아보니 pub.dev에 키보드를 제어할 수 있게 해주는 package가 존재하는 것같지만 초보자가 다른 사람의 코드를 쓰는 것은 바람직하지 않은 것 같고 이런 단순한 UX때문에 다른 사람의 무기(비장의 무기)를 쓰고싶지 않았다.
그래서 포기하지 않고 열심히 구글링한 결과 정석적이지는 않지만 내 나름의 규칙안에서 최대한 야비해 보이는 코드가 있어서 그것을 이용하였다.

III. 해결방법

더 좋은 방법이 있다면 알려주세요ㅎ.ㅎ 부탁드립니다.

이 방법은 매우 직관적이다. suffixIcon을 onPressed하는 이벤트가 발생하면 이후에 TextFormField에서 onTap이벤트가 발생하는데 Flutter는 onTap이벤트에서 내부적으로 FocusNode를 요청한다. 따라서 suffixIcon을 onPressed할 때 TextFormField가 FocusNode를 요청하지 못하게 canRequestFocus에 false를 집어넣으면 이후에 onTap이벤트에서 Focus를 TextFormField로 이동하지 못하게하고 결과적으로 키보드 또한 올라오지 않는다. 이 과정은 이미 Focus가 TextFormField에 있어서 키보드가 올라와 있는 상태에서는 작동할 필요가 없기 때문에 if문안에서 hasFocus를 이용하여 이미 Focus가 TextFormField에 있는지 확인하였다.
그런데 계속 TextFormField가 FocusNode를 요청할 수 없으면 당연히 문제가 된다. TextFormField를 눌러도 Focus가 이동하지 않고 키보드가 올라오지 않을 수 있기 때문이다. 따라서 suffixIcon을 onPressed를 한 후에TextFormField의 onTap이벤트가 진행될 때까지는 Focus를 요청못하더라도 onTap이벤트 후에는 다시 Focus를 요청하는 것이 가능하도록 해주어야 하는데 단순(무식)하게 0.1초 뒤에 canRequestFocus에 true를 넣어주는 방법으로 진행하였다.
이것이 가능하려면 한가지의 가정이 필요한데 바로 0.1초 안에 TextFormField의 onTap이벤트가 처리되어야 한다는 가정이다. 근데 생각해보면 매우 인접한 두 위젯(TextFormField와 IconButton)의 동시에 발생한 두 이벤트(onTap과 onPressed)가 거의 비슷한 시기에 처리될 것이므로 웬만하면 저 코드가 잘 동작할 것이다. 하지만 더 복잡한 경우 또는 내가 예상하지 못한 것들로 인해 저 코드가 동작하지 않아도 이상할 것은 없다.
결론은 이제 이 글을 읽은 분들 중 비슷한 문제를 겪고 더 지혜롭게 해결하신 분의 가르침을 받기 위해 나는 멈추지 않고 flutter의 다른 부분을 공부하며 기다린다는 것이다. (비동기적으로 ㅎ.ㅎ)

'프로그래밍 > Flutter' 카테고리의 다른 글

[Flutter] NFC in Flutter  (3) 2022.11.17