Skip to content

Commit d734a95

Browse files
authored
feat: 홈화면에 파이차트 추가 및 캡슐 버튼 자연스럽게 수정 (#36)
1 parent 93ff567 commit d734a95

File tree

8 files changed

+208
-163
lines changed

8 files changed

+208
-163
lines changed
255 KB
Loading

frontend/ongi/assets/images/users/elderly_woman.svg

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 118 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,88 @@
11
import 'package:flutter/material.dart';
22
import 'package:ongi/core/app_colors.dart';
33
import 'package:flutter_svg/flutter_svg.dart';
4+
import 'package:ongi/widgets/custom_chart_painter.dart';
45

56
class HomeCapsuleSection extends StatelessWidget {
67
const HomeCapsuleSection({super.key});
78

89
@override
910
Widget build(BuildContext context) {
1011
return Expanded(
11-
child: Row(
12-
crossAxisAlignment: CrossAxisAlignment.center,
12+
child: Stack(
1313
children: [
14-
Expanded(
15-
flex: 2,
16-
child: Align(
17-
alignment: Alignment.centerRight,
18-
// 도넛 위치
14+
// 도넛 차트 영역
15+
Positioned(
16+
left: 0,
17+
bottom: MediaQuery.of(context).size.height * 0.05,
18+
width: MediaQuery.of(context).size.width * 0.95,
19+
height: MediaQuery.of(context).size.width * 0.95,
20+
child: Stack(
21+
clipBehavior: Clip.none,
22+
children: [
23+
Align(
24+
alignment: Alignment.centerLeft,
25+
child: Transform.translate(
26+
offset: Offset(-MediaQuery.of(context).size.width * 0.35, 0), // 왼쪽으로 이동
27+
child: OverflowBox(
28+
maxWidth: double.infinity,
29+
maxHeight: double.infinity,
30+
child: CustomPaint(
31+
painter: CustomChartPainter(
32+
percentages: [15, 10, 20, 20],
33+
),
34+
size: Size(
35+
MediaQuery.of(context).size.width * 0.95,
36+
MediaQuery.of(context).size.width * 0.95,
37+
),
38+
),
39+
),
40+
),
41+
),
42+
// 텍스트 (화면 안에 있음)
43+
Positioned(
44+
left: MediaQuery.of(context).size.width * 0.05, // 도넛 차트 중심에 맞춰 조정
45+
top: 0,
46+
bottom: 0,
47+
child: Center(
48+
child: Row(
49+
crossAxisAlignment: CrossAxisAlignment.end,
50+
children: [
51+
Text(
52+
'36.5',
53+
style: TextStyle(
54+
fontSize: 43,
55+
fontWeight: FontWeight.w800,
56+
color: AppColors.ongiOrange,
57+
height: 1,
58+
),
59+
),
60+
Text(
61+
'℃',
62+
style: TextStyle(
63+
fontSize: 24,
64+
fontWeight: FontWeight.w800,
65+
color: AppColors.ongiOrange,
66+
),
67+
),
68+
],
69+
),
70+
),
71+
),
72+
],
1973
),
2074
),
21-
Expanded(
22-
flex: 2,
23-
child: Padding(
24-
padding: EdgeInsets.only(
25-
top: MediaQuery.of(context).size.height * 0.17
26-
),
27-
28-
child: Transform.translate(
29-
30-
offset: Offset(MediaQuery.of(context).size.width * 0.35, 0),
31-
child: ButtonColumn(),
32-
),
33-
),
75+
Positioned(
76+
right: 0,
77+
bottom: MediaQuery.of(context).size.height * 0.05,
78+
child: ButtonColumn(),
3479
),
3580
],
3681
),
3782
);
3883
}
3984
}
4085

41-
4286
class CapsuleButton extends StatelessWidget {
4387
final String svgAsset;
4488
final bool selected;
@@ -57,56 +101,58 @@ class CapsuleButton extends StatelessWidget {
57101
Widget build(BuildContext context) {
58102
return GestureDetector(
59103
onTap: onTap,
60-
child: FractionallySizedBox(
61-
widthFactor: selected ? 2.9 : 1.0, // 선택 시 2.8배, 아니면 1.0배
62-
alignment: Alignment.centerRight,
63-
child: AnimatedContainer(
64-
duration: const Duration(milliseconds: 200),
65-
height: 68,
66-
margin: const EdgeInsets.symmetric(vertical: 2),
67-
decoration: BoxDecoration(
68-
color: selected ? AppColors.ongiOrange : Colors.white,
69-
border: Border.all(
70-
color: AppColors.ongiOrange,
71-
width: 2,
72-
),
73-
borderRadius: BorderRadius.circular(39),
74-
boxShadow: selected
75-
? [
76-
BoxShadow(
77-
color: AppColors.ongiOrange,
78-
offset: Offset(0, 4),
79-
),
80-
]
81-
: [],
104+
child: AnimatedContainer(
105+
duration: const Duration(milliseconds: 250),
106+
curve: Curves.easeInOut,
107+
height: MediaQuery.of(context).size.width * 0.18,
108+
width: selected ? MediaQuery.of(context).size.width * 0.9 : MediaQuery.of(context).size.width * 0.17,
109+
margin: const EdgeInsets.only(top: 2, bottom: 2, left: 0, right: 0),
110+
decoration: BoxDecoration(
111+
color: selected ? AppColors.ongiOrange : AppColors.ongiLigntgrey,
112+
border: Border(
113+
top: BorderSide(color: AppColors.ongiOrange, width: 2),
114+
bottom: BorderSide(color: AppColors.ongiOrange, width: 2),
115+
left: BorderSide(color: AppColors.ongiOrange, width: 2),
116+
right: BorderSide.none, // 오른쪽 테두리 제거
82117
),
83-
child: Row(
84-
children: [
85-
const SizedBox(width: 20),
86-
SvgPicture.asset(
87-
svgAsset,
88-
width: MediaQuery.of(context).size.width * 0.07,
89-
height: MediaQuery.of(context).size.width * 0.07,
90-
colorFilter: ColorFilter.mode(
91-
selected ? Colors.white : AppColors.ongiOrange,
92-
BlendMode.srcIn,
93-
),
118+
borderRadius: BorderRadius.only(
119+
topLeft: Radius.circular(39),
120+
bottomLeft: Radius.circular(39),
121+
topRight: Radius.circular(0),
122+
bottomRight: Radius.circular(0),
123+
),
124+
boxShadow: selected
125+
? [BoxShadow(color: AppColors.ongiOrange, offset: Offset(0, 4))]
126+
: [],
127+
),
128+
child: Row(
129+
children: [
130+
const SizedBox(width: 20),
131+
SvgPicture.asset(
132+
svgAsset,
133+
width: MediaQuery.of(context).size.width * 0.07,
134+
height: MediaQuery.of(context).size.width * 0.07,
135+
colorFilter: ColorFilter.mode(
136+
selected ? Colors.white : AppColors.ongiOrange,
137+
BlendMode.srcIn,
94138
),
139+
),
140+
if (selected && notificationText.isNotEmpty) ...[
95141
const SizedBox(width: 20),
96142
Expanded(
97143
child: Text(
98144
notificationText,
99145
overflow: TextOverflow.ellipsis,
100-
// textAlign: TextAlign.center,
146+
textAlign: TextAlign.center, // 텍스트 중앙 정렬
101147
style: TextStyle(
102-
color: selected ? Colors.white : AppColors.ongiOrange,
148+
color: Colors.white,
103149
fontWeight: FontWeight.w600,
104-
fontSize: 20,
150+
fontSize: 22,
105151
),
106152
),
107153
),
108154
],
109-
),
155+
],
110156
),
111157
),
112158
);
@@ -115,42 +161,48 @@ class CapsuleButton extends StatelessWidget {
115161

116162
class ButtonColumn extends StatefulWidget {
117163
const ButtonColumn({super.key});
164+
118165
@override
119166
State<ButtonColumn> createState() => _ButtonColumnState();
120167
}
121168

122169
class _ButtonColumnState extends State<ButtonColumn> {
123-
int selectedIdx = 0;
170+
int selectedIdx = -1; // 초기값을 -1로 변경 (아무것도 선택되지 않은 상태)
124171

125172
@override
126173
Widget build(BuildContext context) {
127174
return SizedBox(
128175
child: Column(
129176
mainAxisAlignment: MainAxisAlignment.end,
130177
mainAxisSize: MainAxisSize.min, // overflow 방지
178+
crossAxisAlignment: CrossAxisAlignment.end, // 오른쪽 정렬
131179
children: [
132180
CapsuleButton(
133181
svgAsset: 'assets/images/homebar_capsule.svg',
134182
selected: selectedIdx == 0,
135-
onTap: () => setState(() => selectedIdx = 0),
136-
notificationText: selectedIdx == 0 ? '23분 뒤, 이부프로펜 1알 섭취 예정' : '알림이 없어요',
183+
onTap: () => setState(() => selectedIdx = selectedIdx == 0 ? -1 : 0),
184+
notificationText: selectedIdx == 0
185+
? '23분 뒤, 이부프로펜 1알 섭취 예정'
186+
: '',
137187
),
138188
const SizedBox(height: 8),
139189
CapsuleButton(
140190
svgAsset: 'assets/images/homebar_med.svg',
141191
selected: selectedIdx == 1,
142-
onTap: () => setState(() => selectedIdx = 1),
143-
notificationText: selectedIdx == 1 ? '오늘의 통증 부위: 허리, 오른쪽 무릎' : '알림이 없어요',
192+
onTap: () => setState(() => selectedIdx = selectedIdx == 1 ? -1 : 1),
193+
notificationText: selectedIdx == 1
194+
? '오늘의 통증 부위: 허리, 오른쪽 무릎'
195+
: '',
144196
),
145197
const SizedBox(height: 8),
146198
CapsuleButton(
147199
svgAsset: 'assets/images/homebar_walk.svg',
148200
selected: selectedIdx == 2,
149-
onTap: () => setState(() => selectedIdx = 2),
150-
notificationText: selectedIdx == 2 ? '12,000 걸음' : '알림이 없어요',
201+
onTap: () => setState(() => selectedIdx = selectedIdx == 2 ? -1 : 2),
202+
notificationText: selectedIdx == 2 ? '12,000 걸음' : '',
151203
),
152204
],
153205
),
154206
);
155207
}
156-
}
208+
}

frontend/ongi/lib/screens/home/home_logo.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ class HomeBackgroundLogo extends StatelessWidget {
1010
return Stack(
1111
children: [
1212
Positioned(
13-
top: -screenHeight * 0.16,
14-
right: -screenWidth * 0.5,
13+
top: -screenHeight * 0.2,
14+
right: -screenWidth * 0.57,
1515
child: Opacity(
1616
opacity: 0.30,
1717
child: Image.asset(
1818
'assets/images/logo.png',
19-
width: screenWidth * 1.2,
20-
height: screenWidth * 1.2,
19+
width: screenWidth * 1.3,
20+
height: screenWidth * 1.3,
2121
color: Colors.white,
2222
fit: BoxFit.contain,
2323
),
Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,67 @@
1+
import 'dart:ui';
2+
13
import 'package:flutter/material.dart';
2-
import 'package:flutter_svg/flutter_svg.dart';
34

45
class HomeOngiText extends StatelessWidget {
56
final String username;
7+
68
const HomeOngiText({required this.username, super.key});
9+
710
@override
811
Widget build(BuildContext context) {
912
final screenWidth = MediaQuery.of(context).size.width;
1013
final double iconSize = screenWidth * 0.08;
1114

12-
return Column(
13-
crossAxisAlignment: CrossAxisAlignment.start,
14-
children: [
15-
Row(
16-
children: [
17-
SvgPicture.asset(
18-
'assets/images/users/elderly_woman.svg',
19-
width: iconSize,
20-
height: iconSize,
21-
),
22-
const SizedBox(width: 8),
23-
Text(
24-
'$username님',
25-
style: const TextStyle(
26-
fontFamily: 'Pretendard',
27-
fontWeight: FontWeight.w600,
28-
fontSize: 24,
29-
color: Colors.white,
30-
),
31-
),
32-
],
33-
),
34-
RichText(
35-
text: const TextSpan(
36-
style: TextStyle(
37-
fontFamily: 'Pretendard',
38-
fontSize: 60,
39-
color: Colors.white,
40-
),
15+
return Padding(
16+
padding: EdgeInsets.only(
17+
left: 32,
18+
right: 0, // 오른쪽 패딩 제거
19+
top: 16,
20+
bottom: 16,
21+
),
22+
child: Column(
23+
crossAxisAlignment: CrossAxisAlignment.start,
24+
children: [
25+
Row(
4126
children: [
42-
TextSpan(
43-
text: '우리가족의\n',
44-
style: TextStyle(fontWeight: FontWeight.w300),
27+
Image.asset(
28+
'assets/images/users/elderly_woman.png',
29+
width: iconSize,
30+
height: iconSize,
4531
),
46-
TextSpan(
47-
text: '온기는',
48-
style: TextStyle(fontWeight: FontWeight.w800),
32+
const SizedBox(width: 8),
33+
Text(
34+
'$username님',
35+
style: const TextStyle(
36+
fontFamily: 'Pretendard',
37+
fontWeight: FontWeight.w600,
38+
fontSize: 24,
39+
color: Colors.white,
40+
),
4941
),
5042
],
5143
),
52-
),
53-
],
44+
RichText(
45+
text: const TextSpan(
46+
style: TextStyle(
47+
fontFamily: 'Pretendard',
48+
fontSize: 60,
49+
color: Colors.white,
50+
),
51+
children: [
52+
TextSpan(
53+
text: '우리가족의\n',
54+
style: TextStyle(fontWeight: FontWeight.w200),
55+
),
56+
TextSpan(
57+
text: '온기는',
58+
style: TextStyle(fontWeight: FontWeight.w800),
59+
),
60+
],
61+
),
62+
),
63+
],
64+
),
5465
);
5566
}
56-
}
67+
}

0 commit comments

Comments
 (0)