1. 기본 세팅

assets 폴더를 만든 후 svg 파일을 넣는다.


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), ), ], ), ], ), ), ); } }

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() 를 호출한다 }, ); } }

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), ), ), ), ], ), ), ], ), ], ), ); } }


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"), ) ], ), ); } }

버튼이 생성되었다.

버튼을 누르면 유효성 검사가 된다.
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"), ], ), ); } }

버튼을 누르면 “/home” 으로 이동한다.
Share article