[Flutter] Flutter 예제 - Login App 만들기

류재성's avatar
Apr 16, 2024
[Flutter] Flutter 예제 - Login App 만들기
 

1. 기본 세팅

notion image
 
assets 폴더를 만든 후 svg 파일을 넣는다.
 
notion image
notion image
pubspect.yaml 설정 후 pub get 을 누른다.
 

2. 테마 만들기

 
theme.dart
import 'package:flutter/material.dart'; final ThemeData appTheme = ThemeData( textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( backgroundColor: Colors.black, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), minimumSize: Size(400, 60), ), ), );
 
main.dart
import 'package:flutter/material.dart'; import 'theme.dart'; // 사용자 정의 테마를 import합니다. void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: appTheme, // 테마 지정 ); } }
 
 

3. Svg 파일 불러오기

 
main.dart
import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'theme.dart'; // 사용자 정의 테마를 import합니다. void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: appTheme, // 테마 지정 // initialRoute: "/login", home: Scaffold( body: ListView( children: [ SizedBox(height: 100), Column( children: [ SvgPicture.asset( "assets/logo.svg", height: 70, width: 70, ), Text( "Login", style: TextStyle(fontSize: 40,fontWeight: FontWeight.bold), ), ], ), ], ), ), ); } }
 
notion image
 
 

4. Route 사용하기

 
💡
Route 는 화면을 이동할 때 사용한다. initialRoute : 앱이 시작할 때 최초로 시작될 화면을 지정한다. routes : 이동할 화면을 설정한다. 익명함수를 통해 설정한다.
pages/login_page.dart
import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class LoginPage extends StatelessWidget { const LoginPage({ super.key, }); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: 100), Column( children: [ SvgPicture.asset( "assets/logo.svg", height: 70, width: 70, ), Text( "Login", style: TextStyle(fontSize: 40,fontWeight: FontWeight.bold), ), ], ), ], ), ); } }
 
Scaffold 이하의 코드를 LoginPage 로 컴퍼넌트를 분리한다. 해당 페이이지는 “/login” 주소로 설정한다.
 
main.dart
import 'package:flutter/material.dart'; import 'package:login_app/page/login_page.dart'; import 'theme.dart'; // 사용자 정의 테마를 import합니다. void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: appTheme, // 테마 지정 initialRoute: "/login", // 앱이 시작하면 로그인 페이지로 이동 routes: { "/login": (context) => LoginPage(), // "/login"가 요청되면 LoginPage() 를 호출한다 }, ); } }
 
notion image
 

5. 로그인 페이지 디자인하기

 

5.1. Form 웨젯, TextFormField

 
💡
Form 위젯과 TextFormField를 사용해 유효성 검사를 쉽게 할 수 있다.
 
import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class LoginPage extends StatelessWidget { final GlobalKey<FormState> formKey = GlobalKey<FormState>(); LoginPage({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: ListView( // 키패드가 올라오면 화면을 기리기때문에 ListView 로 만듬 children: [ SizedBox(height: 100), Column( children: [ SvgPicture.asset( "assets/logo.svg", height: 70, width: 70, ), Text( "Login", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold), ), Form( key: formKey, child: Column( children: [ Text('Email'), SizedBox(height: 20), TextFormField( validator: (value) => value!.isEmpty ? "Please enter some text" : null, decoration: InputDecoration( hintText: "Enter email", enabledBorder: OutlineInputBorder( // 기본 디자인 borderRadius: BorderRadius.circular(20), ), focusedBorder: OutlineInputBorder( // 손가락 터치시 디자인 borderRadius: BorderRadius.circular(20), ), errorBorder: OutlineInputBorder( //에러 발생시 borderRadius: BorderRadius.circular(20), ), focusedErrorBorder: OutlineInputBorder( // 에러 발생 후 손가락 터치했을 때 borderRadius: BorderRadius.circular(20), ), ), ), ], ), ), ], ), ], ), ); } }
 
notion image
notion image
 
 

5.2. 컴포넌트 분리, 변수화하기

 
pages/login_page.dart
import 'package:flutter/material.dart'; import '../components/custom_form.dart'; import '../components/logo.dart'; class LoginPage extends StatelessWidget { LoginPage({Key?key}) :super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: ListView( // 키패드가 올라오면 화면을 기리기때문에 ListView 로 만듬 children: [ SizedBox(height: 100), Logo(logoText: "Login"), CustomForm(), ], ), ); } }
 
components/logo.dart
import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class Logo extends StatelessWidget { final logoText ; Logo({required this.logoText}); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset( "assets/logo.svg", height: 70, width: 70, ), Text( logoText, style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold), ), ], ); } }
 
components/custom_text_form.dart
import 'package:flutter/material.dart'; class CustomTextFormField extends StatelessWidget { final text; CustomTextFormField({required this.text}); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(text), TextFormField( validator: (value) => value!.isEmpty ? "Please enter some text" : null,// 입력값이 없다면 출력 obscureText: text == "Password" ? true : false, // 비밀번호를 입력하면 **** 처리하기 decoration: InputDecoration( hintText: "Enter ${text}", enabledBorder: OutlineInputBorder( // 기본 디자인 borderRadius: BorderRadius.circular(20), ), focusedBorder: OutlineInputBorder( // 손가락 터치시 디자인 borderRadius: BorderRadius.circular(20), ), errorBorder: OutlineInputBorder( //에러 발생시 borderRadius: BorderRadius.circular(20), ), focusedErrorBorder: OutlineInputBorder( // 에러 발생 후 손가락 터치했을 때 borderRadius: BorderRadius.circular(20), ), ), ), ], ); } }
 
components/custom_form.dart
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'custom_text_form_field.dart'; class CustomForm extends StatelessWidget { final GlobalKey<FormState> formKey = GlobalKey<FormState>(); CustomForm({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Form( key: formKey, child: Column(children: [ CustomTextFormField(text: "Email"), // 컴포넌트로 만들어 재사용할 수 있다. SizedBox(height: 20), CustomTextFormField(text: "Password"), ], ), ); } }
 
 

6. 버튼 만들기

 
custom_form.dart
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'custom_text_form_field.dart'; class CustomForm extends StatelessWidget { final GlobalKey<FormState> formKey = GlobalKey<FormState>(); // 추가된 부분 CustomForm({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Form( key: formKey, // 수정된 부분 child: Column( children: [ CustomTextFormField(text: "Email"), SizedBox(height: 20), CustomTextFormField(text: "Password"), SizedBox(height: 20), TextButton( onPressed: () { //버튼을 누르면 "/home" if (formKey.currentState!.validate() ) { Navigator.pushNamed(context, "/home"); } }, child: Text("Login"), ) ], ), ); } }
 
notion image
버튼이 생성되었다.
 
notion image
 
버튼을 누르면 유효성 검사가 된다.
 
main.dart
import 'package:flutter/material.dart'; import 'package:login_app/page/login_page.dart'; import 'theme.dart'; // void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: appTheme, // 테마 지정 initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home" :(context) => HomePage(), }, ); } }
 
버튼을 누르면 “/home” 이 요청된다. 버튼을 누르면 이동될 HomePage 클래스를 만들자.
 

7. HomePage 만들기

 
pages/home_page.dart
import 'package:flutter/material.dart'; import '../components/logo.dart'; class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ SizedBox(height: 200), Logo(logoText: "Care Soft"), ], ), ); } }
 
notion image
 
버튼을 누르면 “/home” 으로 이동한다.
Share article
RSSPowered by inblog