Skip to content

Commit

Permalink
Improved loading indicater and Now Playing bar transitions. Added set…
Browse files Browse the repository at this point in the history
…ting to diable queue autoload.
  • Loading branch information
Komodo5197 committed Dec 6, 2023
1 parent c2bfcd7 commit 36871c1
Show file tree
Hide file tree
Showing 15 changed files with 584 additions and 511 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:hive/hive.dart';

import '../../services/finamp_settings_helper.dart';
import '../../models/finamp_models.dart';

class LoadQueueOnStartupSelector extends StatelessWidget {
const LoadQueueOnStartupSelector({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return ValueListenableBuilder<Box<FinampSettings>>(
valueListenable: FinampSettingsHelper.finampSettingsListener,
builder: (_, box, __) {
return SwitchListTile.adaptive(
title:
Text(AppLocalizations.of(context)!.autoloadLastQueueOnStartup),
subtitle: Text(AppLocalizations.of(context)!
.autoloadLastQueueOnStartupSubtitle),
value:
FinampSettingsHelper.finampSettings.autoloadLastQueueOnStartup,
onChanged: (value) =>
FinampSettingsHelper.setAutoloadLastQueueOnStartup(value),
);
},
);
}
}
2 changes: 1 addition & 1 deletion lib/components/PlayerScreen/queue_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ Future<dynamic> showQueueBottomSheet(BuildContext context) {
builder: (context) {
return Consumer(
builder: (BuildContext context, WidgetRef ref, Widget? child) {
final imageTheme = ref.watch(playerScreenThemeProvider);
final imageTheme = ref.watch(playerScreenThemeProvider(context)).value;

return AnimatedTheme(
duration: const Duration(milliseconds: 500),
Expand Down
55 changes: 3 additions & 52 deletions lib/components/PlayerScreen/song_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:palette_generator/palette_generator.dart';

import '../../generate_material_color.dart';
import '../../models/jellyfin_models.dart' as jellyfin_models;
import '../../screens/artist_screen.dart';
import '../../services/current_album_image_provider.dart';
import '../../services/finamp_settings_helper.dart';
import '../../services/jellyfin_api_helper.dart';
import '../../services/music_player_background_task.dart';
import '../../services/player_screen_theme_provider.dart';
import 'song_name_content.dart';
import '../album_image.dart';
import '../../at_contrast.dart';

/// Album image and song name/album etc. We do this in one widget to share a
/// StreamBuilder and to make alignment easier.
Expand Down Expand Up @@ -118,7 +113,7 @@ class _SongInfoState extends State<SongInfo> {
}
}

class _PlayerScreenAlbumImage extends ConsumerWidget {
class _PlayerScreenAlbumImage extends StatelessWidget {
_PlayerScreenAlbumImage({
Key? key,
required this.queueItem,
Expand All @@ -127,7 +122,7 @@ class _PlayerScreenAlbumImage extends ConsumerWidget {
final FinampQueueItem queueItem;

@override
Widget build(BuildContext context, WidgetRef ref) {
Widget build(BuildContext context) {
final queueService = GetIt.instance<QueueService>();

final item = queueItem.item.extras?["itemJson"] != null
Expand Down Expand Up @@ -166,51 +161,7 @@ class _PlayerScreenAlbumImage extends ConsumerWidget {
: null;
return item!;
}).toList(),
// We need a post frame callback because otherwise this
// widget rebuilds on the same frame
imageProviderCallback: (imageProvider) =>
WidgetsBinding.instance.addPostFrameCallback((_) async {
// Don't do anything if the image from the callback is the same as
// the current provider's image. This is probably needed because of
// addPostFrameCallback shenanigans
if (imageProvider != null &&
ref.read(currentAlbumImageProvider.notifier).state ==
imageProvider) {
return;
}

ref.read(currentAlbumImageProvider.notifier).state = imageProvider;

if (imageProvider != null) {
final theme = Theme.of(context);

final palette =
await PaletteGenerator.fromImageProvider(
imageProvider,
timeout: const Duration(milliseconds: 2000),
);

// Color accent = palette.dominantColor!.color;
Color accent = palette.vibrantColor?.color ?? palette.dominantColor?.color ?? const Color.fromARGB(255, 0, 164, 220);

final lighter = theme.brightness == Brightness.dark;

final background = Color.alphaBlend(
lighter
? Colors.black.withOpacity(0.675)
: Colors.white.withOpacity(0.675),
accent);

accent = accent.atContrast(4.5, background, lighter);

ref.read(playerScreenThemeProvider.notifier).state =
ColorScheme.fromSwatch(
primarySwatch: generateMaterialColor(accent),
accentColor: accent,
brightness: theme.brightness,
);
}
}),
updateProvider: true,
),
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';

import '../models/finamp_models.dart';
import '../services/queue_service.dart';
import 'album_image.dart';
import 'error_snackbar.dart';
import '../../models/finamp_models.dart';
import '../../services/queue_service.dart';
import '../album_image.dart';
import '../error_snackbar.dart';

class QueueRestoreTile extends StatelessWidget {
const QueueRestoreTile({Key? key, required this.info}) : super(key: key);
Expand Down
47 changes: 29 additions & 18 deletions lib/components/album_image.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:finamp/services/current_album_image_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:octo_image/octo_image.dart';

import '../models/jellyfin_models.dart';
Expand All @@ -11,11 +12,11 @@ typedef ImageProviderCallback = void Function(ImageProvider? imageProvider);
/// Aspect ratio 1 with a circular border radius of 4. If you don't want these
/// customisations, use [BareAlbumImage] or get an [ImageProvider] directly
/// through [AlbumImageProvider.init].
class AlbumImage extends StatelessWidget {
class AlbumImage extends ConsumerWidget {
const AlbumImage({
Key? key,
this.item,
this.imageProviderCallback,
this.updateProvider = false,
this.itemsToPrecache,
this.borderRadius,
}) : super(key: key);
Expand All @@ -24,7 +25,7 @@ class AlbumImage extends StatelessWidget {
final BaseItemDto? item;

/// A callback to get the image provider once it has been fetched.
final ImageProviderCallback? imageProviderCallback;
final bool updateProvider;

/// A list of items to precache
final List<BaseItemDto>? itemsToPrecache;
Expand All @@ -34,12 +35,12 @@ class AlbumImage extends StatelessWidget {
static final defaultBorderRadius = BorderRadius.circular(4);

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final borderRadius = this.borderRadius ?? defaultBorderRadius;

if (item == null || item!.imageId == null) {
if (imageProviderCallback != null) {
imageProviderCallback!(null);
if (updateProvider) {
_BareAlbumImageState.registerThemeUpdate(null, ref);
}

return ClipRRect(
Expand Down Expand Up @@ -71,7 +72,7 @@ class AlbumImage extends StatelessWidget {
item: item!,
maxWidth: physicalWidth,
maxHeight: physicalHeight,
imageProviderCallback: imageProviderCallback,
updateProvider: updateProvider,
itemsToPrecache: itemsToPrecache,
);
}),
Expand All @@ -81,33 +82,33 @@ class AlbumImage extends StatelessWidget {
}

/// An [AlbumImage] without any of the padding or media size detection.
class BareAlbumImage extends StatefulWidget {
class BareAlbumImage extends ConsumerStatefulWidget {
const BareAlbumImage({
Key? key,
required this.item,
this.maxWidth,
this.maxHeight,
this.errorBuilder,
this.placeholderBuilder,
this.imageProviderCallback,
this.itemsToPrecache,
this.updateProvider = false,
}) : super(key: key);

final BaseItemDto item;
final int? maxWidth;
final int? maxHeight;
final WidgetBuilder? placeholderBuilder;
final OctoErrorBuilder? errorBuilder;
final ImageProviderCallback? imageProviderCallback;
final bool updateProvider;

/// A list of items to precache
final List<BaseItemDto>? itemsToPrecache;

@override
State<BareAlbumImage> createState() => _BareAlbumImageState();
ConsumerState<BareAlbumImage> createState() => _BareAlbumImageState();
}

class _BareAlbumImageState extends State<BareAlbumImage> {
class _BareAlbumImageState extends ConsumerState<BareAlbumImage>{
late Future<ImageProvider?> _albumImageContentFuture;
late WidgetBuilder _placeholderBuilder;
late OctoErrorBuilder _errorBuilder;
Expand Down Expand Up @@ -155,14 +156,24 @@ class _BareAlbumImageState extends State<BareAlbumImage> {
(context, _, __) => const _AlbumImageErrorPlaceholder();
}

static void registerThemeUpdate (ImageProvider? imageProvider, WidgetRef ref) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
// Do not update provider if values are == to prevent redraw loops.
if (ref.read(currentAlbumImageProvider.notifier).state == imageProvider) {
return;
}
ref.read(currentAlbumImageProvider.notifier).state = imageProvider;
});
}

@override
Widget build(BuildContext context) {
return FutureBuilder<ImageProvider?>(
future: _albumImageContentFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (widget.imageProviderCallback != null) {
widget.imageProviderCallback!(snapshot.data!);
if (widget.updateProvider) {
_BareAlbumImageState.registerThemeUpdate(snapshot.data!, ref);
}

return OctoImage(
Expand All @@ -174,14 +185,14 @@ class _BareAlbumImageState extends State<BareAlbumImage> {
}

if (snapshot.hasError) {
if (widget.imageProviderCallback != null) {
widget.imageProviderCallback!(null);
if (widget.updateProvider) {
_BareAlbumImageState.registerThemeUpdate(null, ref);
}
return const _AlbumImageErrorPlaceholder();
}

if (widget.imageProviderCallback != null) {
widget.imageProviderCallback!(null);
if (widget.updateProvider) {
_BareAlbumImageState.registerThemeUpdate(null, ref);
}

return Builder(builder: _placeholderBuilder);
Expand Down
Loading

0 comments on commit 36871c1

Please sign in to comment.