로그인창을 만들 때 엔터나 탭으로 다음 칸으로 이동하게 만들기를 해볼 것이다!
//앞전의 정의들
static const inputRadius = 8.0;
OutlineInputBorder enabledBorder = const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(inputRadius)),
borderSide: BorderSide.none);
OutlineInputBorder focusedBorder = const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(inputRadius)),
borderSide: BorderSide(color: Color(0xFFffbc00), width: 1.0));
TextStyle hintStyle = TextStyle(
color: Color(0xFFAAACB2),
fontSize: Constants.FONT_SZIE_DEFAULT,
fontWeight: FontWeight.w500);
//실제 로그인 박스
Container(
width: 400,
height: 276.0,
decoration: BoxDecoration(
color: Color(0xFFffffff),
borderRadius: BorderRadius.circular(16),
),
padding: const EdgeInsets.all(40.0),
child: Center(
child: Obx(() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ID 입력
Container(
height: 40,
decoration: BoxDecoration(
color: Color(0xFFF3F4F8),
borderRadius: BorderRadius.circular(inputRadius),
),
child: TextField(
onTapOutside: (event) {
ctrl.idFocus.unfocus();
},
onChanged: (text) {
// 입력된 텍스트가 있을 때
},
textAlignVertical: TextAlignVertical.center,
controller: ctrl.textIdController,
focusNode: ctrl.idFocus,
cursorColor: Color(0xFFffbc00),
cursorHeight: 18,
style: TextStyle(
fontSize: Constants.FONT_SZIE_DEFAULT,
fontWeight: FontWeight.w500,
color: Color(0xFFffbc00),
),
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16),
border: InputBorder.none,
hintText: ctrl.isIdFocused.value ? null : '아이디',
hintStyle: hintStyle,
// 비활성화 상태
enabledBorder: enabledBorder,
// 포커스 상태
focusedBorder: focusedBorder,
),
onSubmitted: (value) {
FocusScope.of(context).requestFocus(ctrl.pwFocus);
},
),
),
const SizedBox(height: 8),
// 비밀번호
Container(
height: 40,
decoration: BoxDecoration(
color: Color(0xFFF3F4F8),
borderRadius: BorderRadius.circular(inputRadius),
),
child: TextField(
onTapOutside: (event) {
ctrl.pwFocus.unfocus();
},
onChanged: (text) {
if (text.isNotEmpty) {
ctrl.hideIsObscuredBtn.value = false;
} else {
ctrl.hideIsObscuredBtn.value = true;
}
},
textAlignVertical: TextAlignVertical.center,
controller: ctrl.textPwController,
focusNode: ctrl.pwFocus,
obscureText: ctrl.isObscured.value,
style: TextStyle(
fontSize: Constants.FONT_SZIE_DEFAULT,
fontWeight: FontWeight.w500,
color: Color(0xFF494A4D),
),
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16),
border: InputBorder.none,
hintText: ctrl.isPwFocused.value ? null : '비밀번호',
hintStyle: hintStyle,
// 비활성화 상태
enabledBorder: enabledBorder,
// 포커스 상태
focusedBorder: focusedBorder,
suffixIcon: ctrl.hideIsObscuredBtn.value
? null
: customButton(
width: 40,
height: 40,
child: ctrl.isObscured.value
? Image.asset(
"assets/image/view.png",
width: 16,
height: 16,
)
: Image.asset(
"assets/image/blind.png",
width: 16,
height: 16,
),
onTap: () {
ctrl.isObscured.value = !ctrl.isObscured.value;
},
),
),
onSubmitted: (value) {
bool result = ctrl.loginCheck();
if (result) {
ctrl.loginRequest(ctrl.textIdController.text,
ctrl.textPwController.text);
}
},
),
),
const SizedBox(height: 8),
// 아이디 저장
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
customButton(
width: 16,
height: 16,
onTap: () {
ctrl.isChecked.value = !ctrl.isChecked.value;
},
child: ctrl.isChecked.value
? Image.asset(
"assets/image/check_o.png",
width: 16,
height: 16,
)
: Image.asset(
"assets/image/check_n.png",
width: 16,
height: 16,
),
),
const SizedBox(width: 4),
customTextWidget(
'아이디저장',
color: Color(0xFF929499),
fontWeight: FontWeight.w500,
fontSize: Constants.FONT_SZIE_SMALL2,
),
],
),
const SizedBox(height: 12),
SizedBox(
height: 20,
child: customTextWidget(
ctrl.loginFailText.value,
color: Color(0xFFE84C3F),
fontSize: Constants.FONT_SZIE_SMALL2,
fontWeight: FontWeight.w300,
align: TextAlign.start,
),
),
const SizedBox(height: 8),
// 로그인 버튼
customButton(
onTap: ctrl.isActiveLoginBtn.value
? () {
bool result = ctrl.loginCheck();
if (result) {
ctrl.loginRequest(ctrl.textIdController.text,
ctrl.textPwController.text);
}
}
// 비활성화일 때 물결 효과를 없애기 위해 null 처리
: null,
width: double.infinity,
height: 42,
radius: 4,
color: ctrl.isActiveLoginBtn.value
? Color(0xff335CEA)
: Color(0xFFE8E9ED),
child: Text(
'로그인',
style: TextStyle(
color: ctrl.isActiveLoginBtn.value
? Color(0XFFffffff)
: Color(0xFFAAACB2),
),
),
),
],
);
}),
),
);
//로그인 컨트롤러
class LoginPageController extends GetxController {
final textIdController = TextEditingController(); //TextEditingController
final textPwController = TextEditingController();
var isLogin = false.obs; //로그인 했나 안했나 확인
var isChecked = false.obs; //아이디 저장을 체크 했나 안했나 확인
String id = ''; //id를 저장할 변수
var idFocus = FocusNode(); //FocusNode 객체 생성
var pwFocus = FocusNode();
var isIdFocused = false.obs; // id가 Focus 되었나 안되었나
var isPwFocused = false.obs; // pw가 Focus 되었나 안되었나
var isObscured = true.obs; //작성한 내용을 볼 것인지 말 것인지
var hideIsObscuredBtn = true.obs; //작성한게 없으면 버튼을 안보이게 할 상태관리 변수
var isActiveLoginBtn = false.obs; // 로그인 버튼 활성화 여부
}
'Flutter' 카테고리의 다른 글
Flutter 네이버 그린닷 구현 (feat. 네이버 스마트렌즈) _ 2 (0) | 2024.10.09 |
---|---|
Flutter 네이버 그린닷 구현 (feat. 네이버 스마트렌즈) _ 1 (0) | 2024.10.07 |
Flutter API 통신을 간편하게 해주는 dio 라이브러리 (0) | 2024.09.20 |
ModalBarrier 활용하기 (0) | 2024.09.02 |
Flutter 녹음 시 동그란 파동 웨이브 구현 (round_wave) (0) | 2024.08.22 |