본문 바로가기
프로그래밍/Flutter 앱 개발

Flutter 앱 개발 12 : 우주 사진/비디오 상세 뷰

by drogrammer 2021. 1. 13.
반응형

지난 포스트에서 우주 사진/비디오 리스트 뷰를 완성 했다. 이제 리스트 아이템을 클릭 했을 때 보여질 상세 뷰를 만들어 보자.

 

Flutter 앱 개발 11 : 우주 사진/비디오 리스트뷰

지난 포스트에서 BLoC 패턴을 활용한 서버 연동 부분을 구현해 보았다. 이제 리스트 뷰 UI 를 연동해 보자. Flutter 앱 개발 10 : 우주 사진/비디오 데이터 가져오기 (BLoC) 이번 포스트에서는 BLoC 패턴

drogrammer.tistory.com

 

1. 필요 패키지 의존성 추가 (youtube_player_flutter)

이번 포스트에서 사용할 패키지는 loading_animation, extended_image, youtube_player_flutter 이다. 앞의 두가지는 이미 지난 포스트에서 의존성을 추가해 두었으므로, 추가하지 않았던 youtube_player_flutter 를 pubspec.yaml 에 추가해 주자. 

...
dependencies:
  loading_animations: 2.1.0         # 지난 포스트에서 이미 추가함
  extended_image: ^1.5.0            # 지난 포스트에서 이미 추가함
  youtube_player_flutter: ^7.0.0+7  # Youtube 패키지
  ...

 

Youtube 패키지를 사용하려면, 안드로이드의 경우 minSdkVersion 을 17이상으로 변경해야 한다. 

android > app > build.gradle 파일을 아래와 같이 변경한다.

...
android {
	...

    defaultConfig {
		...
        minSdkVersion 17  // minSdkVersion 을 17로 변경
		...
    }
	...
}

2. 우주 사진/비디오 설명 위젯 추가 (ApodDescription)

우주 사진/비디오 상세 뷰에서 활용할 설명 위젯을 추가하자. lib/widget/apod_description.dart 파일을 추가한다.

사진/비디오의 제목, 설명, 날짜, 저작권 정보를 받아서 표시하는 위젯으로 아래와 같은 레이아웃을 가진다. 

사진/비디오 설명 위젯 레이아웃

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

class ApodDescription extends StatelessWidget {
  final String title;     // 사진/비디오 제목
  final String contents;  // 사진/비디오 설명
  final DateTime date;    // 날짜
  final String copyright; // 저작권 정보

  ApodDescription({this.title, this.contents, this.date, this.copyright});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Padding(
          padding: EdgeInsets.symmetric(vertical: 8),
          child: Text(
            DateFormat('yyyy MM dd').format(date),
            style: TextStyle(
              fontSize: 16,
              color: Colors.white,
              fontFamily: 'NexonLv2GothicMedium',
            ),
          ),
        ),
        Text(
          title,
          style: TextStyle(
            fontSize: 24,
            color: Colors.white,
          ),
        ),
        // 저작권 정보가 있으면 표시, 없으면 빈 Container 추가
        copyright != null
            ? Padding(
                padding: EdgeInsets.only(top: 6),
                child: Text(
                  "Image Credit & Copyright: $copyright",
                  style: TextStyle(
                    fontSize: 13,
                    color: Colors.white,
                  ),
                ),
              )
            : Container(),
        Padding(
          padding: EdgeInsets.only(top: 25),
          child: Text(
            contents,
            style: TextStyle(
              fontSize: 16,
              color: Colors.white,
              height: 1.5,
            ),
          ),
        ),
      ],
    );
  }
}

 

3. 우주 사진/비디오 상세 뷰 추가 (ApodDetail)

우주 사진/비디오 상세 뷰를 추가하자. lib/view/apod_detail.dart 파일을 추가하자.

코드 중 핵심적인 부분을 설명하면,

  • 컨텐츠와 부가 설명을 리스트뷰에 넣어서 보여준다.
  • 컨텐츠가 비디오일 경우 유튜브 플레이어를 활용하여 동영상을 자동 재생한다.
  • 이미지의 경우 네트워크로부터 사진을 받아서 보여주며, 로딩 과정에는 로딩 애니메이션을 보여준다.
  • 사진/비디오 리스트 뷰로부터 상세 뷰로 전환 시, 이미지의 경우에만 히어로 애니메이션을 사용한다.
  • 제목 문자열은 다국어를 지원한다. (다국어 지원 포스트 참고)

