와챠의 우당탕탕 코딩 일기장
[Flutter] Flutter 로 웹툰 앱 만들기: Pomodoro 앱 만들기/Timer/Flexible 본문
코딩 일기장/Flutter
[Flutter] Flutter 로 웹툰 앱 만들기: Pomodoro 앱 만들기/Timer/Flexible
minWachya 2023. 1. 27. 18:35반응형
Pomodoro 앱 만들기
- 타이머 시작 기능: 25분에서 1초씩 줄어들기
- 타이머 중지 기능: 타이머 일시 멈춤
- 타이머 재시작 기능: 25분부터 재시작
- 뽀모도로 한 번 끝나면 아래 숫자 ++
주석으로 설명 달겠슴다.
+ Flexible 위젯으로 비율 설정 가능한데, 이를 사용해서 UI를 꾸며보겠다.
Flexible(
flex: 1,
child: Container(
decoration: const BoxDecoration(color: Colors.red),
),
),
Flexible(
flex: 2,
child: Container(
decoration: const BoxDecoration(color: Colors.green),
),
),
Flexible(
flex: 1,
child: Container(
decoration: const BoxDecoration(color: Colors.blue),
),
),
완성 코드
프로퍼티들
// 타이머가 25분동안 돌기 때문에 1500이라는 숫자를 상수로 지정해둠
static const twentyFiveMinnutes = 1500; // 25분 == 1500초
int totalSeconds = twentyFiveMinnutes; // 총 시간
bool isRunning = false; // 타이머가 돌고있는지 아닌지 여부
int totalPomodoros = 0; // 뽀모도로 끝낸 횟수
late Timer timer; // 타이머
함수 부분
- 시작 버튼 클릭 리스너
- 일지 중지 버튼 클릭 리스너
- 재시작 버튼 클릭 리스너
- 1초마다 실행되는 함수
- mm:ss로 format해주는 함수
/* 시작 버튼 클릭 */
void onStartPressed() {
// 타이머 시작, onTick함수를 1초마다 호출
timer = Timer.periodic(
const Duration(seconds: 1),
onTick,
);
// 타이머가 돌고있으니 isRunnng 을 true로 변경
setState(() {
isRunning = true;
});
}
/* 타이머가 1초씩 없어지도록 함 */
void onTick(Timer timer) {
// 25분 끝나면
if (totalSeconds == 0) {
setState(() {
totalPomodoros = totalPomodoros + 1; // 총 뽀모도로 횟수 증가
isRunning = false; // 타이머 상태 변경
totalSeconds = twentyFiveMinnutes; // 타이머를 다시 25분으로 돌려놓음
});
// 타이머 중지
timer.cancel();
} else {
// 타이머 진행중이면 1초씩 빼기
setState(() {
totalSeconds = totalSeconds - 1;
});
}
}
// ---------------------
/* 일시 중지 버튼 클릭 */
void onPausePressed() {
timer.cancel(); // 타이머 중지
setState(() { // 타이머 상태 변경
isRunning = false;
});
}
// ---------
/* 재시작 버튼 클릭 */
void onReStartPressed() {
timer.cancel(); // 타이머 중지
// 타이머 상태 저장 및 총 시간 25분으로 초기화
setState(() {
isRunning = false;
totalSeconds = twentyFiveMinnutes;
});
}
// ---------
/* mm:ss로 변경 */
String format(int seconds) {
var duration = Duration(seconds: seconds); // 0:25:00.000000
return duration
.toString()
.split(".") // [0:25:00, 000000]
.first // 0:25:00
.substring(2, 7); // 25:00
}
ui부분
@override
Widget build(BuildContext context) {
return Scaffold(
// 배경색
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Column(
children: [
// 25:00 숫자 부분
Flexible(
flex: 1, // 비율
child: Container(
alignment: Alignment.bottomCenter,
// mm:ss로 포맷되어진 숫자 출력
child: Text(
format(totalSeconds),
style: TextStyle(
color: Theme.of(context).cardColor,
fontSize: 89,
fontWeight: FontWeight.w600,
),
),
),
),
// 시작<>중지 버튼, 재시작 버튼
Flexible(
flex: 3, // 비율
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 타이머가 진행중이면 중지버튼, 멈춰있으면 시작 버튼
IconButton(
onPressed: isRunning ? onPausePressed : onStartPressed,
iconSize: 120,
color: Theme.of(context).cardColor,
icon: Icon(isRunning
? Icons.pause_circle_outline
: Icons.play_circle_outline),
),
// 재시작 버튼
IconButton(
onPressed: onReStartPressed,
iconSize: 50,
color: Theme.of(context).cardColor,
icon: const Icon(Icons.restart_alt),
),
],
)),
// 뽀모도로 횟수
Flexible(
flex: 1,
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
// 모서리 둥글게
borderRadius: const BorderRadius.vertical(
top: Radius.circular(50), bottom: Radius.zero),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Pomodoros',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color:
Theme.of(context).textTheme.displayLarge!.color,
),
),
Text(
'$totalPomodoros',
style: TextStyle(
fontSize: 58,
fontWeight: FontWeight.w600,
color:
Theme.of(context).textTheme.displayLarge!.color,
),
),
],
),
),
),
],
),
),
],
),
);
}
전체코드
import 'dart:async';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const twentyFiveMinnutes = 1500;
int totalSeconds = twentyFiveMinnutes; // 25분 == 1500초
bool isRunning = false;
int totalPomodoros = 0;
late Timer timer;
void onTick(Timer timer) {
if (totalSeconds == 0) {
setState(() {
totalPomodoros = totalPomodoros + 1;
isRunning = false;
totalSeconds = twentyFiveMinnutes;
});
timer.cancel();
} else {
setState(() {
totalSeconds = totalSeconds - 1;
});
}
}
// 매 초마다 동작
void onStartPressed() {
timer = Timer.periodic(
const Duration(seconds: 1),
onTick,
);
setState(() {
isRunning = true;
});
}
void onPausePressed() {
timer.cancel();
setState(() {
isRunning = false;
});
}
void onReStartPressed() {
timer.cancel();
setState(() {
isRunning = false;
totalSeconds = twentyFiveMinnutes;
});
}
String format(int seconds) {
var duration = Duration(seconds: seconds); // 0:25:00.000000
return duration
.toString()
.split(".") // [0:25:00, 000000]
.first // 0:25:00
.substring(2, 7); // 25:00
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Column(
children: [
Flexible(
flex: 1,
child: Container(
alignment: Alignment.bottomCenter,
child: Text(
format(totalSeconds),
style: TextStyle(
color: Theme.of(context).cardColor,
fontSize: 89,
fontWeight: FontWeight.w600,
),
),
),
),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: isRunning ? onPausePressed : onStartPressed,
iconSize: 120,
color: Theme.of(context).cardColor,
icon: Icon(isRunning
? Icons.pause_circle_outline
: Icons.play_circle_outline),
),
IconButton(
onPressed: onReStartPressed,
iconSize: 50,
color: Theme.of(context).cardColor,
icon: const Icon(Icons.restart_alt),
),
],
)),
Flexible(
flex: 1,
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(50), bottom: Radius.zero),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Pomodoros',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color:
Theme.of(context).textTheme.displayLarge!.color,
),
),
Text(
'$totalPomodoros',
style: TextStyle(
fontSize: 58,
fontWeight: FontWeight.w600,
color:
Theme.of(context).textTheme.displayLarge!.color,
),
),
],
),
),
),
],
),
),
],
),
);
}
}
느낀 점
- 플러터 상수 네이밍 컨벤션도 변수와 같나,,, 대문자로 써줘야 하지 않나
- 안드에서는 타이머,,,진짜 지독하게 사용하기 귀찮은데 플러터는 정말 간단하군아,,,,
반응형
'코딩 일기장 > Flutter' 카테고리의 다른 글
[Flutter] Camera에서 실시간으로 이미지 받아오기 (0) | 2023.05.28 |
---|---|
[Flutter] MediaPipe로 스켈레톤 추출하기 (1) | 2023.05.15 |
[Flutter] Flutter 로 웹툰 앱 만들기: Data fetch/fromJson/async await/ListView/Hero/Futures/Uri Luncher/Pub.dev (1) | 2023.01.29 |
[Flutter] Flutter 로 웹툰 앱 만들기: State/buildContext/Widget Life Cycle (0) | 2023.01.27 |
[Flutter] Flutter 로 웹툰 앱 만들기: 헤더/버튼/카드/컴포넌트 (0) | 2023.01.25 |
Comments