fix(performance): always running marquee text causes high GPU usage #175 and UserArtist overflow on smaller displays

This commit is contained in:
Kingkor Roy Tirtho 2022-08-18 12:15:31 +06:00
parent e48b67cd47
commit a23ce61446
7 changed files with 232 additions and 188 deletions

View File

@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/Shared/HoverBuilder.dart';
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
class ArtistCard extends StatelessWidget { class ArtistCard extends StatelessWidget {
@ -15,12 +16,16 @@ class ArtistCard extends StatelessWidget {
false) false)
? artist.images!.first.url! ? artist.images!.first.url!
: "https://avatars.dicebear.com/api/open-peeps/${artist.id}.png?b=%231ed760&r=50&flip=1&translateX=3&translateY=-6"); : "https://avatars.dicebear.com/api/open-peeps/${artist.id}.png?b=%231ed760&r=50&flip=1&translateX=3&translateY=-6");
return InkWell( return SizedBox(
height: 240,
width: 200,
child: InkWell(
onTap: () { onTap: () {
GoRouter.of(context).push("/artist/${artist.id}"); GoRouter.of(context).push("/artist/${artist.id}");
}, },
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
child: Ink( child: HoverBuilder(builder: (context, isHovering) {
return Ink(
width: 200, width: 200,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).backgroundColor, color: Theme.of(context).backgroundColor,
@ -43,15 +48,14 @@ class ArtistCard extends StatelessWidget {
backgroundImage: backgroundImage, backgroundImage: backgroundImage,
), ),
SizedBox( SizedBox(
height: 30, height: 20,
child: artist.name!.length > 15 child: SpotubeMarqueeText(
? SpotubeMarqueeText(
text: artist.name!, text: artist.name!,
style: Theme.of(context).textTheme.headline5!, style: Theme.of(context).textTheme.bodyLarge!.copyWith(
) fontWeight: FontWeight.bold,
: Text( ),
artist.name!, minStartLength: 15,
style: Theme.of(context).textTheme.headline5, isHovering: isHovering,
), ),
), ),
Text( Text(
@ -61,6 +65,8 @@ class ArtistCard extends StatelessWidget {
], ],
), ),
), ),
);
}),
), ),
); );
} }

View File

