- 라우터 Router
- 사용자 입력 받기 -TextFormField / shrinkWrap
- 로그인 입력 화면 - Flexible / shrinkWrap / ListView
1. 라우터
1-1. 화면


1-2. 개념
- Named Routes 방식
MaterialApp
의routes
속성에 경로 이름(/login, /home)과 해당 경로에 연결된 위젯을 매핑하여 사용.Navigator.pushNamed()
를 사용해 화면을 전환한다.
- initialRoute 초기 경로 설정 앱 시작시 보여지는 초기 화면을 설정. /login 경로와 연결된 LoginPage가 나온다.
- 화면 전환 구현
Navigator.pushNamed(context, "/home");
화면 이동. 이름이 등록된 경로로 이동하며, 화면 스택에 새로운 화면을 추가.Navigator.pop(context);
: 뒤로 가기 현재 화면을 제거하고 이전 화면으로 돌아가기
1-3. 코드
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// 라우터 -버튼 누르면 화면 이동
initialRoute: "/login", // 초기화면 설정
routes: {
"/login": (context) => LoginPage(),
"/home": (context) => HomePage(),
},
);
}
}
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("LoginPage")),
// Align 위치 지정가능
body: Align(
alignment: Alignment(0, 0), // -1, -1 -> 1, 1 묵시적 형변환
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/home");
// 경로를 직접 설정하고 데이터를 전달할 수 있는 익명 라우팅 방식
// Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage()));
},
child: Text("로그인")),
),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("HomePage")),
body: Align(
alignment: Alignment(0, 0),
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("돌아가기")),
),
);
}
}
2. 사용자 입력받기
TextFormField
는 사용자로부터 입력값을 수집하는 입력 필드.여러 입력 항목(예: Username, Password, Email)을 화면에 배치할 때, 스크롤 가능하도록
ListView
를 사용하기.2-1. 화면

2-2. 개념
- TextFormField
- 사용자 입력을 받기 위해 사용되는 위젯
- 폼 입력을 쉽게 검증하고 관리가능하게
Form
과 함께 사용 - 텍스트 입력 필드의 상위 버전으로, 입력 검증 및 상태 관리 기능이 추가
- ListView의 shrinkWrap
- ListView는 기본적으로 스크롤 가능한 영역의 최대 크기(스크롤 가능한 부모 위젯 전체)를 차지한다.
shrinkWrap: true
를 설정 시 ListView가 자식들의 크기만큼만 공간을 차지하도록 제한된다.
2-3. 코드
package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: LoginPage(),
);
}
}
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: Column(
children: [
Flexible( // ListView 크기를 조정하여 화면의 남은 공간을 사용
child: ListView(
shrinkWrap: true, // 자식 요소 만큼 크기 축소
children: [
Container(
height: 400,
color: Colors.redAccent,
),
TextFormField(),
TextFormField(),
TextFormField(),
],
),
),
ElevatedButton(onPressed: () {}, child: Text("로그인"))
],
),
);
}
}
3. 로그인 입력 화면
3-1. 화면

3-2. 개념
- Flexible
- 코드에 사용되지 않음
- Column 이나 Row 안 위젯의 크기를 유동적으로 조정할 때 사용.
- 부모 위젯의 남은 공간을 비율에 따라 나누어 사용하는 위젯.
- 비슷한 위젯: Expanded 남은 공간을 모두 차지.
- shrinkWrap
- 리스트뷰가 자식 요소만큼만 크기를 차지하도록 제어.
- 스크롤 컨텍스트 내부에 중첩된
ListView
에 유용.
- ListView
- 특징
- 스크롤 가능한 위젯으로, 자식 위젯들을 세로 또는 가로 방향으로 나열.
- 자식 위젯을 배열하거나 레이아웃을 구성할 때 사용.
- 적용
- 로그인 화면의 전체 내용을 감싸는 주요 컨테이너로 사용.
LoginPage
화면에서 모든 입력 필드와 버튼을 스크롤 가능하도록 감쌈.- 여러 위젯을 스크롤 가능한 리스트 형태로 배치.
3-3. 코드
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: LoginPage(),
);
}
}
class LoginPage extends StatelessWidget {
LoginPage({super.key});
String? username;
final password = TextEditingController();
final email = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: ListView(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
onChanged: (value) {
username = value;
},
decoration: InputDecoration(
filled: true,
fillColor: Colors.yellow, // 배경색 설정
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
), // 외곽선 설정 (옵션)
),
),
),
CustomInput(password, "패스워드를 입력하세요", obsecure: true),
CustomInput(email, "이메일을 입력하세요"),
ElevatedButton(
onPressed: () {
fetch(username!.trim(), password.text.trim());
},
child: Text("로그인"),
),
],
),
],
),
);
}
}
class CustomInput extends StatelessWidget {
CustomInput(this.controller, this.hint, {this.obsecure = false});
final controller;
final hint;
final obsecure;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: controller,
obscureText: obsecure,
decoration: InputDecoration(
hintText: "${hint}",
suffixIcon: Icon(Icons.password),
focusedBorder: OutlineInputBorder(),
enabledBorder: OutlineInputBorder(),
),
),
);
}
}
void fetch(String username, String password) {
print("$username과 $password를 전송하였습니다");
}
Share article