
1. LoginPage 디자인하기
- svg: 벡터 이미지 = 글자

import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ SvgPicture.asset("assets/logo.svg"), Text("Login", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ) ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }
- 컴포넌트로 분리하기
import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ Logo("Login"), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }


size.dart
- gap에 일관성을 만들어서 여백 지정하기 → 아니면 다 무너짐
double smallGap = 5.0; double mediumGap = 10.0; double largeGap = 20.0; double xlargeGap = 100.0;
import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }

import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), Text("Email"), TextFormField( decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), ), ), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }


import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), Text("Email"), TextFormField( decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), ), ), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }

import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), Text("Email"), TextFormField( decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), ), ), ), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), Text("Email"), TextFormField( 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), ), ), ), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }

import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), Text("Email"), Form( child: Column( children: [ TextFormField( 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), ), ), ) ], )), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:login_app_test/size.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() }, ); } } class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), Text("Email"), Form( child: Column( children: [ TextFormField( 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), ), ), ), TextButton( onPressed: () {}, child: Text("login"), ), ], )), ], )); } } class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }

- 컴포넌트로 뺴기
import 'package:flutter/material.dart'; class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Align( alignment: Alignment.topCenter, child: ElevatedButton( child: Text("move login page"), onPressed: () { Navigator.pushNamed(context, "/login"); }, ), ), ); } }
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../components/custom_form.dart'; import '../components/logo.dart'; import '../size.dart'; class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ SizedBox(height: xlargeGap), Logo("Login"), CustomForm(), ], )); } }
import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter_svg/svg.dart'; class Logo extends StatelessWidget { final title; Logo(this.title); @override Widget build(BuildContext context) { return Column( children: [ SvgPicture.asset("assets/logo.svg", height: 70, width: 70), Text("${title}", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), ], ); } }
import 'package:flutter/material.dart'; class CustomForm extends StatelessWidget { const CustomForm({ super.key, }); @override Widget build(BuildContext context) { return Form( child: Column( children: [ Text("Email"), TextFormField( 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), ), ), ), TextButton( onPressed: () {}, child: Text("login"), ), ], )); }
import 'package:flutter/material.dart'; import 'package:login_app_test/pages/home_page.dart'; import 'package:login_app_test/pages/login_page.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage(), }, ); } }

import 'package:flutter/material.dart'; class CustomForm extends StatelessWidget { TextEditingController? emailController; @override Widget build(BuildContext context) { return Form( child: Column( children: [ Text("Email"), TextFormField( controller: emailController, 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), ), ), ), TextButton( onPressed: () {}, child: Text("login"), ), ], )); } }
import 'package:flutter/material.dart'; class CustomForm extends StatelessWidget { TextEditingController emailController = TextEditingController(); @override Widget build(BuildContext context) { return Form( child: Column( children: [ Text("Email"), TextFormField( controller: emailController, 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), ), ), ), TextButton( onPressed: () {}, child: Text("login"), ), ], )); } }
import 'package:flutter/material.dart'; class CustomForm extends StatelessWidget { TextEditingController email = TextEditingController(); @override Widget build(BuildContext context) { return Form( child: Column( children: [ Text("Email"), TextFormField( controller: email, 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), ), ), ), TextButton( onPressed: () {}, child: Text("login"), ), ], )); } }
Share article