Pomodor timer 클론 코딩 하는 강의와 UI구현 연습 강의를 보며 배운 내용을 기록한 내용입니다.
--UI구현 연습--
Container에 padding을 준다.
body: Container(
height: 150,
padding: EdgeInsets.all(10),
)
padding 위젯으로 감싼다.
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
...
)
const는 compile time에 값을 알고있어야 하고,final은 그렇지 않아도 되는 상수라고 했었다. const를 쓰려면 '딱 정해진 값'이어야 한다는 말인데, 무슨 말인지는 다음 코드를 보면 이해할 수 있다.
이 코드에선 Text의 첫번째 인자와 style의 color값이 하드코딩된 상수이다. 따라서 const키워드를 Padding 위젯 앞에다가 붙인것이다.
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(45)),
child: const Padding(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Text(
"request",
style: TextStyle(color: Colors.white, fontSize: 20),
)));
}
이 코드에서는 text와 textColor라는 변수로 이루어져있다. 두번째 코드에서는 text와 textColor가 변할 수 있다는 것이다. 그래서 const 키워드를 padding에만 붙인것이다.
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(45)),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Text(
text,
style: TextStyle(color: textColor, fontSize: 20),
)));
}
유로 표시 아이콘의 크기를 늘이고 싶다면, 다음 코드에서 Transform 기능을 이용하면 된다.
transform을 이용하지 않고 아이콘 크기를 키운다면 아이콘을 담고있는 카드의 크기도 같이 늘어나기 때문에 Transform 을 이용해야 한다.

const Icon(
Icons.euro_rounded,
color: Colors.white,
size: 98,
),
다음 코드와 같이 Transform.scale을 사용하고, scale과 child를 정의해주면 사진과 같이 아이콘이 늘어난다..!
Transform.scale(
scale: 5,
child: const Icon(
Icons.euro_rounded,
color: Colors.white,
size: 98,
),
)

아이콘의 위치를 움직이고 싶다면 transform.translate를 이용하면 된다.
offset에는 (x축 offset, y축 offset)이 들어간다.
Transform.scale(
scale: 2.2,
child: Transform.translate(
offset: Offset(8, 15),
child: const Icon(
Icons.euro_rounded,
color: Colors.white,
size: 88,
),
),
)
clipbehavior는 Container가 내용물(?)이 overflow 되었을 때 어떻게 해야할 지를 정해주는 것이다. 아래와 같이 Clip.hardEdge를 사용하면 overflow된 것을 자를수가 있다.

Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Color(0xff1f2123),
borderRadius: BorderRadius.circular(25),
),
state를 갱신하고 build메서드를 다시 실행하여 ui에 값을 반영하고 싶다면 다음과 같이 setstate를 호출하면 된다.
class _AppState extends State<MyApp> {
int counter = 0;
void onClick() {
setState(() { //setstate호출
counter = counter + 1;
});
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: const Color(0xFFF4EDDB),
body: Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(
'Click Count',
style: TextStyle(fontSize: 30),
),
Text(
'$counter',
style: TextStyle(fontSize: 30),
),
IconButton(
iconSize: 40,
onPressed: onClick,
icon: Icon(Icons.add_box_rounded),
)
])),
));
}
}
context는 해당 위젯 이전에 있는 모든 상위 요소들에 대한 정보이다. 위젯트리에 대한 정보가 담겨있다. 부모 요소에 접근할 수 있다.
이걸 이용하면 부모요소에 미리 theme을 정의하고, 자식요소에서 일관성 있게 theme을 가져다 사용할 수 있다. 이때, null safety만 신경쓰면 된다. (color가 null이 아니라고 확신하는 ! 키워들를 붙인다.)
부모
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
textTheme: TextTheme(titleLarge: TextStyle(color: Colors.red,))
),
home: Scaffold(
backgroundColor: const Color(0xFFF4EDDB),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center, children: const [
MyLargeTitle(),
])),
));
}
자식
class MyLargeTitle extends StatelessWidget {
const MyLargeTitle({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Text(
'My Large Title',
style: TextStyle(fontSize: 30, color: Theme
.of(context)
.textTheme
.titleLarge!
.color,),
);
}
}
--pomodors--
late 키워드를 이용해 timer 변수를 나중에 초기화 한다고 선언한다.
함수 원형은 다음과 같다. 이때, 실행할 함수 이름 뒤에 괄호는 쓰지 않아야 한다. 지금 당장 실행할 것이 아니기 때문이다.
Timer.periodic(몇초마다 반복할 것인지, 실행할 함수 이름, )

또한 자세히 보면, Timer 함수가 실행하는 함수는 인자로 Timer를 받는다는 것을 알 수 있다.
late Timer timer;
void onTick(Timer timer){//인자로 Timer를 받음.
setState((){totalSeconds=totalSeconds-1;});
}
void onStartPressed(){
timer = Timer.periodic(Duration(seconds:1),onTick,);//괄호가 있으면 함수를 지금 실행하는 것을 의미
}
멈춤 재생 버튼 만들때 처음엔 onStartPressd함수 이름 바꿔서 그 안에 멈춤,재생기능 다 넣으려고 했다. ^^..
다음과 같이 하면 직관적이고 깔끔한 코드 가능하다는 점을 배웠다.
onPressed:isRunning?onPausePressed: onStartPressed,
저는 간단하게 재시작 함수 구현해서 만들었습니다. 아이콘은 무난한 것으로 선택하여 원래 있던 시작/멈춤 버튼과 함께 Column으로 묶어 배치해줬습니다.
onRestart함수
void onRestart(){
timer.cancel();
setState((){
isRunning = false;
totalSeconds = twentyFiveMinutes;
totalPomodors=0;
});
}
UI
Flexible(
flex: 2,
child: Center(
child: Column(
children: [
IconButton(
iconSize: 120,
color: Theme.of(context).cardColor,
icon: isRunning
? const Icon(Icons.pause_circle_outline)
: const Icon(Icons.play_circle_outline),
onPressed: isRunning ? onPausePressed : onStartPressed,
),
IconButton(
iconSize: 30,
color: Theme.of(context).cardColor,
icon: const Icon(Icons.restart_alt_outlined),
onPressed:onRestart)
],
))),
