Screens / lists
Music library
Lists a listener's songs, albums as a grid of artwork and playlists in tabs, searchable by title or artist, loaded page by page, with a mini player for the song now playing.
Import
Music library is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:
fliin login
fliin add libraryAlso installs: alert, app-bar, button, core, empty-state, list-row, mini-player, page, result-card, search-field, separator, skeleton, spinner, tabs, text, text-field, track-row
Examples
Songs
The songs, loaded ten at a time: tapping one plays it in the mini player, which nowPlaying drives. standalone holds the library over the LibraryService you pass.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibrarySongsExample extends StatefulWidget {
const LibrarySongsExample({super.key});
@override
State<LibrarySongsExample> createState() => _LibrarySongsExampleState();
}
class _LibrarySongsExampleState extends State<LibrarySongsExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: LibraryShown(sampleLibraryTracks.take(10).toList(), total: 24, more: const LibraryMoreAvailable('10')),
tab: LibraryTab.songs,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Albums
The albums tab, as a grid of covers.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryAlbumsExample extends StatefulWidget {
const LibraryAlbumsExample({super.key});
@override
State<LibraryAlbumsExample> createState() => _LibraryAlbumsExampleState();
}
class _LibraryAlbumsExampleState extends State<LibraryAlbumsExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: LibraryShown(sampleLibraryAlbums, total: 6),
tab: LibraryTab.albums,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Playlists
The playlists tab, each with its track count.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryPlaylistsExample extends StatefulWidget {
const LibraryPlaylistsExample({super.key});
@override
State<LibraryPlaylistsExample> createState() => _LibraryPlaylistsExampleState();
}
class _LibraryPlaylistsExampleState extends State<LibraryPlaylistsExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: LibraryShown(sampleLibraryPlaylists, total: 4),
tab: LibraryTab.playlists,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}No match
A search that matches nothing in the library says so and offers to clear it.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryNoMatchExample extends StatefulWidget {
const LibraryNoMatchExample({super.key});
@override
State<LibraryNoMatchExample> createState() => _LibraryNoMatchExampleState();
}
class _LibraryNoMatchExampleState extends State<LibraryNoMatchExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: const LibraryNoMatch('violin'),
tab: LibraryTab.songs,
query: 'violin',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Empty
A tab with nothing in it yet.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryEmptyExample extends StatefulWidget {
const LibraryEmptyExample({super.key});
@override
State<LibraryEmptyExample> createState() => _LibraryEmptyExampleState();
}
class _LibraryEmptyExampleState extends State<LibraryEmptyExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: const LibraryEmpty(),
tab: LibraryTab.playlists,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Loading
While the first page loads, skeletons keep the place of the rows.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryLoadingExample extends StatefulWidget {
const LibraryLoadingExample({super.key});
@override
State<LibraryLoadingExample> createState() => _LibraryLoadingExampleState();
}
class _LibraryLoadingExampleState extends State<LibraryLoadingExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: const LibraryLoading(),
tab: LibraryTab.songs,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Error
When loading fails, the screen says so and offers to try again.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryErrorExample extends StatefulWidget {
const LibraryErrorExample({super.key});
@override
State<LibraryErrorExample> createState() => _LibraryErrorExampleState();
}
class _LibraryErrorExampleState extends State<LibraryErrorExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: const LibraryFailed(),
tab: LibraryTab.songs,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Offline
Without a connection, the screen asks to reconnect.
import 'package:your_app/ui/fliin/components/result_card/fl_result_card.dart';
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryOfflineExample extends StatefulWidget {
const LibraryOfflineExample({super.key});
@override
State<LibraryOfflineExample> createState() => _LibraryOfflineExampleState();
}
class _LibraryOfflineExampleState extends State<LibraryOfflineExample> {
final _player = SampleNowPlaying(
const LibraryNowPlaying(
trackId: 'track-1',
title: 'October Light',
artist: 'The Tides',
playing: true,
artwork: FlSampleImage(FlSampleArt.lamp),
),
);
@override
void dispose() {
_player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
initial: const LibraryOffline(),
tab: LibraryTab.songs,
query: '',
nowPlaying: _player,
onPlay: _player.play,
onPlayPause: _player.playPause,
onOpenPlayer: () {},
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Tabs
Only the tabs your app has: tabs lists them in their order, here the playlists first, then the albums, and no tracks tab.
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:flutter/widgets.dart';
class LibraryTabsExample extends StatelessWidget {
const LibraryTabsExample({super.key});
@override
Widget build(BuildContext context) {
return LibraryScreen.standalone(
service: SampleLibraryService(latency: const Duration(milliseconds: 900)),
tabs: const [LibraryTab.playlists, LibraryTab.albums],
initial: LibraryShown(sampleLibraryPlaylists, total: 4),
onOpenAlbum: (_) {},
onOpenPlaylist: (_) {},
);
}
}Routing
fliin add library prints the route for the router it finds in your pubspec, a file next to the page that wires its navigation. Each of them:
go_router
// lib/library_route.dart, next to library_page.dart.
import 'package:go_router/go_router.dart';
import 'library_page.dart';
/// Add libraryRoute to the routes of your GoRouter.
final libraryRoute = GoRoute(
path: '/library',
builder: (context, state) => LibraryScreenPage(
onOpenPlayer: () => context.push('/player'),
// The path of the page of an album in your app.
onOpenAlbum: (album) => context.push('/albums/${Uri.encodeComponent(album.id)}'),
// The path of the page of a playlist in your app.
onOpenPlaylist: (playlist) => context.push('/playlists/${Uri.encodeComponent(playlist.id)}'),
),
);navigator
// lib/library_route.dart, next to library_page.dart.
import 'package:flutter/material.dart';
import 'library_page.dart';
import 'player_route.dart';
/// Opens “Music library”: `Navigator.of(context).push(libraryRoute(albumPage: …, playlistPage: …))`.
/// [albumPage] builds the page of an album in your app.
/// [playlistPage] builds the page of a playlist in your app.
Route<void> libraryRoute({
required Widget Function(String albumId) albumPage,
required Widget Function(String playlistId) playlistPage,
}) => MaterialPageRoute<void>(
builder: (context) => LibraryScreenPage(
onOpenPlayer: () => Navigator.of(context).push(playerRoute()),
onOpenAlbum: (album) => Navigator.of(context).push(MaterialPageRoute<void>(builder: (_) => albumPage(album.id))),
onOpenPlaylist: (playlist) => Navigator.of(context).push(MaterialPageRoute<void>(builder: (_) => playlistPage(playlist.id))),
),
);State
The controller is a ChangeNotifier from widgets.dart, with no package. fliin add library prints the page that holds it with the state management it finds, its imports written for your package; below they read your_app. Your backend implements the Service interface of the screen's _data.dart file:
none
// lib/library_page.dart
// Your backend: implement LibraryService (library_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:your_app/ui/fliin/screens/player/player_data.dart';
import 'package:flutter/widgets.dart';
import 'player_page.dart';
// The mini player shows the app's player, the one of PlayerPage: a
// track tapped here plays there, and a pause there shows here.
final libraryNowPlaying = LibraryNowPlayingOf(
samplePlayer.playback,
(playback) => switch (playback) {
PlaybackReady(:final track, :final playing, :final buffering) => LibraryNowPlaying(
trackId: track.id,
title: track.title,
artist: track.artist,
artwork: track.artwork,
playing: playing,
buffering: buffering,
),
_ => null,
},
);
void playLibraryTrack(LibraryTrack track) => samplePlayer.playTrack(
PlayerTrack(
id: track.id,
title: track.title,
artist: track.artist,
album: track.album,
duration: track.duration,
artwork: track.artwork,
),
);
class LibraryScreenPage extends StatelessWidget {
const LibraryScreenPage({
super.key,
required this.onOpenPlayer,
required this.onOpenAlbum,
required this.onOpenPlaylist,
});
final VoidCallback onOpenPlayer;
final ValueChanged<LibraryAlbum> onOpenAlbum;
final ValueChanged<LibraryPlaylist> onOpenPlaylist;
@override
Widget build(BuildContext context) => LibraryScreen.standalone(
service: SampleLibraryService(),
nowPlaying: libraryNowPlaying,
onPlay: playLibraryTrack,
onPlayPause: samplePlayer.playPause,
onOpenPlayer: onOpenPlayer,
onOpenAlbum: onOpenAlbum,
onOpenPlaylist: onOpenPlaylist,
);
}provider
// lib/library_page.dart
// Your backend: implement LibraryService (library_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:your_app/ui/fliin/screens/player/player_data.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'player_page.dart';
// The mini player shows the app's player, the one of PlayerPage: a
// track tapped here plays there, and a pause there shows here.
final libraryNowPlaying = LibraryNowPlayingOf(
samplePlayer.playback,
(playback) => switch (playback) {
PlaybackReady(:final track, :final playing, :final buffering) => LibraryNowPlaying(
trackId: track.id,
title: track.title,
artist: track.artist,
artwork: track.artwork,
playing: playing,
buffering: buffering,
),
_ => null,
},
);
void playLibraryTrack(LibraryTrack track) => samplePlayer.playTrack(
PlayerTrack(
id: track.id,
title: track.title,
artist: track.artist,
album: track.album,
duration: track.duration,
artwork: track.artwork,
),
);
class LibraryScreenPage extends StatelessWidget {
const LibraryScreenPage({
super.key,
required this.onOpenPlayer,
required this.onOpenAlbum,
required this.onOpenPlaylist,
});
final VoidCallback onOpenPlayer;
final ValueChanged<LibraryAlbum> onOpenAlbum;
final ValueChanged<LibraryPlaylist> onOpenPlaylist;
@override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (_) => LibraryController(service: SampleLibraryService())..load(),
child: Builder(
builder: (context) => LibraryScreen(
controller: context.read<LibraryController>(),
nowPlaying: libraryNowPlaying,
onPlay: playLibraryTrack,
onPlayPause: samplePlayer.playPause,
onOpenPlayer: onOpenPlayer,
onOpenAlbum: onOpenAlbum,
onOpenPlaylist: onOpenPlaylist,
),
),
);
}riverpod
// lib/library_page.dart
// Your backend: implement LibraryService (library_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:your_app/ui/fliin/screens/player/player_data.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'player_page.dart';
// The mini player shows the app's player, the one of PlayerPage: a
// track tapped here plays there, and a pause there shows here.
final libraryNowPlaying = LibraryNowPlayingOf(
samplePlayer.playback,
(playback) => switch (playback) {
PlaybackReady(:final track, :final playing, :final buffering) => LibraryNowPlaying(
trackId: track.id,
title: track.title,
artist: track.artist,
artwork: track.artwork,
playing: playing,
buffering: buffering,
),
_ => null,
},
);
void playLibraryTrack(LibraryTrack track) => samplePlayer.playTrack(
PlayerTrack(
id: track.id,
title: track.title,
artist: track.artist,
album: track.album,
duration: track.duration,
artwork: track.artwork,
),
);
final libraryControllerProvider = Provider.autoDispose<LibraryController>((ref) {
final controller = LibraryController(service: SampleLibraryService())..load();
ref.onDispose(controller.dispose);
return controller;
});
class LibraryScreenPage extends ConsumerWidget {
const LibraryScreenPage({
super.key,
required this.onOpenPlayer,
required this.onOpenAlbum,
required this.onOpenPlaylist,
});
final VoidCallback onOpenPlayer;
final ValueChanged<LibraryAlbum> onOpenAlbum;
final ValueChanged<LibraryPlaylist> onOpenPlaylist;
@override
Widget build(BuildContext context, WidgetRef ref) => LibraryScreen(
controller: ref.watch(libraryControllerProvider),
nowPlaying: libraryNowPlaying,
onPlay: playLibraryTrack,
onPlayPause: samplePlayer.playPause,
onOpenPlayer: onOpenPlayer,
onOpenAlbum: onOpenAlbum,
onOpenPlaylist: onOpenPlaylist,
);
}bloc
// lib/library_page.dart
// Your backend: implement LibraryService (library_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/library/library_controller.dart';
import 'package:your_app/ui/fliin/screens/library/library_data.dart';
import 'package:your_app/ui/fliin/screens/library/library_screen.dart';
import 'package:your_app/ui/fliin/screens/player/player_data.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'player_page.dart';
// The mini player shows the app's player, the one of PlayerPage: a
// track tapped here plays there, and a pause there shows here.
final libraryNowPlaying = LibraryNowPlayingOf(
samplePlayer.playback,
(playback) => switch (playback) {
PlaybackReady(:final track, :final playing, :final buffering) => LibraryNowPlaying(
trackId: track.id,
title: track.title,
artist: track.artist,
artwork: track.artwork,
playing: playing,
buffering: buffering,
),
_ => null,
},
);
void playLibraryTrack(LibraryTrack track) => samplePlayer.playTrack(
PlayerTrack(
id: track.id,
title: track.title,
artist: track.artist,
album: track.album,
duration: track.duration,
artwork: track.artwork,
),
);
/// The screen's controller as a bloc, for your BlocListener and BlocBuilder:
/// one emission per controller notification. `version` tells them apart
/// when `state` does not change (mode, filters, text…, read them on
/// `controller`). The screen listens to the controller itself.
class LibraryCubit extends Cubit<({LibraryState state, int version})> {
LibraryCubit(this.controller) : super((state: controller.state, version: 0)) {
controller.addListener(_changed);
}
final LibraryController controller;
void _changed() => emit((state: controller.state, version: state.version + 1));
@override
Future<void> close() {
controller
..removeListener(_changed)
..dispose();
return super.close();
}
}
class LibraryScreenPage extends StatelessWidget {
const LibraryScreenPage({
super.key,
required this.onOpenPlayer,
required this.onOpenAlbum,
required this.onOpenPlaylist,
});
final VoidCallback onOpenPlayer;
final ValueChanged<LibraryAlbum> onOpenAlbum;
final ValueChanged<LibraryPlaylist> onOpenPlaylist;
@override
Widget build(BuildContext context) => BlocProvider(
create: (_) => LibraryCubit(LibraryController(service: SampleLibraryService())..load()),
child: Builder(
builder: (context) => LibraryScreen(
controller: context.read<LibraryCubit>().controller,
nowPlaying: libraryNowPlaying,
onPlay: playLibraryTrack,
onPlayPause: samplePlayer.playPause,
onOpenPlayer: onOpenPlayer,
onOpenAlbum: onOpenAlbum,
onOpenPlaylist: onOpenPlaylist,
),
),
);
}The snippets lead to player: install these screens too and paste their snippets.
API
LibraryScreen: class extends StatefulWidget
LibraryScreen.new: const new({super.key, required LibraryController controller, ValueListenable<LibraryNowPlaying?>? nowPlaying, ValueChanged<LibraryTrack>? onPlay, VoidCallback? onPlayPause, VoidCallback? onOpenPlayer, ValueChanged<LibraryAlbum>? onOpenAlbum, ValueChanged<LibraryPlaylist>? onOpenPlaylist, VoidCallback? onBack, List<LibraryTab> tabs})
LibraryScreen.standalone: const new({super.key, required LibraryService service, LibraryState? initial, LibraryTab tab, String query, ValueListenable<LibraryNowPlaying?>? nowPlaying, ValueChanged<LibraryTrack>? onPlay, VoidCallback? onPlayPause, VoidCallback? onOpenPlayer, ValueChanged<LibraryAlbum>? onOpenAlbum, ValueChanged<LibraryPlaylist>? onOpenPlaylist, VoidCallback? onBack, List<LibraryTab> tabs})
LibraryScreen.controller: get LibraryController?
LibraryScreen.service: get LibraryService?
LibraryScreen.initial: get LibraryState?
LibraryScreen.tab: get LibraryTab
LibraryScreen.query: get String
LibraryScreen.nowPlaying: get ValueListenable<LibraryNowPlaying?>?
LibraryScreen.onPlay: get ValueChanged<LibraryTrack>?
LibraryScreen.onPlayPause: get VoidCallback?
LibraryScreen.onOpenPlayer: get VoidCallback?
LibraryScreen.onOpenAlbum: get ValueChanged<LibraryAlbum>?
LibraryScreen.onOpenPlaylist: get ValueChanged<LibraryPlaylist>?
LibraryScreen.onBack: get VoidCallback?
LibraryScreen.tabs: get List<LibraryTab>
LibraryScreen.createState: State<LibraryScreen>()
LibraryState: sealed class permits {LibraryEmpty, LibraryFailed, LibraryLoading, LibraryNoMatch, LibraryOffline, LibraryShown}
LibraryState.new: const new()
LibraryLoading: final class extends LibraryState
LibraryLoading.new: const new()
LibraryFailed: final class extends LibraryState
LibraryFailed.new: const new()
LibraryOffline: final class extends LibraryState
LibraryOffline.new: const new()
LibraryEmpty: final class extends LibraryState
LibraryEmpty.new: const new()
LibraryNoMatch: final class extends LibraryState
LibraryNoMatch.new: const new(String query)
LibraryNoMatch.query: get String
LibraryMore: sealed class permits {LibraryMoreAvailable, LibraryMoreEnd, LibraryMoreFailed, LibraryMoreLoading}
LibraryMore.new: const new()
LibraryMoreAvailable: final class extends LibraryMore
LibraryMoreAvailable.new: const new(String after)
LibraryMoreAvailable.after: get String
LibraryMoreLoading: final class extends LibraryMore
LibraryMoreLoading.new: const new(String after)
LibraryMoreLoading.after: get String
LibraryMoreFailed: final class extends LibraryMore
LibraryMoreFailed.new: const new(String after, {required bool offline})
LibraryMoreFailed.after: get String
LibraryMoreFailed.offline: get bool
LibraryMoreEnd: final class extends LibraryMore
LibraryMoreEnd.new: const new()
LibraryNotice: enum {refreshFailed, refreshOffline}
LibraryNotice.refreshFailed: static final LibraryNotice
LibraryNotice.refreshOffline: static final LibraryNotice
LibraryShown: final class extends LibraryState
LibraryShown.new: new(List<LibraryEntry> entries, {required int total, LibraryMore more, bool refreshing, LibraryNotice? notice})
LibraryShown.entries: get List<LibraryEntry>
LibraryShown.total: get int
LibraryShown.more: get LibraryMore
LibraryShown.refreshing: get bool
LibraryShown.notice: get LibraryNotice?
LibraryController: class extends ChangeNotifier
LibraryController.new: new({required LibraryService service, LibraryTab tab, String query, LibraryState initial, Duration debounce})
LibraryController.service: get LibraryService
LibraryController.debounce: get Duration
LibraryController.state: get LibraryState
LibraryController.tab: get LibraryTab
LibraryController.query: get String
LibraryController.shownQuery: get String
LibraryController.load: Future<void>()
LibraryController.setTab: void(LibraryTab tab)
LibraryController.setQuery: void(String query)
LibraryController.submit: void()
LibraryController.refresh: Future<void>()
LibraryController.loadMore: Future<bool>()
LibraryController.dispose: void()
LibraryTab: enum {songs, albums, playlists}
LibraryTab.songs: static final LibraryTab
LibraryTab.albums: static final LibraryTab
LibraryTab.playlists: static final LibraryTab
LibraryEntry: sealed class permits {LibraryAlbum, LibraryPlaylist, LibraryTrack}
LibraryEntry.new: const new({required String id, required String title, ImageProvider? artwork})
LibraryEntry.id: get String
LibraryEntry.title: get String
LibraryEntry.artwork: get ImageProvider?
LibraryTrack: final class extends LibraryEntry
LibraryTrack.new: const new({required super.id, required super.title, required String artist, required String album, required Duration duration, super.artwork})
LibraryTrack.artist: get String
LibraryTrack.album: get String
LibraryTrack.duration: get Duration
LibraryAlbum: final class extends LibraryEntry
LibraryAlbum.new: const new({required super.id, required super.title, required String artist, required int year, super.artwork})
LibraryAlbum.artist: get String
LibraryAlbum.year: get int
LibraryPlaylist: final class extends LibraryEntry
LibraryPlaylist.new: const new({required super.id, required super.title, required int trackCount, super.artwork})
LibraryPlaylist.trackCount: get int
LibraryPage: final class
LibraryPage.new: const new({required List<LibraryEntry> entries, required int total, String? next})
LibraryPage.entries: get List<LibraryEntry>
LibraryPage.total: get int
LibraryPage.next: get String?
LibraryNoNetwork: final class implements Exception
LibraryNoNetwork.new: const new()
LibraryService: abstract interface class
LibraryService.new: new()
LibraryService.entries: abstract Future<LibraryPage>(LibraryTab tab, {String query, String? after})
LibraryNowPlaying: final class
LibraryNowPlaying.new: const new({required String trackId, required String title, required String artist, required bool playing, bool buffering, ImageProvider? artwork})
LibraryNowPlaying.trackId: get String
LibraryNowPlaying.title: get String
LibraryNowPlaying.artist: get String
LibraryNowPlaying.playing: get bool
LibraryNowPlaying.buffering: get bool
LibraryNowPlaying.artwork: get ImageProvider?
LibraryNowPlayingOf: final class<T> implements ValueListenable<LibraryNowPlaying?>
LibraryNowPlayingOf.new: const new(ValueListenable<T> source, LibraryNowPlaying? Function(T state) read)
LibraryNowPlayingOf.source: get ValueListenable<T>
LibraryNowPlayingOf.read: get LibraryNowPlaying? Function(T state)
LibraryNowPlayingOf.value: get LibraryNowPlaying?
LibraryNowPlayingOf.addListener: void(VoidCallback listener)
LibraryNowPlayingOf.removeListener: void(VoidCallback listener)
sampleLibraryTracks: get List<LibraryTrack>
sampleLibraryAlbums: const List<LibraryAlbum>
sampleLibraryPlaylists: const List<LibraryPlaylist>
sampleLibraryOf: List<LibraryEntry>(LibraryTab tab)
SampleLibraryService: final class implements LibraryService
SampleLibraryService.new: new({int pageSize, Duration latency, List<LibraryEntry> Function(LibraryTab tab) entriesOf})
SampleLibraryService.pageSize: get int
SampleLibraryService.latency: get Duration
SampleLibraryService.entriesOf: get List<LibraryEntry> Function(LibraryTab tab)
SampleLibraryService.requests: get List<(LibraryTab, String, String?)>
SampleLibraryService.entries: Future<LibraryPage>(LibraryTab tab, {String query, String? after})
SampleNowPlaying: final class extends ValueNotifier<LibraryNowPlaying?>
SampleNowPlaying.new: new([super.initial])
SampleNowPlaying.play: void(LibraryTrack track)
SampleNowPlaying.playPause: void()
libraryL10n: const Map<String, Map<String, String>>
LibraryStrings: final class
LibraryStrings.new: const new(String language)
LibraryStrings.of: factory new(BuildContext context)
LibraryStrings.language: get String
LibraryStrings.digits: String(int n)
LibraryStrings.clock: String(Duration time)
LibraryStrings.spoken: String(Duration time)
LibraryStrings.title: get String
LibraryStrings.back: get String
LibraryStrings.refresh: get String
LibraryStrings.loading: get String
LibraryStrings.tab: String(LibraryTab tab)
LibraryStrings.search: get String
LibraryStrings.searchHint: get String
LibraryStrings.clear: get String
LibraryStrings.nowPlaying: get String
LibraryStrings.paused: get String
LibraryStrings.openPlayer: String(LibraryNowPlaying now)
LibraryStrings.play: get String
LibraryStrings.pause: get String
LibraryStrings.buffering: get String
LibraryStrings.trackDetail: String(LibraryTrack track)
LibraryStrings.tracks: String(int count)
LibraryStrings.results: String(int count)
LibraryStrings.emptyTitle: String(LibraryTab tab)
LibraryStrings.emptyMessage: get String
LibraryStrings.noMatchTitle: String(String query)
LibraryStrings.noMatchMessage: get String
LibraryStrings.loadingMore: get String
LibraryStrings.moreFailed: get String
LibraryStrings.moreOffline: get String
LibraryStrings.end: get String
LibraryStrings.refreshFailed: get String
LibraryStrings.refreshOffline: get String
LibraryStrings.errorTitle: get String
LibraryStrings.errorMessage: get String
LibraryStrings.offlineTitle: get String
LibraryStrings.offlineMessage: get String
LibraryStrings.retry: get StringStrings
Every label in English, French and Arabic. The screen follows the closest Localizations; the same table is in the manifest, ready for your ARB files.
| Key | en | fr | ar |
|---|---|---|---|
libraryTitle | Library | Bibliothèque | المكتبة |
libraryBack | Back | Retour | رجوع |
libraryRefresh | Refresh the library | Actualiser la bibliothèque | تحديث المكتبة |
libraryLoading | Loading your library | Chargement de la bibliothèque | جارٍ تحميل المكتبة |
librarySongs | Songs | Morceaux | المقاطع |
libraryAlbums | Albums | Albums | الألبومات |
libraryPlaylists | Playlists | Playlists | قوائم التشغيل |
librarySearch | Search the library | Rechercher dans la bibliothèque | البحث في المكتبة |
librarySearchHint | Title or artist | Titre ou artiste | العنوان أو الفنان |
libraryClear | Clear search | Effacer la recherche | مسح البحث |
libraryNowPlaying | Now playing | En cours de lecture | قيد التشغيل |
libraryPaused | Paused | En pause | متوقف مؤقتًا |
libraryOpenPlayer | Now playing: {title}, {artist} | Lecture en cours : {title}, {artist} | قيد التشغيل: {title}، {artist} |
libraryOpenPlayerPaused | Paused: {title}, {artist} | En pause : {title}, {artist} | متوقف مؤقتًا: {title}، {artist} |
libraryPlay | Play | Lire | تشغيل |
libraryPause | Pause | Mettre en pause | إيقاف مؤقت |
libraryBuffering | Buffering… | Mise en mémoire tampon… | جارٍ التخزين المؤقت… |
libraryTrackDetail | {artist} · {album} | {artist} · {album} | {artist} · {album} |
libraryZero | 0 seconds | 0 seconde | ٠ ثانية |
libraryHoursOne | 1 hour | 1 heure | ساعة واحدة |
libraryHoursTwo | 2 hours | 2 heures | ساعتان |
libraryHours | {count} hours | {count} heures | {count} ساعات |
libraryHoursMany | {count} hours | {count} heures | {count} ساعة |
libraryHoursOther | {count} hours | {count} heures | {count} ساعة |
libraryMinutesOne | 1 minute | 1 minute | دقيقة واحدة |
libraryMinutesTwo | 2 minutes | 2 minutes | دقيقتان |
libraryMinutes | {count} minutes | {count} minutes | {count} دقائق |
libraryMinutesMany | {count} minutes | {count} minutes | {count} دقيقة |
libraryMinutesOther | {count} minutes | {count} minutes | {count} دقيقة |
librarySecondsOne | 1 second | 1 seconde | ثانية واحدة |
librarySecondsTwo | 2 seconds | 2 secondes | ثانيتان |
librarySeconds | {count} seconds | {count} secondes | {count} ثوانٍ |
librarySecondsMany | {count} seconds | {count} secondes | {count} ثانية |
librarySecondsOther | {count} seconds | {count} secondes | {count} ثانية |
libraryTracksZero | No songs | Aucun morceau | لا توجد مقاطع |
libraryTracksOne | 1 song | 1 morceau | مقطع واحد |
libraryTracksTwo | 2 songs | 2 morceaux | مقطعان |
libraryTracks | {count} songs | {count} morceaux | {count} مقاطع |
libraryTracksMany | {count} songs | {count} morceaux | {count} مقطعًا |
libraryTracksOther | {count} songs | {count} morceaux | {count} مقطع |
libraryResultsZero | No results found | Aucun résultat trouvé | لم يُعثر على أي نتيجة |
libraryResultsOne | 1 result | 1 résultat | نتيجة واحدة |
libraryResultsTwo | 2 results | 2 résultats | نتيجتان |
libraryResults | {count} results | {count} résultats | {count} نتائج |
libraryResultsMany | {count} results | {count} résultats | {count} نتيجة |
libraryResultsOther | {count} results | {count} résultats | {count} نتيجة |
libraryEmptySongs | No songs yet | Aucun morceau pour l’instant | لا توجد مقاطع بعد |
libraryEmptyAlbums | No albums yet | Aucun album pour l’instant | لا توجد ألبومات بعد |
libraryEmptyPlaylists | No playlists yet | Aucune playlist pour l’instant | لا توجد قوائم تشغيل بعد |
libraryEmptyMessage | What you save will show here. | Ce que vous enregistrez s’affichera ici. | سيظهر هنا ما تحفظه. |
libraryNoMatchTitle | Nothing matches “{query}” | Rien ne correspond à « {query} » | لا شيء يطابق «{query}» |
libraryNoMatchMessage | Search by title or by artist. | Cherchez par titre ou par artiste. | ابحث بالعنوان أو باسم الفنان. |
libraryLoadingMore | Loading more | Chargement de la suite | جارٍ تحميل المزيد |
libraryMoreFailed | More could not load. | La suite n’a pas pu se charger. | تعذّر تحميل المزيد. |
libraryMoreOffline | You are offline. Connect to see more. | Vous êtes hors ligne. Connectez-vous pour voir la suite. | أنت غير متصل. اتصل لعرض المزيد. |
libraryEnd | That is everything. | Vous avez tout vu. | هذا كل شيء. |
libraryRefreshFailed | Your library could not refresh. | La bibliothèque n’a pas pu se mettre à jour. | تعذّر تحديث المكتبة. |
libraryRefreshOffline | You are offline. Your library may be out of date. | Vous êtes hors ligne. La bibliothèque n’est peut-être pas à jour. | أنت غير متصل. قد لا تكون المكتبة محدّثة. |
libraryErrorTitle | Your library could not load | Impossible de charger la bibliothèque | تعذّر تحميل المكتبة |
libraryErrorMessage | Try again in a moment. | Réessayez dans un instant. | أعد المحاولة بعد قليل. |
libraryOfflineTitle | You are offline | Vous êtes hors ligne | أنت غير متصل |
libraryOfflineMessage | Connect to see your library. | Connectez-vous pour voir votre bibliothèque. | اتصل لعرض مكتبتك. |
libraryRetry | Try again | Réessayer | أعد المحاولة |
Reference renders
The 16 images published with version 0.2.5, rendered by the shared harness on macOS with Flutter 3.47.7. Each shows every state side by side, at the size of an iPhone (390 × 844) or of an Android phone (412 × 915). fliin verify library compares your copy to them.
















Use it to
- show a listener's music library: songs, albums and playlists
- pick a song by its title or its artist
- show albums as a grid of artwork
- open an album or a playlist from the music library
- make more songs appear while scrolling the library
- Not for
- the player currently playing: use the music player (player)
- a search in a product catalog: use the search screen (search)
- States