티스토리 뷰

코딩/Flutter

[Flutter] Pomodoro를 만들다.

김기지 2024. 8. 23. 12:30

완성모습!

나의 첫 앱 Pomodoro에 대한 정리 겸 회고록을 작성하겠다.

https://nomadcoders.co/flutter-for-beginners/lobby

 

Flutter 로 웹툰 앱 만들기 – 노마드 코더 Nomad Coders

Flutter for Beginners

nomadcoders.co

 

 


1. color theme 설정하기

theme에는 color에 대한 세팅을 했다.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        scaffoldBackgroundColor: const Color(0xffE7626C),
        textTheme: const TextTheme(
          headlineLarge: TextStyle(
            color: Color(0xFF232B55),
          ),
        ),
        cardColor: const Color(0xFFF4EDDB),
      ),
      home: const HomeScreen(),
    );
  }
}

flutter ThemeData가 버전이 바뀌면서 강의와 작성 방법이 조금 바뀌었다.

backgroundColor이 surface로 대체되었다.

 

home widget은 따로 분리해 코드를 작성하였다.

 

 

2. HomeScreen UI

home screen은 세 개의 구역으로 나뉘어진다.

여기서는 Flexible을 사용해 각 구역을 비율로 나누어주었다.

  • Flexible: 하나의 박스가 얼마나 공간을 차지할 지 비율을 정함

-> UI를 비율에 기반해 더 유연하게 만들 수 있다.

    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      body: Column(
        children: [
          Flexible(
            flex: 1,
            child: Container(),
          ),
          Flexible(
            flex: 3,
            child: Container(),
          ),
          Flexible(
             flex: 1,
            child: Container(),
          ),
        ],
    ),
);

첫 번째, 세번째 컨테이너는 1, 두 번째 컨테이너는 3비율로 설정했다.

 

 

① 첫 번째 컨테이너

첫 번째 컨테이너는 시간(초)를 표시한다.

시간은 1500초(25분)으로 프로퍼티를 생성한 후 표시해준다.

 

초는 Duration을 사용한 포맷팅을 통해 좀 더 보기 편하도록 바꿔 표시했다.

  static const twentyFiveMinutes = 1500;
  int totalSeconds = twentyFiveMinutes;
  
  String format(int seconds) {
    var duration = Duration(seconds: seconds);
    return duration.toString().split('.').first.substring(2, 7);
  }
  ...
  
  Flexible(
    flex: 1,
    child: Container(
      alignment: Alignment.bottomCenter,
      child: Text(
        format(totalSeconds),
        style: TextStyle(
            color: Theme.of(context).cardColor,
            fontSize: 89,
            fontWeight: FontWeight.w600),
      ),
    ),
  ),

alignment 속성을 사용해 텍스트가 컨테이너의 하단에 위치하도록 정렬했다.

 

 

② 두 번째 컨테이너

두 번째 컨테이너는 타이머를 동작하는 버튼이 위치한다.

강의에서는 시작/멈춤 버튼만 구현했지만 추가로 restart 버튼을 구현했다.

 

시작/멈춤 버튼은 상태에 따라 다르게 표시되어야 하기 때문에 상태 변경을 위한 프로퍼티를 생성한다.

bool isRunning = false;

...

Flexible(
    flex: 3,
    child: Container(
      alignment: Alignment.center,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          IconButton(
            iconSize: 120,
            color: Theme.of(context).cardColor,
            onPressed: isRunning ? onPausePressed : onStartPressed,
            icon: Icon(isRunning
                ? Icons.pause_circle_outline
                : Icons.play_circle_outline),
          ),
          IconButton(
              iconSize: 120,
              color: Theme.of(context).cardColor,
              onPressed: onRestartPressed,
              icon: const Icon(Icons.restore_rounded))
        ],
      ),
    ),
),

isRunning에 따라 시작/멈춤 아이콘이 달라지고 동작할 함수 또한 바뀐다.

 

동작할 함수를 작성하자!

함수는

  1. onStartPressed -> 타이머를 시작할 함수
  2. onPausedPressed -> 타이머를 멈출 함수
  3. onRestartPressed -> 타이머를 재시작할 함수

타이머는 초가 1씩 줄어들기 때문에 Timer를 사용해 정해진 간격에 특정 함수를 한 번씩 실행하도록 한다.

따라서 여기에 초를 1씩 줄이는 함수가 추가된다.

 late Timer timer;

  void onTick(Timer timer) {
    if (totalSeconds == 0) {
      setState(() {
        isRunning = false;
        totalSeconds = twentyFiveMinutes;
      });
      timer.cancel();
    } else {
      setState(() {
        totalSeconds--;
      });
    }
  }

totalSeconds ==0 이 되면 타이머가 종료된 것이기 때문에 타이머를 멈추고 초를 다시 할당한다.

그리고 timer를 멈춰야 하기 때문에 cancel메소드를 호출한다.

 

onStartPressed 함수는 onTick 함수를 1초에 한 번씩 호출하면 된다.

  void onStartPressed() {
    timer = Timer.periodic(
      const Duration(seconds: 1),
      onTick,
    );
    setState(() {
      isRunning = true;
    });
  }

isRunning을 true로 변경해야 아이콘이 바뀐다!

 

onPausePressed 함수는 실행 중이던 timer 함수를 멈추고 isRunning의 상태를 변경한다.

void onPausePressed() {
    timer.cancel();
    setState(() {
      isRunning = false;
    });
}

 

 

onRestartPressed 함수는 실행 중 또는 멈춰진 타이머를 초기화하는 함수로 실행 중이던 timer를 멈추고 totalSeconds를 초기화한다.

void onRestartPressed() {
    timer.cancel();
    setState(() {
      totalSeconds = twentyFiveMinutes;
    });
    isRunning = false;
}

 

 

 

③ 세 번째 컨테이너

세 번째 컨테이너는 타이머가 끝난 횟수를 표시한다.

프로퍼티를 생성한 후 텍스트로 넘겨준다.

int totalPomodoros = 0;

...

Flexible(
    flex: 1,
    child: Row(
      children: [
        Expanded(
          child: Container(
            decoration: BoxDecoration(
              color: Theme.of(context).cardColor,
              borderRadius: BorderRadius.circular(35),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  'Pomodoros',
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.w600,
                    color: Theme.of(context)
                        .textTheme
                        .headlineLarge!
                        .color,
                  ),
                ),
                Text(
                  '$totalPomodoros',
                  style: TextStyle(
                    fontSize: 58,
                    fontWeight: FontWeight.w600,
                    color: Theme.of(context)
                        .textTheme
                        .headlineLarge!
                        .color,
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    ),
),

컨테이너의 경우 안의 자식들의 크기에 맞춰져 크기가 표시되기 때문에 옆으로 펼쳐지게 하기 위해서 Expanded로 감싸주었다.

 

 

totalPomodoros는 타이머가 0이 되면 1씩 증가한다.

따라서 totalSeconds가 0이 되면 1씩 증가하도록 코드를 추가한다.

  void onTick(Timer timer) {
    if (totalSeconds == 0) {
      setState(() {
        totalPomodoros++; // 타이머가 종료되면 1씩 증가
        isRunning = false;
        totalSeconds = twentyFiveMinutes;
      });
      timer.cancel();
    } else {
      setState(() {
        totalSeconds--;
      });
    }
  }

 

728x90

'코딩 > Flutter' 카테고리의 다른 글

[Flutter] API요청  (0) 2024.08.30