import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:sprintf/sprintf.dart';
enum TimerStatus {running,paused,stopped,resting}
class TimerScreen extends StatefulWidget{
@override
_TimerScreenState createState() => _TimerScreenState();
}
class _TimerScreenState extends State<TimerScreen>{
static const WORK_SECONDS = 3;
static const REST_SECONDS = 5;
late TimerStatus _timerStatus;
late int _timer;
late int _pomodoroCount;
@override
void initState(){
super.initState();
_timerStatus = TimerStatus.stopped;
print(_timerStatus.toString());
_timer = WORK_SECONDS;
_pomodoroCount = 0;
}
void run(){
setState(() {
_timerStatus = TimerStatus.running;
print("[=>] "+_timerStatus.toString());
runTimer();
});
}
void rest(){
setState(() {
_timer = REST_SECONDS;
_timerStatus = TimerStatus.resting;
print("[=>] "+_timerStatus.toString());
});
}
void stop(){
setState(() {
_timer = WORK_SECONDS;
_timerStatus = TimerStatus.stopped;
print("[=>] $_timerStatus");
});
}
void pause(){
setState(() {
_timerStatus = TimerStatus.paused;
print("[=>] $_timerStatus");
});
}
void resume(){
setState(() {
run();
});
}
String secondsToString(int seconds){
return sprintf("%02d:%02d",[seconds~/60, seconds%60]);
}
void runTimer() async{
Timer.periodic(Duration(seconds: 1), (Timer t){
switch(_timerStatus){
case TimerStatus.paused:
t.cancel();
break;
case TimerStatus.stopped:
t.cancel();
break;
case TimerStatus.running:
if(_timer<=0){
showToast("작업 완료!");
rest();
}
else{
setState(() {
_timer-=1;
});
}
break;
case TimerStatus.resting:
if(_timer<=0){
setState(() {
_pomodoroCount+=1;
});
showToast("오늘 $_pomodoroCount개의 뽀모도로를 달성했습니다.");
t.cancel();
stop();
}
else{
setState(() {
_timer-=1;
});
}
break;
default:
break;
}
});
}
void showToast(String message){
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 5,
backgroundColor: Colors.grey,
textColor: Colors.white,
fontSize: 16.0,
);
}
@override
Widget build(BuildContext context){
final List<Widget> _runningButtons =[
ElevatedButton(
child: Text(
_timerStatus==TimerStatus.paused? '계속하기' : '일시정지',
style: TextStyle(color:Colors.white, fontSize: 16)
),
style: ElevatedButton.styleFrom(primary: Colors.blue),
onPressed: _timerStatus == TimerStatus.paused ? resume : pause,
),
Padding(padding: EdgeInsets.all(20)),
ElevatedButton(
child: Text(
'포기하기',
style: TextStyle(fontSize: 16)
),
style: ElevatedButton.styleFrom(primary: Colors.grey),
onPressed: stop,
),
];
final List<Widget> _stoppedButtons = [
ElevatedButton(
child: Text(
'시작하기',
style: TextStyle(color:Colors.white, fontSize: 16)
),
style: ElevatedButton.styleFrom(
primary: _timerStatus==TimerStatus.resting? Colors.green : Colors.blue
),
onPressed: run,
),
];
return Scaffold(
appBar: AppBar(
title: Text('뽀모도로 타이머'),
backgroundColor: _timerStatus == TimerStatus.running? Colors.green : Colors.blue,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
height: MediaQuery.of(context).size.height*0.5,
width: MediaQuery.of(context).size.width*0.6,
child: Center(
child: Text(
secondsToString(_timer),
style: TextStyle(
color:Colors.white,
fontSize: 48,
fontWeight: FontWeight.bold,
),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _timerStatus==TimerStatus.resting? Colors.green : Colors.blue
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _timerStatus==TimerStatus.resting? const [] : _timerStatus==TimerStatus.stopped? _stoppedButtons : _runningButtons,
)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:timer/screens/timer_screen.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(
primarySwatch: Colors.blue,
),
home: TimerScreen(),
);
}
}
Timer.periodic(Duration duration, Timer t)을 사용하면 단위 시간마다 코드를 실행시킬 수 있다.
C스타일의 문자열 포맷팅 함수를 지원해주는 외부 패키지,
String secondsToString(int seconds){
return sprintf("%02d:%02d",[seconds~/60, seconds%60]);
}
화면에 잠시 나타났다가 사라지는 메시지 창
Toast를 구현하기 위해서는 Scaffold와 Context가 필요하다. 하지만 이를 쓸 수 없는 환경이라면 외부 패키지인 fluttertoast를 통해 위 환경을 만족하지 않아도 사용할 수 있다.
void showToast(String message){
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_LONG, // 토스트 시간
gravity: ToastGravity.BOTTOM, // 위치
timeInSecForIosWeb: 5, // ios,web 환경에서 토스트 시간
backgroundColor: Colors.grey,
textColor: Colors.white,
fontSize: 16.0,
);
}