UI 작성
시작 / 일시정지 버튼 영역 UI 작성하기
- bottomNavigationBar : 어떤 위젯도 배치할 수 있음
타이머 영역 UI 작성
- Positioned
타이머 구현하기
- Duration 클래스 주기
타이머 클래스와 필요한 변수들
시작 / 일시정지 /초기화 기능
시간표시하기
- ~/ 몫을 구하는 연산자, 초 부분 구하기
- 1/100초 구하기 (00~99 표현하기)
랩타임 기록하기
랩타임 표시하기
전체코드⬇︎
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'StopWatch',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: StopWatchPage(),
);
}
}
class StopWatchPage extends StatefulWidget {
const StopWatchPage({Key key}) : super(key: key);
@override
_StopWatchPageState createState() => _StopWatchPageState();
}
class _StopWatchPageState extends State<StopWatchPage> {
Timer _timer; // 타이머 선언
var _time = 0; // 0.01초마다 1씩 증가시킬 정수형 변수
var _isRunning = false; // 현재 시각 상태를 나타낼 변수 (타이머 시작하거나 멈추기 위함)
List<String> _lapTimes = []; // 랩타임에 표시할 시간을 저장할 리스트
@override
void dispose(){
_timer?.cancel(); // 앱화면이 종료되면 타이머가 취소됨
// ?. 는 타이머를 한 번도 동작시키지 않았을 때도(null인 상태) 안전하게 동작을 취소하기 위해 사용
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('스탑워치!~'),
),
body: _buildBody(),
bottomNavigationBar: BottomAppBar(
child: Container(
height: 50.0,
),
),
// ↱ _clickButton() 메서드가 실행됨
floatingActionButton: FloatingActionButton(
onPressed: () => setState((){
_clickButton();
}),
// ↱_isRunning이 true인 경우면(스탑워치 진행중) pause아이콘 보여지고, false면 시작 아이콘 보여짐
child: _isRunning ? Icon(Icons.pause) : Icon(Icons.play_arrow),
),
// ↱floatingActionButton 위젯의 위치를 지정
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
// 내용 부분
Widget _buildBody(){
// 초 부분과, 1/100초 부분을 분리하여 계산하기
// ↱ _time 변수는 _start() 메서드로 인해 시간을 1/100초 단위로 저장함->
// 이 것을 100을 나눌경우 몫이 초가 됨, 그리고 나머지가 1/100초 단위가 됨
var sec = _time ~/ 100; // " ~/ " 는 몫을 구하는 연산자이다.
// 1/100 표현하기 ↱ 나머지 구하기 ↱ 문자열을 2자리로, 왼쪽의 빈곳을 0으로 채워 넣음
var hundredth = '${_time % 100}'.padLeft(2, '0');
return Center(
child: Padding(
padding: const EdgeInsets.only(top: 30),
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
// 시간 표시하는 영역
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text( // 초
'$sec',
style: TextStyle(fontSize: 50.0),
),
Text('$hundredth'), // 1/100초
],
),
// 랩타임을 표시하는 영역
Container(
width: 100,
height: 200,
child: ListView(
// _lapTimes리스트를 -> map을 이용해 각각 Text 형태로 변환 후 -> 다시 리스트 형태로 반환
children: _lapTimes.map((t) => Text(t)).toList(),
),
),
],
),
// 초기화 버튼 (왼쪽 아래에 위치시키기 위해 Positioned 위젯으로 감쌈)
Positioned(
left: 10,
bottom: 10,
child: FloatingActionButton(
backgroundColor: Colors.deepOrange,
onPressed: _reset, // 리셋 메서드 실행
child: Icon(Icons.rotate_left),
),
),
// 랩타임 버튼
Positioned(
right: 10,
bottom: 10,
child: ElevatedButton(
onPressed: (){
setState(() {
// ↱ x.xx 형태로 연결하여 전달
_recordLapTime('$sec.$hundredth');
});
},
child: Text('랩타임'),
),
),
],
),
),
);
}
// 시작 또는 일시정지 버튼 클릭
void _clickButton(){
_isRunning = !_isRunning; // 클릭할 때마다 boolean 값 변경
if(_isRunning){
_start();
}else{
_pause();
}
}
// Timer 객체를 초기화하고 0.01초에 한 번씩 반복하게 하기
void _start(){
// 첫 번째 인수 Duration 인수를 설정하면 -> 두 번째 인수로 받은 함수에서 실행됨
// ↱0.01초
_timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
setState(() {
_time++; // 0.01초마다 _time변수의 값을 1씩 증가 시킴
});
});
}
// 타이머 일시정지하기
void _pause(){
_timer?.cancel();
}
// 초기화
void _reset(){
setState(() {
_isRunning = false;
_timer?.cancel();
_lapTimes.clear();
_time = 0;
});
}
// 랩타임 기록하기
void _recordLapTime(String time){
// ↱_lapTimes 리스트의 맨 앞에 추가하기
_lapTimes.insert(0, '스탑워치 ${_lapTimes.length +1} 번째 실행 $time');
}
}