Skip to content

Commit 1816351

Browse files
committed
프로젝트 정보 및 이미지 파일 추가
1 parent dd585a5 commit 1816351

File tree

8 files changed

+445
-299
lines changed

8 files changed

+445
-299
lines changed

lib/ProjectListPage.dart

Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
import 'dart:convert';
2+
import 'dart:html' as html;
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter/services.dart';
5+
import 'package:markdown_widget/markdown_widget.dart';
6+
import 'package:sando_diary/customDecoration.dart';
7+
8+
class Projectpage extends StatefulWidget {
9+
const Projectpage({super.key});
10+
11+
@override
12+
State<Projectpage> createState() => _ProjectpageState();
13+
}
14+
15+
class _ProjectpageState extends State<Projectpage> {
16+
bool isShowDetail = false;
17+
List<bool> _isHovering = [];
18+
19+
late CardData currentProject;
20+
21+
final List<CardData> cardList = [
22+
CardData(
23+
title: 'Iconhabit App',
24+
description: 'This is the first card description.',
25+
fileName: 'iconhabit',
26+
content: '',
27+
tags: ['Language', 'Platform'],
28+
),
29+
CardData(
30+
title: 'Sando`s Diary',
31+
description: 'This is the second card description.',
32+
fileName: 'sando-diary',
33+
content: '',
34+
tags: ['Flutter', 'Dart', 'Platform'],
35+
),
36+
];
37+
38+
final Map<String, Color> tagColors = {
39+
'Flutter': Color(0xFF027DFD),
40+
'Android': Color(0xFF3DDC84),
41+
'iOS': Color(0xFF555555),
42+
'Web': Colors.white,
43+
};
44+
45+
final Map<String, IconData> tagIcons = {
46+
'Flutter': Icons.flutter_dash_outlined,
47+
'Android': Icons.android_outlined,
48+
'iOS': Icons.apple_outlined,
49+
'Web': Icons.web_asset_outlined,
50+
'else': Icons.crop_square_rounded
51+
};
52+
53+
@override
54+
void initState() {
55+
// 카드 개수만큼 hover 상태 리스트 초기화
56+
_isHovering = List.generate(cardList.length, (index) => false);
57+
58+
_loadPosts();
59+
60+
super.initState();
61+
}
62+
63+
Future<void> _loadPosts() async {
64+
// JSON 파일 읽기
65+
final jsonString = await rootBundle.loadString('./project_info/projects.json');
66+
final List<dynamic> jsonData = json.decode(jsonString);
67+
68+
// JSON 데이터를 맵으로 변환하여 파일명과 내용을 쉽게 찾도록 설정
69+
final Map<String, String> contentMap = {
70+
for (var item in jsonData)
71+
item['filename']: item['content'] // filename을 키로, content를 값으로 설정
72+
};
73+
74+
// 기존 리스트 순서에 맞춰 file_name에 맞는 content 추가
75+
for (var card in cardList) {
76+
if (contentMap.containsKey(card.fileName)) {
77+
card.content = contentMap[card.fileName] ?? ''; // 파일명이 일치하는 경우 content 추가
78+
}
79+
}
80+
}
81+
82+
Widget _projectDetail(CardData currentProject) {
83+
return Padding(
84+
padding: const EdgeInsets.all(16.0),
85+
child: MarkdownWidget(
86+
data: currentProject.content,
87+
config: MarkdownConfig(configs: [
88+
LinkConfig(
89+
style: TextStyle(
90+
color: Colors.cyan,
91+
decoration: TextDecoration.underline,
92+
),
93+
onTap: (url) {
94+
_launchURLInNewTab(url);
95+
},
96+
)
97+
]),
98+
),
99+
);
100+
}
101+
102+
void _launchURLInNewTab(String url) {
103+
html.window.open(url, '_blank');
104+
}
105+
106+
@override
107+
Widget build(BuildContext context) {
108+
return Scaffold(
109+
appBar: !isShowDetail
110+
? null
111+
: AppBar(
112+
elevation: 0,
113+
scrolledUnderElevation: 0,
114+
shadowColor: Colors.transparent,
115+
surfaceTintColor: Colors.transparent,
116+
forceMaterialTransparency: true,
117+
leading: BackButton(
118+
onPressed: () => setState(() {
119+
isShowDetail = false;
120+
}),
121+
),
122+
),
123+
body: !isShowDetail ? LayoutBuilder(
124+
builder: (BuildContext context, BoxConstraints constraints) {
125+
double width = constraints.maxWidth;
126+
127+
if (width > 700) {
128+
return Padding(
129+
padding: const EdgeInsets.all(16.0),
130+
child: ScrollConfiguration(
131+
behavior:
132+
ScrollConfiguration.of(context).copyWith(scrollbars: false),
133+
child: ListView.builder(
134+
itemCount: cardList.length,
135+
itemBuilder: (context, index) {
136+
final cardData = cardList[index];
137+
return Column(
138+
children: [
139+
Padding(
140+
padding: const EdgeInsets.all(8.0),
141+
child: Container(
142+
height: 220,
143+
decoration: BoxDecoration(
144+
color: Colors.white,
145+
border: Border.all(width: 1, color: Colors.black),
146+
boxShadow: !_isHovering[index]
147+
? AppShadows.customBaseBoxShadow
148+
: AppShadows.customHoverBoxShadow,
149+
),
150+
child: InkWell(
151+
splashColor: Colors.transparent,
152+
hoverColor: Colors.transparent,
153+
onHover: (hovering) {
154+
setState(() {
155+
_isHovering[index] = hovering;
156+
});
157+
},
158+
onTap: () {
159+
setState(() {
160+
currentProject = cardData;
161+
isShowDetail = true;
162+
});
163+
},
164+
child: Row(
165+
children: [
166+
Placeholder(),
167+
Padding(
168+
padding: const EdgeInsets.only(
169+
left: 12,
170+
top: 16,
171+
bottom: 16,
172+
right: 10,
173+
),
174+
child: Column(
175+
mainAxisAlignment: MainAxisAlignment.start,
176+
crossAxisAlignment:
177+
CrossAxisAlignment.start,
178+
children: [
179+
Text(
180+
cardData.title,
181+
style: TextStyle(fontSize: 25),
182+
),
183+
Expanded(
184+
child: Text(cardData.description),
185+
),
186+
Row(
187+
children: List.generate(
188+
cardData.tags.length, (tagIndex) {
189+
String tag = cardData.tags[tagIndex];
190+
Color tagColor =
191+
tagColors[tag] ?? Colors.grey;
192+
IconData? tagIcon = tagIcons[tag] ??
193+
tagIcons['else'];
194+
return Padding(
195+
padding: const EdgeInsets.only(
196+
right: 8.0),
197+
child: Container(
198+
decoration: BoxDecoration(
199+
color: Colors.white,
200+
borderRadius:
201+
BorderRadius.circular(15),
202+
border: Border.all(
203+
width: 1,
204+
color: Colors.black),
205+
),
206+
child: Padding(
207+
padding:
208+
const EdgeInsets.symmetric(
209+
vertical: 4,
210+
horizontal: 8,
211+
),
212+
child: Row(
213+
children: [
214+
Icon(tagIcon),
215+
Text(tag),
216+
],
217+
),
218+
),
219+
),
220+
);
221+
}),
222+
),
223+
],
224+
),
225+
),
226+
],
227+
),
228+
),
229+
),
230+
),
231+
const SizedBox(
232+
height: 12,
233+
),
234+
],
235+
);
236+
},
237+
),
238+
),
239+
);
240+
} else {
241+
return Padding(
242+
padding: const EdgeInsets.all(16.0),
243+
child: ScrollConfiguration(
244+
behavior:
245+
ScrollConfiguration.of(context).copyWith(scrollbars: false),
246+
child: ListView.builder(
247+
itemCount: cardList.length,
248+
itemBuilder: (context, index) {
249+
final cardData = cardList[index];
250+
return Column(
251+
children: [
252+
Padding(
253+
padding: const EdgeInsets.all(8.0),
254+
child: Container(
255+
width: width - 80,
256+
decoration: BoxDecoration(
257+
color: Colors.white,
258+
border: Border.all(width: 1, color: Colors.black),
259+
boxShadow: !_isHovering[index]
260+
? AppShadows.customBaseBoxShadow
261+
: AppShadows.customHoverBoxShadow,
262+
),
263+
child: InkWell(
264+
splashColor: Colors.transparent,
265+
hoverColor: Colors.transparent,
266+
onHover: (hovering) {
267+
setState(() {
268+
_isHovering[index] = hovering;
269+
});
270+
},
271+
onTap: () {
272+
debugPrint('Tapped on ${cardData.title}');
273+
},
274+
child: Column(
275+
children: [
276+
const Placeholder(
277+
fallbackHeight: 200,
278+
),
279+
Padding(
280+
padding: const EdgeInsets.only(
281+
left: 12,
282+
top: 16,
283+
bottom: 16,
284+
right: 10,
285+
),
286+
child: Column(
287+
mainAxisAlignment: MainAxisAlignment.start,
288+
crossAxisAlignment:
289+
CrossAxisAlignment.start,
290+
children: [
291+
Text(
292+
cardData.title,
293+
style: TextStyle(fontSize: 25),
294+
),
295+
SizedBox(
296+
height: 8,
297+
),
298+
Row(
299+
children: List.generate(
300+
cardData.tags.length, (tagIndex) {
301+
String tag = cardData.tags[tagIndex];
302+
Color tagColor =
303+
tagColors[tag] ?? Colors.grey;
304+
IconData? tagIcon = tagIcons[tag] ?? tagIcons['else'];
305+
306+
return Padding(
307+
padding: const EdgeInsets.only(
308+
right: 8.0),
309+
child: Container(
310+
decoration: BoxDecoration(
311+
color: Colors.white,
312+
borderRadius:
313+
BorderRadius.circular(15),
314+
border: Border.all(
315+
width: 1,
316+
color: Colors.black),
317+
),
318+
child: Padding(
319+
padding:
320+
const EdgeInsets.symmetric(
321+
vertical: 4,
322+
horizontal: 8,
323+
),
324+
child: Row(
325+
children: [
326+
Icon(tagIcon),
327+
Text(
328+
tag,
329+
// style: TextStyle(
330+
// color: tag == "iOS"
331+
// ? Colors.white
332+
// : Colors.black),
333+
),
334+
],
335+
),
336+
),
337+
),
338+
);
339+
}),
340+
),
341+
],
342+
),
343+
),
344+
],
345+
),
346+
),
347+
),
348+
),
349+
const SizedBox(
350+
height: 12,
351+
),
352+
],
353+
);
354+
},
355+
),
356+
),
357+
);
358+
}
359+
}) : _projectDetail(currentProject),
360+
);
361+
}
362+
}
363+
364+
class CardData {
365+
final String title;
366+
final String description;
367+
final String fileName;
368+
String content;
369+
final List<String> tags;
370+
371+
CardData({
372+
required this.title,
373+
required this.description,
374+
required this.fileName,
375+
required this.content,
376+
required this.tags,
377+
});
378+
379+
}

0 commit comments

Comments
 (0)