flutter-2024.pdf
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
),
home: LayoutDemoPage(),
);
}
}
class LayoutDemoPage extends StatelessWidget {
const LayoutDemoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
title: Text(
'Flutter Layout Demo',
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryContainer),
),
),
body: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
'스위스에 멋지 캠프 그라운드',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Text(
'캔더스태그 / 스위스',
style: TextStyle(color: Colors.grey.shade500),
),
],
),
),
Icon(
Icons.star,
color: Colors.red,
),
Text('41')
],
),
);
}
}
Flutter Layout Demo 코드 (리팩토링 후)
- main.dart
- views/layout_demo_page.dart
- components/icon_group.dart
// main.dart
import 'package:flutter/material.dart';
import 'views/layout_demo_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
),
home: LayoutDemoPage(),
);
}
}
//views/layout_demo_page.dart
import 'package:flutter/material.dart';
import '../components/icon_group.dart';
class LayoutDemoPage extends StatelessWidget {
const LayoutDemoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
title: Text(
'Flutter Layout Demo',
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryContainer),
),
),
body: Column(
children: [
Image.asset(
'assets/m.webp',
width: 600,
height: 190,
fit: BoxFit.cover,
),
Padding(
padding: const EdgeInsets.all(32),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
'스위스에 멋지 캠프 그라운드',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Text(
'캔더스태그 / 스위스',
style: TextStyle(color: Colors.grey.shade500),
),
],
),
),
Icon(
Icons.star,
color: Colors.red,
),
Text('41')
],
),
),
IconGroup(),
Padding(
padding: const EdgeInsets.all(32),
child: Text(
'Lake Oeschinen lies at the foot of the Blüemlisalp in the '
'Bernese Alps. Situated 1,578 meters above sea level, it '
'is one of the larger Alpine Lakes. A gondola ride from '
'Kandersteg, followed by a half-hour walk through pastures '
'and pine forest, leads you to the lake, which warms to 20 '
'degrees Celsius in the summer. Activities enjoyed here '
'include rowing, and riding the summer toboggan run.',
softWrap: true,
),
)
],
),
);
}
}
// components/icon_group.dart
import 'package:flutter/material.dart';
class IconGroup extends StatelessWidget {
const IconGroup({
super.key,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [Icon(Icons.call), Text('CALL')],
),
Column(
children: [Icon(Icons.route), Text('ROUTE')],
),
Column(
children: [Icon(Icons.share), Text('SHARE')],
),
],
);
}
}
2일차
상태관리
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: GestureDemoPage(),
);
}
}
class GestureDemoPage extends StatefulWidget {
GestureDemoPage({super.key});
@override
State<GestureDemoPage> createState() => _GestureDemoPageState();
}
class _GestureDemoPageState extends State<GestureDemoPage> {
int myNumber = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$myNumber'),
ElevatedButton(
onPressed: () {
setState(() {
myNumber++;
});
print(myNumber);
},
child: Text('눌러주세요'),
),
],
),
),
);
}
}
주사위 굴리기 최종 코드
import 'dart:math';
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(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
useMaterial3: true,
),
home: const DicePage(),
);
}
}
class DicePage extends StatefulWidget {
const DicePage({super.key});
@override
State<DicePage> createState() => _DicePageState();
}
class _DicePageState extends State<DicePage> {
int _firstDiceNumber = 3;
int _secondDiceNumber = 5;
// 1~6 랜덤 숫자 생성
int _generateRandomDiceNumber() => Random().nextInt(6) + 1;
// 주사위 클릭 시 호출
void _onDiceTap() {
setState(() {
_firstDiceNumber = _generateRandomDiceNumber();
_secondDiceNumber = _generateRandomDiceNumber();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.primary,
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text('Dice App'),
),
body: Center(
child: Row(
children: [
// 주사위 이미지 빌드
_buildDice(_firstDiceNumber),
_buildDice(_secondDiceNumber),
],
),
),
);
}
// 주사위 위젯 생성
Widget _buildDice(int diceNumber) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: _onDiceTap,
child: Image.asset('images/dice$diceNumber.png'),
),
),
);
}
}
로그인 화면 코드 - 폼과 밸리데이션
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _formKey = GlobalKey<FormState>();
String _name = '로그인 해주세요';
void _onLogin() {
final form = _formKey.currentState;
if (form?.validate() ?? false) {
print('login 성공');
setState(() {
_name = '${_nameController.text} 님 로그인 하셨습니다.';
});
} else {
print('login 실패');
}
}
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Please Login'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.red, // 테두리 색상
width: 3, // 테두리 두께
),
top: BorderSide(
color: Colors.red, // 테두리 색상
width: 3, // 테두리 두께
),
left: BorderSide(
color: Colors.red, // 테두리 색상
width: 3, // 테두리 두께
),
right: BorderSide(
color: Colors.red, // 테두리 색상
width: 3, // 테두리 두께
),
),
),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: '이름'),
validator: (text) => text!.isEmpty ? '이름을 입력해 주세요' : null,
),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(labelText: '이메일'),
validator: (text) {
if (text!.isEmpty) {
return '이메일을 입력해 주세요';
}
String pattern =
r'^[a-zA-Z0-9.a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$';
RegExp regex = RegExp(pattern);
if (!regex.hasMatch(text)) {
return '올바른 이메일을 입력해 주세요';
}
return null;
},
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: _onLogin,
child: const Text('로그인'),
),
const SizedBox(
height: 20,
),
Text(
_name,
style: Theme.of(context).textTheme.headlineMedium,
)
],
),
),
),
),
);
}
}
로티파일 사용