플러터 2기 3주차 주간회고 (1) Pomodoro

샤워실의 바보·2023년 11월 28일
0
post-thumbnail

2주차 (1) Pomodoro

1. 기간

  • 2023-11-22 ~ 2023-11-29

2. 요약

  • 코드의 모듈화(modularization), 코드 분할(Splitting)
  • main.dart에 모든 코드를 넣지 않고, Widget class를 생성하여 반복을 줄이고 재사용성 높일 것

3. 목표

  • Flutter로 웹툰 앱 만들기 전체 복습
  • 코드의 모듈화, 분할을 위한 리팩토링
  • setState() 메서드 완벽 이해
  • 멤버 변수 및 로직에 대한 완벽한 설명

4. 결과

5. 개선사항

  • 폴더 구조

  • 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(),
    );
  }
}
  • home_screen.dart : 애니메이션 같은 큰 욕심 부리지 않고 챌린지에 제시된 요구사항을 준수하고자 하였다.
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,
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
  • time_button.dart : 5개의 멤버 변수(totalSeconds, label, isRunning, selectedTime, onUpdate)
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,
          ),
        ),
      ),
    );
  }
}

6. 총평

  • ChatGPT 4.0에게 효과적인 질문을 하여 코드 작성 시간 단축
  • 로직에 대하여 스스로 설명할 수 있도록 하자.
  • 나의 가능성을 낮추지 말자. 육아를 한다고 해서 코딩할 시간이 줄어드는 것은 아니다. 다른 사람들도 마찬가지로 업무 등 이유로 바쁘다.
  • 육퇴 후 불끄고 누워서 유튜브 보는 행동 금지. 바로 책상에 앉아 코어타임 활용하여 코딩할 것.
  • 노마드코더 회원 분의 익스텐션(Distraction Detox)을 활용하여 유튜브 차단(리디렉션)하여 집중 유지할 것
profile
공부하는 개발자

0개의 댓글