아래 레이아웃 및 코드를 참고하기 바란다.

비디오 상세 뷰 (좌), 사진 상세 뷰 (우)

import 'package:apod/data/apod.dart';
import 'package:apod/widget/apod_description.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:loading_animations/loading_animations.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'package:easy_localization/easy_localization.dart';

class ApodDetail extends StatelessWidget {
  final Apod apod;    // 선택된 사진/비디오의 상세 정보 (data/apod.dart)
  final String tagId; // Hero 애니메이션을 위한 ID

  ApodDetail({this.apod, this.tagId});

  @override
  Widget build(BuildContext context) {
    YoutubePlayerController controller;
    if (apod.isVideo) {
      // 컨텐츠가 비디오면 유튜브 플레이어 컨트롤러 초기화
      controller = YoutubePlayerController(
        // 비디오 URL 등록
        initialVideoId: YoutubePlayer.convertUrlToId(apod.hdUrl),
        // 자동 시작 활성화
        flags: YoutubePlayerFlags(
          autoPlay: true,
        ),
      );
    }

    // 전체 화면 뷰 이므로 Scaffold 위젯 추가
    return Scaffold(
      // AppBar 를 추가하고 다국어 처리 되어 있는 제목 텍스트 추가
      appBar: AppBar(
        title: Text('title'.tr()),
      ),
      // 컨텐츠가 길어질 수 있으므로 스크롤가능한 리스트뷰 추가
      body: ListView(
        children: [
          apod.isVideo == true
                // 비디오면 유튜브 플레이어를 추가함.
              ? YoutubePlayer(
                  controller: controller,
                  bottomActions: [
                    // 플레이어 아래에 재생 위치, 재생 컨트롤 바, 남은 시간 표시
                    CurrentPosition(),
                    ProgressBar(isExpanded: true),
                    RemainingDuration(),
                  ],
                )
                // Hero 애니메이션을 위한 위젯. (이미지만 Hero 애니메이션 사용)
              : Hero(
                  tag: tagId,
                  // 네트워크로 부터 이미지 다운로드 후 화면에 보여주는 위젯
                  child: ExtendedImage.network(
                    apod.url,
                    cache: true,
                    fit: BoxFit.cover,
                    loadStateChanged: (state) {
                      switch (state.extendedImageLoadState) {
                        // 이미지 로딩 중일 경우 로딩 애니메이션 표시
                        case LoadState.loading:
                          return LoadingFadingLine.circle(
                            backgroundColor: Colors.white,
                          );
                          break;
                        case LoadState.completed:
                          break;
                        case LoadState.failed:
                          break;
                      }
                    },
                  ),
                ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
            // 사진/비디오 상세 설명 텍스트 위젯
            child: ApodDescription(
              title: apod.title,
              contents: apod.description,
              date: apod.date,
              copyright: apod.copyright,
            ),
          ),
        ],
      ),
    );
  }
}

 

4. 우주 사진/비디오 리스트와 연결

지금까지 만든 우주 사진/비디오 상세 뷰 (ApodDetail) 를 우주 사진/비디오 리스트와 연결해 보자. 이전포스트에서 만든 리스트 타일 위젯의 onTap 콜백에서 ApodDetail을 띄워주면 끝이다.

lib/widget/apod_tile.dart 를 아래와 같이 수정하자.

...
import 'package:apod/view/apod_detail.dart';
...

class ApodTile extends StatelessWidget {
  ...

  @override
  Widget build(BuildContext context) {
	...
    return InkWell(
      onTap: () => {
        // ApodDetail 뷰로 전환한다.
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (ctx) => ApodDetail(apod: apod, tagId: id),
          ),
        )
      },
      ...
    );
  }
}

 

5. 결과

아래와 같이 리스트 아이템 (타일) 을 선택하면 사진/비디오 상세뷰로 전환되는 것을 확인 할 수 있다.

결과 화면

반응형

댓글