Flutter Day 14: 블로그 만들기 - 프로젝트 기본 설정, 로그인 UI 구성
Flutter에서 로그인 기능을 구현하려면, 먼저 프로젝트를 설정하고 기본적인 UI를 만들어야 합니다.
이 글에서는 프로젝트 설정 → 기본 UI 구성 → 로그인 화면 구현 → 입력값 검증 → UI 디테일 보완까지
차근차근 정리해보겠습니다.
1. 프로젝트 기본 설정
Flutter 프로젝트에서 사용할 필수 패키지를 추가합니다.
pubspec.yaml 수정
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
flutter_svg: ^2.0.6
intl: ^0.18.1
dio: ^5.2.0 # 서버와 통신하는 HTTP 클라이언트
flutter_riverpod: ^2.3.6 # 상태 관리를 위한 Riverpod
logger: ^1.3.0 # 콘솔 로그 출력용 라이브러리
flutter_secure_storage: ^8.0.0 # JWT 토큰을 저장하는 보안 라이브러리
pull_to_refresh: ^2.0.0 # 화면 새로고침 기능
- dio → 서버와 데이터를 주고받을 때 사용 (API 호출)
- flutter_riverpod → 앱의 전역 상태를 관리하는 라이브러리
- flutter_secure_storage → JWT 토큰 저장 등 보안이 필요한 데이터 저장
- logger → 콘솔에 출력할 때 사용 (디버깅에 유용함)
2. 프로젝트 코드 스타일 유지 (Linter 설정)
Linter는 코드 스타일을 검사하고 오류를 사전에 방지하는 역할을 합니다.
linter:
rules:
prefer_const_constructors: false
prefer_const_declarations: false
prefer_const_literals_to_create_immutables: false
Linter는 코드 스타일을 체크하고 버그 가능성이 있는 코드를 경고해주는 도구입니다.
Dart에서는 linter 설정을 통해 프로젝트의 코딩 스타일을 통일할 수 있습니다.
✔ 코드 스타일을 일관되게 유지
✔ 잠재적인 버그를 사전에 방지
✔ 비효율적인 코드 패턴을 피할 수 있음
3. 앱 테마 및 공통 UI 설정
앱의 전반적인 테마(글꼴, 색상, 스타일)을 설정하고, 여백(Gap)도 전역적으로 관리하면 좋습니다.
theme.dart (앱 테마 설정)
import 'package:flutter/material.dart';
ThemeData theme() {
return ThemeData(
appBarTheme: const AppBarTheme(
titleTextStyle: TextStyle(color: Colors.white, fontSize: 20),
centerTitle: true,
backgroundColor: Colors.black12,
elevation: 0,
),
useMaterial3: true, // 최신 Material3 스타일 적용
);
}
size.dart (전역 여백 설정)
const double smallGap = 8.0;
const double mediumGap = 16.0;
const double largeGap = 32.0;
공통적인 여백(Gap)을 설정해 두면, 코드가 더 깔끔해집니다.
4. 프로젝트 기본 UI 구성
로그인, 회원가입, 게시글 화면을 만들고, 이를 Navigator로 관리할 수 있도록 설정합니다.
📌 주요 페이지 생성 (lib/ui/pages/auth/)
import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder(); // UI 구현 전, 자리 표시자
}
}
class JoinPage extends StatelessWidget {
const JoinPage({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
추가로 PostListPage, PostWritePage도 같은 방식으로 생성하면 됩니다.
5. 화면 전환을 위한 Navigator 설정
Flutter에서는 Navigator를 사용하여 페이지 이동을 관리합니다.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:class_f_story/ui/pages/auth/login_page.dart';
import 'package:class_f_story/ui/pages/auth/join_page.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
// 전역 Navigator 키 설정
GlobalKey<NavigatorState> navigatorkey = GlobalKey<NavigatorState>();
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorkey,
debugShowCheckedModeBanner: false,
home: LoginPage(),
routes: {
"/login": (context) => LoginPage(),
"/join": (context) => JoinPage(),
},
theme: theme(),
);
}
}
이제 Navigator.pushNamed(context, "/join") 같은 방식으로 화면 이동을 쉽게 할 수 있습니다.
✔ GlobalKey를 사용하면 현재 화면(Context)에 접근 가능
✔ ProviderScope → Riverpod 상태 관리를 적용
✔ navigatorKey → 전역적으로 Navigator 상태 관리
✔ routes → 앱에서 이동할 수 있는 페이지 설정
6. 로그인 UI 구현
이제 로그인 화면을 직접 만들어 보겠습니다.
LoginPage 기본 구조
import 'package:class_f_story/ui/pages/auth/login_page/widgets/login_body.dart';
import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: LoginBody(),
);
}
}
별도의 LoginBody 위젯을 만들어 UI를 정리합니다.
로그인 입력 필드 및 버튼 추가 (LoginBody)
import 'package:flutter/material.dart';
import 'package:class_f_story/ui/widgets/custom_auth_text_form_field.dart';
import 'package:class_f_story/ui/widgets/custom_elevated_button.dart';
import 'package:class_f_story/ui/widgets/custom_logo.dart';
import 'package:class_f_story/ui/widgets/custom_text_button.dart';
class LoginBody extends StatelessWidget {
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
LoginBody({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: [
CustomLogo('f-story'),
CustomAuthTextFormField(
text: 'Username',
controller: _usernameController,
),
const SizedBox(height: 20),
CustomAuthTextFormField(
text: 'Password',
controller: _passwordController,
obscureText: true,
),
const SizedBox(height: 20),
CustomElevatedButton(
text: '로그인',
click: () {
String username = _usernameController.text.trim();
String password = _passwordController.text.trim();
if (username.isEmpty || password.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('아이디와 비밀번호를 입력해주세요.')),
);
return;
}
// TODO - 로그인 처리 로직 추가 예정
},
),
CustomTextButton(
text: '회원가입 페이지로 이동',
click: () {
Navigator.pushNamed(context, '/join');
},
),
],
),
);
}
}
✅ 입력값 검증 추가 → 아이디 또는 비밀번호를 입력하지 않으면 오류 메시지 표시
✅ UI 개선 → 버튼 클릭 시 공백 제거 (trim())
마무리
✔ 프로젝트 설정 (패키지 추가, Linter 설정, 테마 설정)
✔ 기본 UI 구성 및 화면 이동 설정
✔ 로그인 화면 UI 디자인
✔ 입력값 검증 추가 (아이디/비밀번호 확인)
다음에는 Dio를 활용한 로그인 API 연동을 진행해보겠습니다.