inblog logo
|
Gyeongwon's blog
    FlutterDart

    [Flutter #10] Flutter 로그인 화면 만들기 (Navigator·Form·TextFormField·Theme·SVG·size)

    도경원's avatar
    도경원
    Sep 28, 2025
    [Flutter #10] Flutter 로그인 화면 만들기 (Navigator·Form·TextFormField·Theme·SVG·size)
    Contents
    1. size.dart로 여백/사이즈 관리하기2. Navigator로 화면 이동 (push/pop) & Named Routes3) Form 위젯으로 입력 묶고, 한 번에 유효성 검사4) TextFormField: 키보드 인셋·스크롤·유효성 검사5) SVG 위젯 사용하기6) 앱 전체 디자인을 Theme로 통일하기 (TextButton 예시)

    1. size.dart로 여백/사이즈 관리하기

    왜? ListView 같은 스크롤 위젯은 세로 길이가 사실상 무한대라서 Expanded나 Spacer를 쓸 수 없다. 이런 경우엔 SizedBox 로 높이를 직접 지정해 주자.
    어떻게? 무작정 숫자를 박기보다 size.dart 파일로 상수화하여 일관성 있게 관리!
    예시:
    // size.dart const double small_gap = 8.0; const double medium_gap = 16.0; const double large_gap = 24.0;
    스크린 곳곳에서:
    SizedBox(height: small_gap), SizedBox(height: medium_gap), SizedBox(height: large_gap),

    2. Navigator로 화면 이동 (push/pop) & Named Routes

    • Navigator = 스택(Stack) 기반 화면 관리자
      • push: 화면 위에 올리기
      • pop: 현재 화면 걷어내기(이전 화면으로 돌아감)
    // 예: 일반 push Navigator.push( context, MaterialPageRoute(builder: (_) => const HomePage()), ); // 예: pop (현재 화면 제거 → 이전 화면 노출) Navigator.pop(context);

    Named Routes(명명된 경로)

    • 경로를 문자열로 관리(예: "/login", "/home").
    • 라우트 맵을 미리 등록해 두면 깔끔하게 네비게이션 가능.
    MaterialApp( initialRoute: "/login", routes: { "/login": (context) => const LoginPage(), "/home": (context) => const HomePage(), }, );
    // 이동 Navigator.pushNamed(context, "/home"); // 혹은 현재 화면 닫고 이전으로 Navigator.pop(context);

    3) Form 위젯으로 입력 묶고, 한 번에 유효성 검사

    • Form = 입력 위젯(TextFormField 등)을 묶는 컨테이너
    • 여러 필드를 한 번에 유효성 검사하거나 전송할 때 사용
    • GlobalKey<FormState>로 상태를 잡고, 버튼에서 validate() 호출
    class CustomForm extends StatelessWidget { CustomForm({super.key}); final _formKey = GlobalKey<FormState>(); // 1) 글로벌 키 @override Widget build(BuildContext context) { return Form( key: _formKey, // 2) Form에 연결 child: Column( children: [ CustomTextFormField("Email"), const SizedBox(height: 16), CustomTextFormField("Password"), const SizedBox(height: 24), TextButton( onPressed: () { // 3) 버튼에서 검증 실행 → 각 필드의 validator 동작 if (_formKey.currentState!.validate()) { // true일 때 다음 화면 이동 or 로그인 요청 Navigator.pushNamed(context, "/home"); } }, child: const Text("Login"), ), ], ), ); } }

    4) TextFormField: 키보드 인셋·스크롤·유효성 검사

    • 키보드가 올라오면(인셋 영역) 화면 그릴 수 없는 높이가 생겨 Overflow 위험
    • 해결: 상위에 스크롤을 달아주자(예: SingleChildScrollView or ListView)
    • TextFormField는 validator로 유효성 검사 가능
    class CustomTextFormField extends StatelessWidget { final String text; const CustomTextFormField(this.text); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(text), SizedBox(height: small_gap), TextFormField( // 1. 느낌표는 null이 절대 아니다 라고 컴파일러에게 알려주는 것 validator: (value) => value!.isEmpty ? "Please enter some text" : null, // 값이 없으면 Please enter some text 경고 화면 표시 obscureText: // 2. 해당 TextFormField가 비밀번호 입력 양삭이면 **** 처리 해주기 text == "Password" ? true : false, decoration: InputDecoration( hintText: "Enter $text", enabledBorder: OutlineInputBorder( // 3. 기본 TextFormField 디자인 borderRadius: BorderRadius.circular(20), ), focusedBorder: OutlineInputBorder( // 4. 손가락 터치시 TextFormField 디자인 borderRadius: BorderRadius.circular(20), ), errorBorder: OutlineInputBorder( // 5. 에러발생시 TextFormField 디자인 borderRadius: BorderRadius.circular(20), ), focusedErrorBorder: OutlineInputBorder( // 6. 에러가 발생 후 손가락을 터치했을 때 TextFormField 디자인 borderRadius: BorderRadius.circular(20), ), ), ), ], ); } }
    Tip
    즉시 에러를 보고 싶다면 autovalidateMode: AutovalidateMode.onUserInteraction 를 추가하면 입력/포커스 시 바로 validator가 동작한다.
    예:
    TextFormField( autovalidateMode: AutovalidateMode.onUserInteraction, validator: (value) => (value == null || value.trim().isEmpty) ? "Please enter some text" : null, ... )

    5) SVG 위젯 사용하기

    SVG(Vector) = 확대/축소해도 깨지지 않는 벡터 이미지
    • Flutter에선 flutter_svg 패키지의 SvgPicture 위젯 사용
    • pubspec.yaml에 의존성 추가 후, 에셋 경로 등록하여 호출
    dependencies: flutter_svg: ^2.0.0
    import 'package:flutter_svg/flutter_svg.dart'; SvgPicture.asset( 'assets/images/logo.svg', width: 120, height: 120, )

    6) 앱 전체 디자인을 Theme로 통일하기 (TextButton 예시)

    • 반복되는 버튼 디자인은 개별 위젯 대신 Theme에 정의 → 앱 전역 일관성 + 생산성 UP
    • 최신 Flutter(Material 3)에서는 TextButton.styleFrom의 primary가 제거되고, foregroundColor 를 사용
    import 'package:flutter/material.dart'; import 'package:login_app/pages/home_page.dart'; import 'package:login_app/pages/login_page.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( // 테마 설정 theme: ThemeData( textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( backgroundColor: Colors.black, foregroundColor: Colors.white, // ← primary 대신 foregroundColor shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), minimumSize: const Size(400, 60), ), ), ), debugShowCheckedModeBanner: false, initialRoute: "/login", routes: { "/login": (context) => const LoginPage(), "/home": (context) => const HomePage(), }, ); } }

    Share article
    Contents
    1. size.dart로 여백/사이즈 관리하기2. Navigator로 화면 이동 (push/pop) & Named Routes3) Form 위젯으로 입력 묶고, 한 번에 유효성 검사4) TextFormField: 키보드 인셋·스크롤·유효성 검사5) SVG 위젯 사용하기6) 앱 전체 디자인을 Theme로 통일하기 (TextButton 예시)

    Gyeongwon's blog

    RSS·Powered by Inblog