폴더 구조
main.dart, home_screen.dart, time_button.dart로 분할
TimeButton 클래스는 15분 ~ 40분까지의 다양한 시간 버튼을 위젯으로 추출
main.dart
import 'package:flutter/material.dart';
import 'package:pomodoro/screens/home_screen.dart';
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSwatch(
backgroundColor: const Color(0xFFE7626C),
),
textTheme: const TextTheme(
// headline1이 deprecated, displayLarge로 변경
displayLarge: TextStyle(
color: Color(0xFF232B55),
),
),
cardColor: const Color(0xFFF4EDDB),
),
home: const HomeScreen(),
);
}
}
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:pomodoro/widgets/time_button.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const twentyFiveMinutes = 1500;
int totalSeconds = twentyFiveMinutes;
bool isRunning = false;
int totalPomodoros = 0; //
late Timer timer;
int selectedTime = twentyFiveMinutes; // 선택 시간의 초기값은 15분
int currentCycle = 0; // 현재 완료한 사이클 수
int roundsCompleted = 0; // 완료한 라운드 수
static const int cyclesPerRound = 4; // 한 라운드 당 사이클 수
void onTick(Timer timer) {
if (totalSeconds == 0) {
setState(() {
totalSeconds = twentyFiveMinutes;
currentCycle++;
if (currentCycle % cyclesPerRound == 0) {
roundsCompleted++;
currentCycle = 0; // 새로운 라운드를 시작하기 위해 사이클 수 리셋
}
isRunning = false;
});
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 onResetPressed() {
if (!isRunning) {
timer.cancel();
setState(() {
totalSeconds = twentyFiveMinutes;
selectedTime = twentyFiveMinutes;
});
}
}
void updateTime(int seconds) {
if (!isRunning) {
setState(() {
totalSeconds = seconds;
selectedTime = seconds;
});
}
}
String format(int seconds) {
var duration = Duration(seconds: seconds);
return duration.toString().substring(2, 7);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'POMOTIMER',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
),
),
backgroundColor: Theme.of(context).colorScheme.background,
elevation: 0,
),
backgroundColor: Theme.of(context).colorScheme.background,
body: Column(
children: [
Flexible(
flex: 1,
child: Column(
children: [
Container(
alignment: Alignment.bottomCenter,
child: Text(
format(totalSeconds),
style: TextStyle(
color: Theme.of(context).cardColor,
fontSize: 89,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
Flexible(
flex: 3,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SingleChildScrollView(
// 가로의 스크롤
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// TimeButton 위젯을 만들고 for문으로 반복(등차수열)
for (int i = 0; i < 6; i++)
TimeButton(
totalSeconds: 900 + 300 * i,
label: '${15 + 5 * i}',
isRunning: isRunning,
selectedTime: selectedTime,
onUpdate: updateTime,
),
],
),
),
IconButton(
iconSize: 120,
color: Theme.of(context).cardColor,
onPressed: isRunning ? onPausePressed : onStartPressed,
icon: Icon(
isRunning
? Icons.pause_circle_filled_rounded
: Icons.play_circle_outline,
),
),
IconButton(
iconSize: 120,
color: Theme.of(context).cardColor,
onPressed: onResetPressed,
icon: const Icon(
Icons.refresh_rounded,
),
),
],
),
),
),
Flexible(
flex: 1,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
color: Theme.of(context).cardColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$currentCycle/$cyclesPerRound',
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const Text(
'ROUND',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$roundsCompleted',
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const Text(
'GOAL',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
],
),
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
class TimeButton extends StatefulWidget {
final int totalSeconds;
final String label;
final bool isRunning;
final int selectedTime;
final Function onUpdate;
const TimeButton({
Key? key,
required this.totalSeconds,
required this.label,
required this.isRunning,
required this.selectedTime,
required this.onUpdate,
}) : super(key: key);
State<TimeButton> createState() => _TimeButtonState();
}
class _TimeButtonState extends State<TimeButton> {
// totalSeconds와 selectedTime이 일치하는지 여부를
// 알려주는 isSelected 변수 설정
bool get isSelected => widget.totalSeconds == widget.selectedTime;
Widget build(BuildContext context) {
return TextButton(
onPressed: () => widget.onUpdate(widget.totalSeconds),
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color:
isSelected ? Colors.deepPurple[300] : Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(10),
),
child: Text(
widget.label,
style: TextStyle(
fontSize: 40,
color: isSelected ? Colors.white : Colors.black,
),
),
),
);
}
}