freezer/lib/ui/tiles.dart

583 lines
16 KiB
Dart
Raw Normal View History

2020-06-23 19:23:12 +00:00
import 'package:flutter/material.dart';
2021-09-01 12:38:32 +00:00
import 'package:flutter/services.dart';
import 'package:fluttericon/octicons_icons.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/download.dart';
2021-09-01 12:38:32 +00:00
import 'package:freezer/api/player.dart';
import 'package:freezer/translations.i18n.dart';
2020-06-23 19:23:12 +00:00
import '../api/definitions.dart';
import 'cached_image.dart';
import 'dart:async';
2020-06-23 19:23:12 +00:00
class TrackTile extends StatefulWidget {
2021-11-01 16:41:25 +00:00
final Track track;
2021-09-01 12:38:32 +00:00
final void Function()? onTap;
final void Function()? onHold;
final Widget? trailing;
2020-06-23 19:23:12 +00:00
2021-09-01 12:38:32 +00:00
TrackTile(this.track, {this.onTap, this.onHold, this.trailing, Key? key})
: super(key: key);
2020-06-23 19:23:12 +00:00
@override
_TrackTileState createState() => _TrackTileState();
}
class _TrackTileState extends State<TrackTile> {
2021-11-01 16:41:25 +00:00
late StreamSubscription _subscription;
bool _isOffline = false;
2021-09-01 12:38:32 +00:00
bool _isHighlighted = false;
2020-06-23 19:23:12 +00:00
@override
void initState() {
//Listen to media item changes, update text color if currently playing
2021-09-01 12:38:32 +00:00
_subscription = audioHandler.mediaItem.listen((mediaItem) {
if (mediaItem == null) return;
2021-11-01 16:41:25 +00:00
if (mediaItem.id == widget.track.id && !_isHighlighted)
2021-09-01 12:38:32 +00:00
setState(() => _isHighlighted = true);
else if (_isHighlighted) setState(() => _isHighlighted = false);
2020-06-23 19:23:12 +00:00
});
//Check if offline
2021-09-01 12:38:32 +00:00
downloadManager.checkOffline(track: widget.track).then((isOffline) {
if (isOffline) setState(() => _isOffline = isOffline);
});
2020-06-23 19:23:12 +00:00
super.initState();
}
@override
void dispose() {
2021-11-01 16:41:25 +00:00
_subscription.cancel();
2020-06-23 19:23:12 +00:00
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
2021-11-01 16:41:25 +00:00
widget.track.title!,
2020-06-23 19:23:12 +00:00
maxLines: 1,
overflow: TextOverflow.clip,
2020-06-23 19:23:12 +00:00
style: TextStyle(
2021-09-01 12:38:32 +00:00
color: _isHighlighted ? Theme.of(context).primaryColor : null),
2020-06-23 19:23:12 +00:00
),
subtitle: Text(
2021-11-01 16:41:25 +00:00
widget.track.artistString,
2020-06-23 19:23:12 +00:00
maxLines: 1,
),
leading: CachedImage(
2021-11-01 16:41:25 +00:00
url: widget.track.albumArt!.thumb!,
width: 48,
2020-06-23 19:23:12 +00:00
),
onTap: widget.onTap,
onLongPress: widget.onHold,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
2021-09-01 12:38:32 +00:00
if (_isOffline)
Padding(
padding: EdgeInsets.symmetric(horizontal: 2.0),
child: Icon(
Octicons.primitive_dot,
color: Colors.green,
size: 12.0,
),
),
2021-11-01 16:41:25 +00:00
if (widget.track.explicit ?? false)
Padding(
padding: EdgeInsets.symmetric(horizontal: 2.0),
child: Text(
'E',
2021-09-01 12:38:32 +00:00
style: TextStyle(color: Colors.red),
),
),
Container(
width: 42.0,
child: Text(
2021-11-01 16:41:25 +00:00
widget.track.durationString,
textAlign: TextAlign.center,
),
),
2021-09-01 12:38:32 +00:00
widget.trailing ?? const SizedBox(width: 0, height: 0)
],
),
2020-06-23 19:23:12 +00:00
);
}
}
class AlbumTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Album? album;
2021-11-01 16:41:25 +00:00
final void Function()? onTap;
final void Function()? onHold;
2021-09-01 12:38:32 +00:00
final Widget? trailing;
2020-06-23 19:23:12 +00:00
AlbumTile(this.album, {this.onTap, this.onHold, this.trailing});
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
2021-09-01 12:38:32 +00:00
album!.title!,
2020-06-23 19:23:12 +00:00
maxLines: 1,
),
subtitle: Text(
2021-09-01 12:38:32 +00:00
album!.artistString,
2020-06-23 19:23:12 +00:00
maxLines: 1,
),
leading: CachedImage(
2021-09-01 12:38:32 +00:00
url: album!.art!.thumb,
width: 48,
2020-06-23 19:23:12 +00:00
),
2021-11-01 16:41:25 +00:00
onTap: onTap,
onLongPress: onHold,
2020-06-23 19:23:12 +00:00
trailing: trailing,
);
}
}
class ArtistTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Artist? artist;
2021-11-01 16:41:25 +00:00
final void Function()? onTap;
final void Function()? onHold;
2020-06-23 19:23:12 +00:00
ArtistTile(this.artist, {this.onTap, this.onHold});
@override
Widget build(BuildContext context) {
return SizedBox(
2021-09-01 12:38:32 +00:00
width: 150,
2021-11-01 16:41:25 +00:00
child: InkWell(
onTap: onTap,
onLongPress: onHold,
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
const SizedBox(height: 4),
CachedImage(
url: artist!.picture!.thumb,
circular: true,
width: 100,
),
const SizedBox(height: 8),
Text(
artist!.name!,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14.0),
),
const SizedBox(height: 4),
])));
2020-06-23 19:23:12 +00:00
}
}
class PlaylistTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Playlist? playlist;
2021-11-01 16:41:25 +00:00
final void Function()? onTap;
final void Function()? onHold;
2021-09-01 12:38:32 +00:00
final Widget? trailing;
2020-06-23 19:23:12 +00:00
PlaylistTile(this.playlist, {this.onHold, this.onTap, this.trailing});
2021-09-01 12:38:32 +00:00
String? get subtitle {
if (playlist!.user == null ||
playlist!.user!.name == null ||
playlist!.user!.name == '' ||
playlist!.user!.id == deezerAPI.userId) {
if (playlist!.trackCount == null) return '';
return '${playlist!.trackCount} ' + 'Tracks'.i18n;
}
2021-09-01 12:38:32 +00:00
return playlist!.user!.name;
}
2020-06-23 19:23:12 +00:00
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
2021-09-01 12:38:32 +00:00
playlist!.title!,
2020-06-23 19:23:12 +00:00
maxLines: 1,
),
subtitle: Text(
2021-09-01 12:38:32 +00:00
subtitle!,
2020-06-23 19:23:12 +00:00
maxLines: 1,
),
leading: CachedImage(
2021-09-01 12:38:32 +00:00
url: playlist!.image!.thumb,
width: 48,
2020-06-23 19:23:12 +00:00
),
2021-11-01 16:41:25 +00:00
onTap: onTap,
onLongPress: onHold,
2020-06-23 19:23:12 +00:00
trailing: trailing,
);
}
}
class ArtistHorizontalTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Artist? artist;
2021-11-01 16:41:25 +00:00
final void Function()? onTap;
final void Function()? onHold;
2021-09-01 12:38:32 +00:00
final Widget? trailing;
2020-06-23 19:23:12 +00:00
ArtistHorizontalTile(this.artist, {this.onHold, this.onTap, this.trailing});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
child: ListTile(
title: Text(
2021-09-01 12:38:32 +00:00
artist!.name!,
maxLines: 1,
),
leading: CachedImage(
2021-09-01 12:38:32 +00:00
url: artist!.picture!.thumb,
circular: true,
),
2021-11-01 16:41:25 +00:00
onTap: onTap,
onLongPress: onHold,
trailing: trailing,
2020-06-23 19:23:12 +00:00
),
);
}
}
class PlaylistCardTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Playlist? playlist;
final Function? onTap;
final Function? onHold;
2020-06-23 19:23:12 +00:00
PlaylistCardTile(this.playlist, {this.onTap, this.onHold});
@override
Widget build(BuildContext context) {
2020-11-01 19:23:24 +00:00
return Container(
2021-09-01 12:38:32 +00:00
height: 180.0,
child: InkWell(
onTap: onTap as void Function()?,
onLongPress: onHold as void Function()?,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(8),
child: CachedImage(
url: playlist!.image!.thumb,
width: 128,
height: 128,
rounded: true,
),
2020-06-23 19:23:12 +00:00
),
2021-09-01 12:38:32 +00:00
Container(height: 2.0),
Container(
width: 144,
child: Text(
playlist!.title!,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14.0),
),
2020-06-23 19:23:12 +00:00
),
2021-09-01 12:38:32 +00:00
Container(
height: 4.0,
)
],
),
));
2020-06-23 19:23:12 +00:00
}
}
class SmartTrackListTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final SmartTrackList? smartTrackList;
final Function? onTap;
final Function? onHold;
2020-06-23 19:23:12 +00:00
SmartTrackListTile(this.smartTrackList, {this.onHold, this.onTap});
@override
Widget build(BuildContext context) {
2020-11-01 19:23:24 +00:00
return Container(
2021-02-09 20:14:14 +00:00
height: 210.0,
2020-06-23 19:23:12 +00:00
child: InkWell(
2021-09-01 12:38:32 +00:00
onTap: onTap as void Function()?,
onLongPress: onHold as void Function()?,
2020-06-23 19:23:12 +00:00
child: Column(
children: <Widget>[
Padding(
2021-09-01 12:38:32 +00:00
padding: EdgeInsets.all(8.0),
child: Stack(
children: [
CachedImage(
width: 128,
height: 128,
url: smartTrackList!.cover!.thumb,
rounded: true,
),
Container(
width: 128.0,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0),
child: Text(
smartTrackList!.title!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18.0,
shadows: [
Shadow(
offset: Offset(1, 1),
blurRadius: 2,
color: Colors.black)
],
color: Colors.white),
),
),
2021-09-01 12:38:32 +00:00
)
],
)),
2020-06-23 19:23:12 +00:00
Container(
width: 144.0,
child: Text(
2021-09-01 12:38:32 +00:00
smartTrackList!.subtitle!,
maxLines: 3,
2020-06-23 19:23:12 +00:00
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
2021-09-01 12:38:32 +00:00
style: TextStyle(fontSize: 14.0),
2020-06-23 19:23:12 +00:00
),
),
2021-09-01 12:38:32 +00:00
Container(
height: 8.0,
)
2020-06-23 19:23:12 +00:00
],
),
),
);
}
}
class AlbumCard extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Album? album;
final Function? onTap;
final Function? onHold;
2020-06-23 19:23:12 +00:00
AlbumCard(this.album, {this.onTap, this.onHold});
@override
Widget build(BuildContext context) {
2020-11-01 19:23:24 +00:00
return Container(
2021-09-01 12:38:32 +00:00
child: InkWell(
onTap: onTap as void Function()?,
onLongPress: onHold as void Function()?,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: CachedImage(
2020-06-23 19:23:12 +00:00
width: 128.0,
height: 128.0,
2021-09-01 12:38:32 +00:00
url: album!.art!.thumb,
rounded: true),
),
Container(
width: 144.0,
child: Text(
album!.title!,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14.0),
),
2021-09-01 12:38:32 +00:00
),
Container(height: 4.0),
Container(
width: 144.0,
child: Text(
album!.artistString,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.0,
2021-09-01 12:38:32 +00:00
color: (Theme.of(context).brightness == Brightness.light)
? Colors.grey[800]
: Colors.white70),
),
2021-09-01 12:38:32 +00:00
),
Container(
height: 8.0,
)
],
),
));
2020-06-23 19:23:12 +00:00
}
}
class ChannelTile extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final DeezerChannel? channel;
final Function? onTap;
2020-06-23 19:23:12 +00:00
ChannelTile(this.channel, {this.onTap});
Color _textColor() {
2021-09-01 12:38:32 +00:00
double luminance = channel!.backgroundColor.computeLuminance();
return (luminance > 0.5) ? Colors.black : Colors.white;
2020-06-23 19:23:12 +00:00
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Card(
2021-09-01 12:38:32 +00:00
color: channel!.backgroundColor,
child: InkWell(
onTap: this.onTap as void Function()?,
child: Container(
width: 150,
height: 75,
child: Center(
child: Text(
channel!.title!,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: _textColor()),
),
2020-06-23 19:23:12 +00:00
),
),
2021-09-01 12:38:32 +00:00
)),
2020-06-23 19:23:12 +00:00
);
}
}
2020-11-28 21:32:17 +00:00
class ShowCard extends StatelessWidget {
2021-09-01 12:38:32 +00:00
final Show? show;
final Function? onTap;
final Function? onHold;
2020-11-28 21:32:17 +00:00
ShowCard(this.show, {this.onTap, this.onHold});
@override
Widget build(BuildContext context) {
return Container(
child: InkWell(
2021-09-01 12:38:32 +00:00
onTap: onTap as void Function()?,
onLongPress: onHold as void Function()?,
2020-11-28 21:32:17 +00:00
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: CachedImage(
2021-09-01 12:38:32 +00:00
url: show!.art!.thumb,
2020-11-28 21:32:17 +00:00
width: 128.0,
height: 128.0,
rounded: true,
),
),
Container(
width: 144.0,
child: Text(
2021-09-01 12:38:32 +00:00
show!.name!,
2020-11-28 21:32:17 +00:00
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
2021-09-01 12:38:32 +00:00
style: TextStyle(fontSize: 14.0),
2020-11-28 21:32:17 +00:00
),
),
],
),
),
);
}
}
class ShowTile extends StatelessWidget {
final Show show;
2021-09-01 12:38:32 +00:00
final Function? onTap;
final Function? onHold;
ShowTile(this.show, {this.onTap, this.onHold});
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
2021-09-01 12:38:32 +00:00
show.name!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
2021-09-01 12:38:32 +00:00
show.description!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
2021-09-01 12:38:32 +00:00
onTap: onTap as void Function()?,
onLongPress: onHold as void Function()?,
leading: CachedImage(
2021-09-01 12:38:32 +00:00
url: show.art!.thumb,
width: 48,
),
);
}
}
2020-11-28 21:32:17 +00:00
class ShowEpisodeTile extends StatelessWidget {
final ShowEpisode episode;
2021-09-01 12:38:32 +00:00
final Function? onTap;
final Function? onHold;
final Widget? trailing;
2020-11-28 21:32:17 +00:00
ShowEpisodeTile(this.episode, {this.onTap, this.onHold, this.trailing});
@override
Widget build(BuildContext context) {
return InkWell(
2021-09-01 12:38:32 +00:00
onLongPress: onHold as void Function()?,
onTap: onTap as void Function()?,
2020-11-28 21:32:17 +00:00
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
2021-09-01 12:38:32 +00:00
title: Text(episode.title!, maxLines: 2),
2020-11-28 21:32:17 +00:00
trailing: trailing,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
2021-09-01 12:38:32 +00:00
episode.description!,
2020-11-28 21:32:17 +00:00
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
2021-09-01 12:38:32 +00:00
color: Theme.of(context)
.textTheme
.subtitle1!
.color!
.withOpacity(0.9)),
2020-11-28 21:32:17 +00:00
),
),
Padding(
padding: EdgeInsets.fromLTRB(16, 8.0, 0, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Text(
'${episode.publishedDate} | ${episode.durationString}',
textAlign: TextAlign.left,
style: TextStyle(
2021-09-01 12:38:32 +00:00
fontSize: 12.0,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.textTheme
.subtitle1!
.color!
.withOpacity(0.6)),
2020-11-28 21:32:17 +00:00
),
],
),
),
Divider(),
],
),
);
}
}