구현 📷
1. Hero widget
Hero(
tag: widget.id,
child: Container(
width: 200,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
blurRadius: 15,
offset: const Offset(2, 2),
color: Colors.black.withOpacity(0.5)),
]), //clipBehavior -> borderRadius 적용 안 됨
child: Image.network(
widget.thumb,
headers: const {
'Referer': 'https://cosmic.naver.com',
},
),
),
),
Hero 위젯
- Hero 위젯은 애니메이션을 통해 화면 전환 시 이미지가 부드럽게 이동하는 효과를 제공한다.
- tag 속성에 고유 값을 widget.id로 설정하여 Hero 애니메이션의 대상이 되도록 했다.
2. ApiService
static Future<WebtoonDetailModel> getToonByID(String id) async {
final url = Uri.parse("$baseUrl/$id");
final response = await http.get(url);
if (response.statusCode == 200) {
final webtoon = jsonDecode(response.body);
return WebtoonDetailModel.fromJson(webtoon);
}
throw Error();
}
static Future<List<WebtoonEpisodeModel>> getLatestEpisodesById(
String id) async {
List<WebtoonEpisodeModel> episodesInstances = [];
final url = Uri.parse("$baseUrl/$id/episodes");
final response = await http.get(url);
if (response.statusCode == 200) {
final episodes = jsonDecode(response.body);
for (var episode in episodes) {
episodesInstances.add(WebtoonEpisodeModel.fromJson(episode));
}
return episodesInstances;
}
throw Error();
}
getToonByID 메서드
- 특정 ID를 가진 웹툰의 세부 정보를 비동기로 가져오는 함수이다.
- 입력받은 id를 통해 baseUrl에 URL을 구성했고, 이를 사용해 HTTP GET 요청을 보내도록 한다.
- 요청 성공) 응답 상태 코드가 200일 경우, response.body를 JSON 디코딩하여 WebtoonDetailModel로 변환한다.
- 요청 실패) 요청이 실패하면 예외 처리를 수행
getLatestEpisodesById 메서드
- 특정 ID를 가진 웹툰의 최신 에피소드 목록을 비동기로 가져오는 함수이다.
- 입력받은 id를 사용해 에피소드 목록 요청 URL을 생성했다.
- 응답이 성공적이면, JSON 데이터를 디코딩하여 WebtoonEpisodeModel 리스트로 변환했다.
- 에피소드 데이터를 순회하며 WebtoonEpisodeModel.fromJson 메서드를 통해 객체로 변환 후 리스트에 추가했다.
- 요청이 실패하면 Error를 던져 예외를 처리했다.
3. Future
class DetailScreen extends StatefulWidget {
final String title, thumb, id;
const DetailScreen({
super.key,
required this.title,
required this.thumb,
required this.id,
});
@override
State<DetailScreen> createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
late Future<WebtoonDetailModel> webtoon;
late Future<List<WebtoonEpisodeModel>> episodes;
@override
void initState() {
// TODO: implement initState
super.initState();
webtoon = ApiService.getToonByID(widget.id);
episodes = ApiService.getLatestEpisodesById(widget.id);
}
DetailScreen 클래스
- StatefulWidget을 상속받아 상태를 가진 화면 위젯을 정의했다. 이 클래스는 title, thumb, id라는 세 개의 필드를 필수로 받도록 한다.
_DetailScreenState 클래스
- DetailScreen의 상태를 정의했다. 두 개의 Future 타입 필드인 webtoon과 episodes를 선언하고, 각각 웹툰 세부 정보와 최근 에피소드 데이터를 비동기로 처리하도록 설정했다.
initState 메서드
- 상태 초기화 메서드를 오버라이드했다. ApiService.getToonByID(widget.id)와 ApiService.getLatestEpisodesById(widget.id)를 호출하여 비동기 데이터를 초기화한다. super.initState()를 호출하여 부모 클래스의 초기화 로직을 유지하도록 한다.
4. UI
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 50,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
snapshot.data!.about,
style: const TextStyle(
fontSize: 16,
),
),
const SizedBox(
height: 15,
),
Text(
'${snapshot.data!.genre} / ${snapshot.data!.age}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
);
padding
- Text위젯을 padding으로 감싸 수평 방향으로 50px만큼의 공간을 할당한다.
Column: 자식 위젯들을 세로로 나
열
- (웹툰 줄거리 - (장르 / 이용연령)) — 나열 순서
- crossAxisAlignment를 CrossAxisAlignment.start로 설정하여 가운데 정렬이었던 text를 왼쪽 정렬로 배치했다.
참고
'Group Study (2024-2025) > Flutter' 카테고리의 다른 글
[Flutter] 8주차 스터디_FutureBuilder와 ListView 이해하기 (0) | 2024.11.27 |
---|---|
[Flutter] 7주차 스터디_Data Fetch 이해하기 (0) | 2024.11.25 |
[Flutter] 5주차 스터디_Widget State 이해하기 (0) | 2024.11.13 |
[Flutter] 6주차 스터디_뽀모도로 앱 만들어보기 (1) | 2024.11.11 |
[Flutter] 4주차 스터디_UI 구성하기 (0) | 2024.10.29 |