[Flutter] 7. Login App v1 : Prototype

문정준's avatar
May 29, 2025
[Flutter] 7. Login App v1 : Prototype
 

1. Project 생성

  • 이전 방법 참조

2. 경로 설정

  • main.dart
    • 로그인 버튼을 눌렀을 때 다른 화면으로 이동하도록 주소 설정
    • routes : 어떤 경로를 받았을 때 동작할 함수(행위) 결정
import 'package:flutter/material.dart'; import 'package:loginapp/page/home_page.dart'; import 'package:loginapp/page/login_page.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp(); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, initialRoute: "/login", routes: {"/home": (context) => HomePage(), "/login": (context) => LoginPage()}, ); } }
 

3. 코드 작성

  • login_page.dart
    • 로그인 화면
    • 이메일, 비밀번호를 입력하고 로그인 버튼을 누르면 메인 화면으로 넘어가도록 설정
    • ListView : 앱의 내부 키보드로 인해 화면이 줄어듬 → Column으로 지정할 경우 화면이 깨짐
import 'package:flutter/material.dart'; import 'package:loginapp/component/m_form.dart'; import 'package:loginapp/component/m_logo.dart'; import '../size.dart'; class LoginPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ListView( children: [ SizedBox(height: xxlGap), MLogo("Login"), SizedBox(height: lGap), MForm(), ], ), ), ); } }
 
  • home_page.dart
    • 로그인 한 이후 메인 화면
    • Navigator.pop(context) : 이전 화면으로 돌아가기
import 'package:flutter/material.dart'; import 'package:loginapp/component/m_button.dart'; import 'package:loginapp/component/m_logo.dart'; import '../size.dart'; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ListView( children: [ SizedBox(height: xxlGap), MLogo("Care Soft"), SizedBox(height: lGap), MButton("Get Started", () { Navigator.pop(context); }), ], ), ), ); } }
 
  • m_logo.dart
    • SVG 파일을 사용하여 크기가 커져도 이미지가 깨지지 않음 : 벡터 파일
 

Widgets

  • SvgPictures.asset : asset에서 SVG 파일을 받아서 출력하는 위젯
import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; class MLogo extends StatelessWidget { String title; MLogo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("logo.svg", width: 70, height: 70), Text( title, style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold), ), ], ); } }
 
  • m_form.dart
    • 데이터를 서버에 전송하기 위한 form
    • 데이터를 전송하기 위해 필요한 컴포넌트들은 form에 묶어서 사용
    • CrossAxisAlignment.stretch : 자식들의 보조 축 길이(너비)를 화면 끝까지 늘려줌
      • minWidth : double.infinity
    • TextEditingController : text에 대한 처리가 가능한 Controller
      • .text : 입력 받은 데이터 값을 Text로 반환
 

Widgets

  • Form : 데이터를 전달하기 위해 사용하는 위젯
import 'package:flutter/material.dart'; import 'package:loginapp/component/m_button.dart'; import 'package:loginapp/component/m_text_box.dart'; import '../size.dart'; class MForm extends StatelessWidget { TextEditingController _email = TextEditingController(); TextEditingController _password = TextEditingController(); void submit(BuildContext context) { var requestBody = {"email": _email.text, "password": _password.text}; print("전송할 데이터 : $requestBody"); Navigator.pushNamed(context, "/home"); } @override Widget build(BuildContext context) { return Form( child: Column( // min 값을 double.infinity로 늘려줌 crossAxisAlignment: CrossAxisAlignment.stretch, children: [ MTextBox("Email", _email), SizedBox(height: sGap), MTextBox("Password", _password, isPassword: true), SizedBox(height: mGap), MButton("Login", () => submit(context)), ], ), ); } }
 
  • m_text_box.dart
    • 텍스트를 입력 받아 TextEditingController에 전달하는 컴포넌트
    • bool isPassword를 통해 비밀번호를 입력받을 경우 입력칸을 *로 표시하게 설정 가능
import 'package:flutter/material.dart'; import 'package:loginapp/component/m_text_field.dart'; class MTextBox extends StatelessWidget { String division; bool isPassword; TextEditingController controller; MTextBox(this.division, this.controller, {this.isPassword = false}); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(division), MTextField("Enter $division", controller, isPassword: isPassword), ], ); } }
 
  • m_text_field.dart
    • TextEditingController를 통해 입력값을 전달
    • 스타일 설정 가능
      • Enabled(Default), Focused, Error, FocusedError 등 4가지 경우에 대해 설정 가능
 

Widgets

  • TextFormField : 텍스트를 입력할 수 있는 위젯
    • controller : TextEditingController를 연결
    • decoration : 해당 위젯의 스타일 지정
    • obscureText : 텍스트를 ‘*’로 표시할 지에 대한 여부
    • hintText : 텍스트가 입력되지 않았을 때 표시할 String = placeholder
    • hintStyle : hintText의 스타일 지정
import 'package:flutter/material.dart'; class MTextField extends StatelessWidget { String placeholder; bool isPassword; TextEditingController controller; MTextField(this.placeholder, this.controller, {this.isPassword = false}); @override Widget build(BuildContext context) { return TextFormField( controller: controller, obscureText: isPassword, decoration: InputDecoration( // 1. enabledBorder : 기본 디자인 // 2. focusedBorder : 포커스가 잡혔을 때 기본 디자인 // 3. errorBorder : 기본 에러 디자인 // 4. focusedErrorBorder : 포커스가 잡혔을 때 기본 에러 디자인 border: OutlineInputBorder(borderRadius: BorderRadius.circular(15)), errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15)), focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15)), hintText: placeholder, hintStyle: TextStyle(color: Colors.black26), ), ); } }
 
  • m_button.dart
    • 입력값을 서버 측에 전달하고, 화면을 전환하는 버튼을 생성하는 컴포넌트
    • 입력값은 submit이라는 함수로 콘솔에 출력하는 행위를 통해 전달
import 'package:flutter/material.dart'; class MButton extends StatelessWidget { String title; var submit; MButton(this.title, this.submit); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: submit, style: ElevatedButton.styleFrom(backgroundColor: Colors.black, fixedSize: Size(double.infinity, 60)), child: Text( title, style: TextStyle(color: Colors.white), ), ); } }
 
  • size.dart
    • 각 화면에서 사용할 수 있는 gap의 크기를 미리 지정하여 사용
    • 디자인의 통일성 부여
double sGap = 5; double mGap = 10; double lGap = 20; double xxlGap = 100;
 

4. Result

  • 로그인 화면
notion image
 
  • 이메일, 비밀번호 입력 후 Login 버튼을 누르면 메인 화면으로 이동 및 콘솔에 정보 출력
notion image
 
  • 메인 화면
notion image
 
  • 콘솔
notion image
Share article

sxias