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:go_router/go_router.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/Shared/HoverBuilder.dart';
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
class ArtistCard extends StatelessWidget {
@ -15,12 +16,16 @@ class ArtistCard extends StatelessWidget {
false)
? artist.images!.first.url!
: "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: () {
GoRouter.of(context).push("/artist/${artist.id}");
},
borderRadius: BorderRadius.circular(10),
child: Ink(
child: HoverBuilder(builder: (context, isHovering) {
return Ink(
width: 200,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
@ -43,15 +48,14 @@ class ArtistCard extends StatelessWidget {
backgroundImage: backgroundImage,
),
SizedBox(
height: 30,
child: artist.name!.length > 15
? SpotubeMarqueeText(
height: 20,
child: SpotubeMarqueeText(
text: artist.name!,
style: Theme.of(context).textTheme.headline5!,
)
: Text(
artist.name!,
style: Theme.of(context).textTheme.headline5,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontWeight: FontWeight.bold,
),
minStartLength: 15,
isHovering: isHovering,
),
),
Text(
@ -61,6 +65,8 @@ class ArtistCard extends StatelessWidget {
],
),
),
);
}),
),
);
}

View File

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

View File

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

View File

@ -82,21 +82,15 @@ class PlayerView extends HookConsumerWidget {
children: [
SizedBox(
height: 30,
child: currentTrack?.name != null &&
currentTrack!.name!.length > 29
? SpotubeMarqueeText(
text: currentTrack.name ?? "Not playing",
style: Theme.of(context)
.textTheme
.headline5
?.copyWith(
child: SpotubeMarqueeText(
text: currentTrack?.name ?? "Not playing",
style:
Theme.of(context).textTheme.headline5?.copyWith(
fontWeight: FontWeight.bold,
color: paletteColor.titleTextColor,
),
)
: Text(
currentTrack?.name ?? "Not Playing",
style: Theme.of(context).textTheme.headline5,
isHovering: true,
minStartLength: 29,
),
),
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:flutter/material.dart';
import 'package:spotube/components/Shared/HoverBuilder.dart';
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
class PlaybuttonCard extends StatelessWidget {
@ -32,7 +33,8 @@ class PlaybuttonCard extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
child: Ink(
child: HoverBuilder(builder: (context, isHovering) {
return Ink(
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
borderRadius: BorderRadius.circular(8),
@ -92,24 +94,20 @@ class PlaybuttonCard extends StatelessWidget {
),
const SizedBox(height: 5),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 10),
child: Column(
children: [
Tooltip(
message: title,
child: SizedBox(
height: 20,
child: title.length > 25
? SpotubeMarqueeText(
child: SpotubeMarqueeText(
text: title,
style: const TextStyle(
fontWeight: FontWeight.bold),
)
: Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold),
style:
const TextStyle(fontWeight: FontWeight.bold),
minStartLength: 25,
isHovering: isHovering,
),
),
),
@ -117,8 +115,7 @@ class PlaybuttonCard extends StatelessWidget {
const SizedBox(height: 10),
SizedBox(
height: 30,
child: description!.length > 30
? SpotubeMarqueeText(
child: SpotubeMarqueeText(
text: description!,
style: TextStyle(
fontSize: 13,
@ -127,16 +124,8 @@ class PlaybuttonCard extends StatelessWidget {
.headline4
?.color,
),
)
: Text(
description!,
style: TextStyle(
fontSize: 13,
color: Theme.of(context)
.textTheme
.headline4
?.color,
),
isHovering: isHovering,
minStartLength: 30,
),
),
]
@ -145,7 +134,8 @@ class PlaybuttonCard extends StatelessWidget {
),
],
),
),
);
}),
),
),
);

View File

@ -1,29 +1,63 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:marquee/marquee.dart';
import 'package:spotube/utils/platform.dart';
class SpotubeMarqueeText extends StatelessWidget {
const SpotubeMarqueeText({Key? key, required this.text, this.style})
: super(key: key);
class SpotubeMarqueeText extends HookWidget {
final int? minStartLength;
final bool? isHovering;
const SpotubeMarqueeText({
Key? key,
required this.text,
this.style,
this.minStartLength,
this.isHovering,
}) : super(key: key);
final TextStyle? style;
final String text;
@override
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(
text: text,
style: style,
scrollAxis: Axis.horizontal,
crossAxisAlignment: CrossAxisAlignment.start,
blankSpace: 60.0,
blankSpace: 40.0,
velocity: 30.0,
startAfter: const Duration(seconds: 2),
pauseAfterRound: const Duration(seconds: 2),
accelerationDuration: const Duration(seconds: 1),
accelerationCurve: Curves.linear,
decelerationDuration: const Duration(milliseconds: 500),
decelerationCurve: Curves.easeOut,
fadingEdgeStartFraction: 0.15,
fadingEdgeEndFraction: 0.15,
showFadingOnlyWhenScrolling: true,
onDone: () {
if (isInitial.value) {
isInitial.value = false;
hovering.value = false;
}
},
numberOfRounds: hovering.value ? null : 1,
);
}
}