webtoon_widget.dart
import 'package:flutter/material.dart';
class Webtoon extends StatelessWidget {
final String title, thumb, id;
const Webtoon({
super.key,
required this.title,
required this.thumb,
required this.id,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
width: 250,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(boxShadow: [
BoxShadow(
blurRadius: 16,
offset: const Offset(5, 10),
color: Colors.black.withOpacity(0.5),
),
], borderRadius: BorderRadius.circular(15)),
child: Image.network(thumb),
),
const SizedBox(
height: 10,
),
Text(title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
)),
],
);
}
}
누군가 탭했을 때 제스처를 감지해야 한다.
그러기 위해서 사용하는 것이 GestureDetector
Column을 저걸로 감싸자
GestureDetector는 대부분의 동작을 감지하게 해 준다.
엄청나게 많은 옵션이 있는데, 우리는 onTap 옵션을 사용한다.
onTap은 onTapUp와 onTapDown을 합친 것이다.
(onTapUp은 손가락이 떼어졌다는 것을 의미)
이제 웹툰을 클릭하면 Detail_Screen 화면으로 보내야 한다.
Navigator는 router를 push할 수 있다.
Navigator는 context와 route가 필요한데,
MaterialPageRoute는 또 다른 클래스이다.
StatelessWidget을 route로 감싸서 다른 스크린처럼 보이게 한다.
builder가 필요한데, route를 만드는 함수이다.
보면 Navigator로 새 route를 push하고 있다.
route는 MaterialPageRoute로 만들었고 그 route는 StatelessWidget일 뿐인 DetailScreen을 렌더한다.
네이게이션바는 HomeScreen에 속해 있어서 scaffold를 렌더해야 한다.
Navigator.push를 사용하면 애니메이션 효과를 이용해서 유저가 다른 페이지롱 왔다고 느끼게 할 수 있다.
(사실은 그저 StatelessWidget을 렌더했을 뿐인데)
그리고 이제 body 안에 Column을 만든다.
Navigator.push와 MaterialPageRoute를 사용할 때 옵션을 전달할 수 있다.
예를 들어 fullScreenDialogue가 있는데, 이를 이용하면 화면이 아래에서부터 올라온다고 하는데...
사실 나는 설정하기전에도 아래에서부터 올라와서 설정이 처음부터 왜 이렇게 되어 있는지 모르겠다
그래서 false를 하나 true를 하나 올라오는 방식은 같고 창 닫기 버튼만 달라진다.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) =>
DetailScreen(title: title, thumb: thumb, id: id),
),
);
},
detail_screen.dart
import 'package:flutter/material.dart';
class DetailScreen extends StatelessWidget {
final String title, thumb, id;
const DetailScreen({
super.key,
required this.title,
required this.thumb,
required this.id,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
elevation: 3,
backgroundColor: Colors.white,
foregroundColor: const Color.fromARGB(255, 13, 10, 0),
title: Text(title,
style: const TextStyle(
color: Colors.black,
)),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
const SizedBox(
height: 50,
),
Container(
width: 250,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(boxShadow: [
BoxShadow(
blurRadius: 16,
offset: const Offset(5, 10),
color: Colors.black.withOpacity(0.5),
),
], borderRadius: BorderRadius.circular(15)),
child: Image.network(thumb),
),
],
),
],
),
);
}
}
home_screen.dart
import 'package:flutter/material.dart';
import 'package:toonflix/models/webtoon.dart';
import 'package:toonflix/services/api_service.dart';
import 'package:toonflix/widgets/webtoon_widget.dart';
class HomeScreen extends StatelessWidget {
HomeScreen({super.key});
final Future<List<WebtoonModel>> webtoons = ApiService.getTodaysToon();
@override
Widget build(BuildContext context) {
// print(webtoons);
return Scaffold(
appBar: AppBar(
centerTitle: true,
elevation: 3,
backgroundColor: Colors.white,
title: const Text("Today's toon",
style: TextStyle(
color: Colors.black,
)),
),
body: FutureBuilder(
future: webtoons,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
const SizedBox(
height: 50,
),
Expanded(
child: makeList(snapshot),
),
],
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
);
}
ListView makeList(AsyncSnapshot<List<WebtoonModel>> snapshot) {
return ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.length,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
itemBuilder: (context, index) {
// print(index);
var webtoon = snapshot.data![index];
return Webtoon(
title: webtoon.title,
thumb: webtoon.thumb,
id: webtoon.id,
);
},
separatorBuilder: (context, index) => const SizedBox(
width: 40,
),
);
}
}
webtoon_widget.dart
import 'package:flutter/material.dart';
import 'package:toonflix/screens/detail_screen.dart';
class Webtoon extends StatelessWidget {
final String title, thumb, id;
const Webtoon({
super.key,
required this.title,
required this.thumb,
required this.id,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailScreen(title: title, thumb: thumb, id: id),
fullscreenDialog: false,
),
);
},
child: Column(
children: [
Container(
width: 250,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(boxShadow: [
BoxShadow(
blurRadius: 16,
offset: const Offset(5, 10),
color: Colors.black.withOpacity(0.5),
),
], borderRadius: BorderRadius.circular(15)),
child: Image.network(thumb),
),
const SizedBox(
height: 10,
),
Text(title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
)),
],
),
);
}
}
오늘의 완성본
목록에서 웹툰 목록과 사진을 출력하고 클릭하면
아래에서부터 다음 화면을 출력하는 위젯을 만들었다.
그리고 그 위젯에서는 사진을 가운데 정렬하게 완성
'Flutter > Flutter로 웹툰 앱 만들기' 카테고리의 다른 글
[노마드코더: Flutter 로 웹툰 앱 만들기] #6.11 Recap (0) | 2022.12.14 |
---|---|
[노마드코더: Flutter 로 웹툰 앱 만들기] #6.10 Hero (0) | 2022.12.14 |
[노마드코더: Flutter 로 웹툰 앱 만들기] #6.8 Webtoon Card (0) | 2022.12.14 |
[노마드코더: Flutter 로 웹툰 앱 만들기] #6.7 ListView (0) | 2022.12.14 |
[노마드코더: Flutter 로 웹툰 앱 만들기] #6.6 FutureBuilder (0) | 2022.12.13 |