Flutter/Flutter로 웹툰 앱 만들기

[노마드코더: Flutter 로 웹툰 앱 만들기] #6.9 Detail Screen

유호야 2022. 12. 14. 06:02
반응형

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,
              )),
        ],
      ),
    );
  }
}

 

 

오늘의 완성본

목록에서 웹툰 목록과 사진을 출력하고 클릭하면 
아래에서부터 다음 화면을 출력하는 위젯을 만들었다. 
그리고 그 위젯에서는 사진을 가운데 정렬하게 완성

반응형