mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
At some places <something>.of(context) was used multiple times in the same widget. This, although small, can have an impact on performance that's just plain unnecessary. It's better to just get things you need out of the context first before you do anything else.
231 lines
6.0 KiB
Dart
231 lines
6.0 KiB
Dart
import 'package:audio_service/audio_service.dart';
|
|
import 'package:auto_route/auto_route.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
import '../cache/image_cache.dart';
|
|
import '../models/support.dart';
|
|
import '../services/audio_service.dart';
|
|
import '../state/audio.dart';
|
|
import '../state/theme.dart';
|
|
import 'app_router.dart';
|
|
import 'images.dart';
|
|
import 'pages/now_playing_page.dart';
|
|
|
|
class NowPlayingBar extends HookConsumerWidget {
|
|
const NowPlayingBar({
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final colors = ref.watch(mediaItemThemeProvider).valueOrNull;
|
|
final noItem = ref.watch(mediaItemProvider).valueOrNull == null;
|
|
|
|
final widget = GestureDetector(
|
|
onTap: () {
|
|
context.navigateTo(const NowPlayingRoute());
|
|
},
|
|
child: Material(
|
|
elevation: 3,
|
|
color: colors?.darkBackground,
|
|
// surfaceTintColor: theme?.colorScheme.background,
|
|
child: const Column(
|
|
children: [
|
|
SizedBox(
|
|
height: 70,
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.all(10),
|
|
child: _ArtImage(),
|
|
),
|
|
Expanded(
|
|
child: Padding(
|
|
padding: EdgeInsets.only(right: 4),
|
|
child: _TrackInfo(),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.only(right: 16, top: 2),
|
|
child: PlayPauseButton(size: 48),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
_ProgressBar(),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
if (noItem) {
|
|
return Container();
|
|
}
|
|
|
|
if (colors != null) {
|
|
return Theme(data: colors.theme, child: widget);
|
|
} else {
|
|
return widget;
|
|
}
|
|
}
|
|
}
|
|
|
|
class _ArtImage extends HookConsumerWidget {
|
|
const _ArtImage();
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final imageCache = ref.watch(imageCacheProvider);
|
|
final uri =
|
|
ref.watch(mediaItemProvider.select((e) => e.valueOrNull?.artUri));
|
|
final cacheKey = ref.watch(mediaItemDataProvider.select(
|
|
(value) => value?.artCache?.thumbnailArtCacheKey,
|
|
));
|
|
|
|
UriCacheInfo? cache;
|
|
if (uri != null && cacheKey != null) {
|
|
cache = UriCacheInfo(
|
|
uri: uri,
|
|
cacheKey: cacheKey,
|
|
cacheManager: imageCache,
|
|
);
|
|
}
|
|
|
|
return AnimatedSwitcher(
|
|
duration: const Duration(milliseconds: 150),
|
|
child: CardClip(
|
|
key: ValueKey(cacheKey ?? 'default'),
|
|
child: cache == null
|
|
? const PlaceholderImage()
|
|
: UriCacheInfoImage(
|
|
cache: cache,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TrackInfo extends HookConsumerWidget {
|
|
const _TrackInfo();
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final theme = Theme.of(context);
|
|
|
|
final item = ref.watch(mediaItemProvider);
|
|
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
...item.when(
|
|
data: (data) => [
|
|
// Text(
|
|
// data?.title ?? 'Nothing!!!',
|
|
// maxLines: 1,
|
|
// softWrap: false,
|
|
// overflow: TextOverflow.fade,
|
|
// style: theme.textTheme.labelLarge,
|
|
// ),
|
|
ScrollableText(
|
|
data?.title ?? 'Nothing!!!',
|
|
style: theme.textTheme.labelLarge,
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
data?.artist ?? 'Nothing!!!',
|
|
maxLines: 1,
|
|
softWrap: false,
|
|
overflow: TextOverflow.fade,
|
|
style: theme.textTheme.labelMedium,
|
|
),
|
|
],
|
|
error: (_, __) => const [Text('Error!')],
|
|
loading: () => const [Text('loading.....')],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class PlayPauseButton extends HookConsumerWidget {
|
|
final double size;
|
|
|
|
const PlayPauseButton({
|
|
super.key,
|
|
required this.size,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final theme = Theme.of(context);
|
|
|
|
final playing = ref.watch(playingProvider);
|
|
final state = ref.watch(processingStateProvider);
|
|
|
|
Widget icon;
|
|
if (state == AudioProcessingState.loading ||
|
|
state == AudioProcessingState.buffering) {
|
|
icon = Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
const Icon(Icons.circle),
|
|
SizedBox(
|
|
height: size / 3,
|
|
width: size / 3,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: size / 16,
|
|
color: theme.colorScheme.surface,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
} else if (playing) {
|
|
icon = const Icon(Icons.pause_circle_rounded);
|
|
} else {
|
|
icon = const Icon(Icons.play_circle_rounded);
|
|
}
|
|
|
|
return IconButton(
|
|
iconSize: size,
|
|
padding: EdgeInsets.zero,
|
|
onPressed: () {
|
|
if (playing) {
|
|
ref.read(audioControlProvider).pause();
|
|
} else {
|
|
ref.read(audioControlProvider).play();
|
|
}
|
|
},
|
|
icon: icon,
|
|
color: theme.colorScheme.surface,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ProgressBar extends HookConsumerWidget {
|
|
const _ProgressBar();
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final colors = ref.watch(mediaItemThemeProvider).valueOrNull;
|
|
final position = ref.watch(positionProvider);
|
|
final duration = ref.watch(durationProvider);
|
|
|
|
return Container(
|
|
height: 4,
|
|
color: colors?.darkerBackground,
|
|
child: Row(
|
|
children: [
|
|
Flexible(
|
|
flex: position,
|
|
child: Container(color: colors?.onDarkerBackground),
|
|
),
|
|
Flexible(flex: duration - position, child: Container()),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|