@ -31,8 +31,8 @@ class UserArtists extends HookConsumerWidget {
return PagedGridView( return PagedGridView(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250, maxCrossAxisExtent: 200,
childAspectRatio: 9 / 11, mainAxisExtent: 250,
crossAxisSpacing: 20, crossAxisSpacing: 20,
mainAxisSpacing: 20, mainAxisSpacing: 20,
), ),

View File

@ -157,16 +157,11 @@ class SyncedLyrics extends HookConsumerWidget {
child: Stack( child: Stack(
children: [ children: [
Center( Center(
child: playback.track?.name != null && child: SpotubeMarqueeText(
playback.track!.name!.length > 29 text: playback.track?.name ?? "Not Playing",
? SpotubeMarqueeText(
text: playback.track?.name ??
"Not Playing",
style: headlineTextStyle,
)
: Text(
playback.track?.name ?? "Not Playing",
style: headlineTextStyle, style: headlineTextStyle,
minStartLength: 29,
isHovering: true,
), ),
), ),
Positioned.fill( Positioned.fill(

View File

@ -82,21 +82,15 @@ class PlayerView extends HookConsumerWidget {
children: [ children: [
SizedBox( SizedBox(
height: 30, height: 30,
child: currentTrack?.name != null && child: SpotubeMarqueeText(
currentTrack!.name!.length > 29 text: currentTrack?.name ?? "Not playing",
? SpotubeMarqueeText( style:
text: currentTrack.name ?? "Not playing", Theme.of(context).textTheme.headline5?.copyWith(
style: Theme.of(context)
.textTheme
.headline5
?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: paletteColor.titleTextColor, color: paletteColor.titleTextColor,
), ),
) isHovering: true,
: Text( minStartLength: 29,
currentTrack?.name ?? "Not Playing",
style: Theme.of(context).textTheme.headline5,
), ),
), ),
TypeConversionUtils.artists_X_ClickableArtists( TypeConversionUtils.artists_X_ClickableArtists(

View File

@ -0,0 +1,25 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class HoverBuilder extends HookWidget {
final Widget Function(BuildContext context, bool isHovering) builder;
const HoverBuilder({
required this.builder,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final hovering = useState(false);
return MouseRegion(
onEnter: (_) {
if (!hovering.value) hovering.value = true;
},
onExit: (_) {
if (hovering.value) hovering.value = false;
},
child: builder(context, hovering.value),
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:spotube/components/Shared/HoverBuilder.dart';
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
class PlaybuttonCard extends StatelessWidget { class PlaybuttonCard extends StatelessWidget {
@ -32,7 +33,8 @@ class PlaybuttonCard extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200), constraints: const BoxConstraints(maxWidth: 200),
child: Ink( child: HoverBuilder(builder: (context, isHovering) {
return Ink(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).backgroundColor, color: Theme.of(context).backgroundColor,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@ -92,24 +94,20 @@ class PlaybuttonCard extends StatelessWidget {
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
Padding( Padding(
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(horizontal: 16, vertical: 10), horizontal: 16, vertical: 10),
child: Column( child: Column(
children: [ children: [
Tooltip( Tooltip(
message: title, message: title,
child: SizedBox( child: SizedBox(
height: 20, height: 20,
child: title.length > 25 child: SpotubeMarqueeText(
? SpotubeMarqueeText(
text: title, text: title,
style: const TextStyle( style:
fontWeight: FontWeight.bold), const TextStyle(fontWeight: FontWeight.bold),
) minStartLength: 25,
: Text( isHovering: isHovering,
title,
style: const TextStyle(
fontWeight: FontWeight.bold),
), ),
), ),
), ),
@ -117,8 +115,7 @@ class PlaybuttonCard extends StatelessWidget {
const SizedBox(height: 10), const SizedBox(height: 10),
SizedBox( SizedBox(
height: 30, height: 30,
child: description!.length > 30 child: SpotubeMarqueeText(
? SpotubeMarqueeText(
text: description!, text: description!,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
@ -127,16 +124,8 @@ class PlaybuttonCard extends StatelessWidget {
.headline4 .headline4
?.color, ?.color,
), ),
) isHovering: isHovering,
: Text( minStartLength: 30,
description!,
style: TextStyle(
fontSize: 13,
color: Theme.of(context)
.textTheme
.headline4
?.color,
),
), ),
), ),
] ]
@ -145,7 +134,8 @@ class PlaybuttonCard extends StatelessWidget {
), ),
], ],
), ),
), );
}),
), ),
), ),
); );

View File

@ -1,29 +1,63 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:marquee/marquee.dart'; import 'package:marquee/marquee.dart';
import 'package:spotube/utils/platform.dart';
class SpotubeMarqueeText extends StatelessWidget { class SpotubeMarqueeText extends HookWidget {
const SpotubeMarqueeText({Key? key, required this.text, this.style}) final int? minStartLength;
: super(key: key); final bool? isHovering;
const SpotubeMarqueeText({
Key? key,
required this.text,
this.style,
this.minStartLength,
this.isHovering,
}) : super(key: key);
final TextStyle? style; final TextStyle? style;
final String text; final String text;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hovering = useState(false);
final isInitial = useState(true);
useEffect(() {
if (isHovering != null && isHovering != hovering.value) {
hovering.value = isHovering!;
}
return null;
}, [isHovering]);
if ((!isInitial.value && !hovering.value && kIsDesktop) ||
minStartLength != null && text.length <= minStartLength!) {
return Text(
text,
style: style,
overflow: TextOverflow.ellipsis,
);
}
return Marquee( return Marquee(
text: text, text: text,
style: style, style: style,
scrollAxis: Axis.horizontal, scrollAxis: Axis.horizontal,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
blankSpace: 60.0, blankSpace: 40.0,
velocity: 30.0, velocity: 30.0,
startAfter: const Duration(seconds: 2),
pauseAfterRound: const Duration(seconds: 2),
accelerationDuration: const Duration(seconds: 1), accelerationDuration: const Duration(seconds: 1),
accelerationCurve: Curves.linear, accelerationCurve: Curves.linear,
decelerationDuration: const Duration(milliseconds: 500), decelerationDuration: const Duration(milliseconds: 500),
decelerationCurve: Curves.easeOut, decelerationCurve: Curves.easeOut,
fadingEdgeStartFraction: 0.15, fadingEdgeStartFraction: 0.15,
fadingEdgeEndFraction: 0.15, fadingEdgeEndFraction: 0.15,
showFadingOnlyWhenScrolling: true,
onDone: () {
if (isInitial.value) {
isInitial.value = false;
hovering.value = false;
}
},
numberOfRounds: hovering.value ? null : 1,
); );
} }
} }