From e12b9a0c726fcf2fdcdf84443b1d00e05ea55953 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Wed, 8 Feb 2023 20:24:31 +0100 Subject: [PATCH] feat(ui): Implement a Hero transition --- lib/src/ui/pages/anime_list.dart | 58 +++++++++++----------- lib/src/ui/pages/details.dart | 6 +-- lib/src/ui/widgets/image.dart | 81 +++++++++++++++++-------------- lib/src/ui/widgets/list_item.dart | 2 + 4 files changed, 80 insertions(+), 67 deletions(-) diff --git a/lib/src/ui/pages/anime_list.dart b/lib/src/ui/pages/anime_list.dart index 445891d..59d8b52 100644 --- a/lib/src/ui/pages/anime_list.dart +++ b/lib/src/ui/pages/anime_list.dart @@ -159,23 +159,24 @@ class AnimeListPage extends StatelessWidget { ); }, child: AnimeCoverImage( - url: anime.thumbnailUrl, - onTap: () { - context.read().add( - AnimeDetailsRequestedEvent(anime), - ); - }, - extra: Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.only(right: 8), - child: Text( - '${anime.episodesWatched}/${anime.episodesTotal ?? "???"}', - style: Theme.of(context).textTheme.titleMedium, + url: anime.thumbnailUrl, + hero: anime.id, + onTap: () { + context.read().add( + AnimeDetailsRequestedEvent(anime), + ); + }, + extra: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.only(right: 8), + child: Text( + '${anime.episodesWatched}/${anime.episodesTotal ?? "???"}', + style: Theme.of(context).textTheme.titleMedium, + ), ), ), ), - ), ); }, ), @@ -208,23 +209,24 @@ class AnimeListPage extends StatelessWidget { ); }, child: AnimeCoverImage( - url: manga.thumbnailUrl, - onTap: () { - context.read().add( - MangaDetailsRequestedEvent(manga), - ); - }, - extra: Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.only(right: 8), - child: Text( - '${manga.chaptersRead}/${manga.chaptersTotal ?? "???"}', - style: Theme.of(context).textTheme.titleMedium, + hero: manga.id, + url: manga.thumbnailUrl, + onTap: () { + context.read().add( + MangaDetailsRequestedEvent(manga), + ); + }, + extra: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.only(right: 8), + child: Text( + '${manga.chaptersRead}/${manga.chaptersTotal ?? "???"}', + style: Theme.of(context).textTheme.titleMedium, + ), ), ), ), - ), ); }, ), diff --git a/lib/src/ui/pages/details.dart b/lib/src/ui/pages/details.dart index e49dea1..62ea333 100644 --- a/lib/src/ui/pages/details.dart +++ b/lib/src/ui/pages/details.dart @@ -38,9 +38,9 @@ class DetailsPage extends StatelessWidget { children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AnimeCoverImage( - url: state.data!.thumbnailUrl, + children: [AnimeCoverImage( + url: state.data!.thumbnailUrl, + hero: state.data!.id, ), Expanded( diff --git a/lib/src/ui/widgets/image.dart b/lib/src/ui/widgets/image.dart index c9df12a..0768a17 100644 --- a/lib/src/ui/widgets/image.dart +++ b/lib/src/ui/widgets/image.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; class AnimeCoverImage extends StatelessWidget { const AnimeCoverImage({ required this.url, + required this.hero, this.cached = true, this.extra, this.onTap, @@ -16,6 +17,9 @@ class AnimeCoverImage extends StatelessWidget { /// Flag indicating if the image should be cached. final bool cached; + /// The hero tag of the image. + final String hero; + /// An extra widget with a translucent backdrop. final Widget? extra; @@ -24,46 +28,51 @@ class AnimeCoverImage extends StatelessWidget { @override Widget build(BuildContext context) { - return ClipRRect( - borderRadius: BorderRadius.circular(8), - child: SizedBox( - height: 100 * (16 / 9), - width: 120, - child: InkWell( - onTap: onTap ?? () {}, - child: Stack( - children: [ - Positioned( - left: 0, - right: 0, - top: 0, - bottom: 0, - child: DecoratedBox( - decoration: BoxDecoration( - image: DecorationImage( - image: cached ? - CachedNetworkImageProvider(url) as ImageProvider : - NetworkImage(url), - fit: BoxFit.cover, + return Hero( + tag: hero, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Material( + child: SizedBox( + height: 100 * (16 / 9), + width: 120, + child: InkWell( + onTap: onTap ?? () {}, + child: Stack( + children: [ + Positioned( + left: 0, + right: 0, + top: 0, + bottom: 0, + child: DecoratedBox( + decoration: BoxDecoration( + image: DecorationImage( + image: cached ? + CachedNetworkImageProvider(url) as ImageProvider : + NetworkImage(url), + fit: BoxFit.cover, + ), + ), ), ), - ), - ), - if (extra != null) - Positioned( - left: 0, - bottom: 0, - right: 0, - child: SizedBox( - height: 40, - child: ColoredBox( - color: Colors.black54, - child: extra, + if (extra != null) + Positioned( + left: 0, + bottom: 0, + right: 0, + child: SizedBox( + height: 40, + child: ColoredBox( + color: Colors.black54, + child: extra, + ), + ), ), - ), - ), - ], + ], + ), + ), ), ), ), diff --git a/lib/src/ui/widgets/list_item.dart b/lib/src/ui/widgets/list_item.dart index 12c1fe9..5f7b561 100644 --- a/lib/src/ui/widgets/list_item.dart +++ b/lib/src/ui/widgets/list_item.dart @@ -76,6 +76,8 @@ class ListItem extends StatelessWidget { children: [ AnimeCoverImage( cached: cached, + // TODO(Unknown): Have the ID here + hero: thumbnailUrl, extra: imageExtra, url: thumbnailUrl, ),