From 3351e5e5abfb4bdcd3d1dc00be7a5dd4947de0a8 Mon Sep 17 00:00:00 2001 From: gianlucaparadise Date: Fri, 22 Jul 2022 06:16:38 +0200 Subject: [PATCH] MediaQueue: QueueList in Example --- example/lib/main.dart | 12 +++++- example/lib/queue_route.dart | 51 +++++++++++++++++++++-- example/lib/widgets/QueueItemHeading.dart | 36 ++++++++++++++++ example/lib/widgets/QueueListItem.dart | 45 ++++++++++++++++++++ example/lib/widgets/Thumbnail.dart | 27 ++++++++++++ example/pubspec.lock | 7 ++++ example/pubspec.yaml | 1 + 7 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 example/lib/widgets/QueueItemHeading.dart create mode 100644 example/lib/widgets/QueueListItem.dart create mode 100644 example/lib/widgets/Thumbnail.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index b44037f..874a123 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -223,7 +223,17 @@ class _MyAppState extends State { padding: const EdgeInsets.all(8.0), child: ElevatedButton( child: Text("Append to Queue"), - onPressed: _hasSession ? _appendToQueue : null, + /** + * From the documentation: + * The Receiver SDK maintains the queue and responds to operations + * on the queue as long as the queue has at least one item + * currently active (playing or paused). + */ + onPressed: _hasSession && + ![PlayerState.idle, PlayerState.unknown] + .contains(_playerState) + ? _appendToQueue + : null, ), ), Padding( diff --git a/example/lib/queue_route.dart b/example/lib/queue_route.dart index 5832752..94e0c05 100644 --- a/example/lib/queue_route.dart +++ b/example/lib/queue_route.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_cast_framework/cast.dart'; +import 'package:flutter_cast_framework/widgets.dart'; +import 'package:flutter_cast_framework_example/widgets/QueueListItem.dart'; class QueueRoute extends StatelessWidget { final FlutterCastFramework castFramework; @@ -9,23 +11,66 @@ class QueueRoute extends StatelessWidget { required this.castFramework, }) : super(key: key); - Widget _getEmptyQueueMessage(BuildContext context) { + Widget _getEmptyQueueMessage(BuildContext context, String text) { return Container( alignment: Alignment.center, child: Text( - "Queue is empty!", + text, style: Theme.of(context).textTheme.headline6, ), ); } + Widget _getEmptyItemMessage(BuildContext context, String text) { + return Container( + alignment: Alignment.center, + child: Text( + text, + style: Theme.of(context).textTheme.bodyText1, + ), + ); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Queue'), ), - body: _getEmptyQueueMessage(context), + body: QueueList( + castFramework: castFramework, + listItemBuilder: (BuildContext context, MediaQueueItem item) { + return QueueListItem(item: item); + }, + emptyListStateBuilder: (context, isLoading, error) { + if (isLoading) { + return _getEmptyQueueMessage(context, "Loading..."); + } + + if (error != null) { + debugPrint( + "MediaQueue - error while retrieving items count $error", + ); + return _getEmptyQueueMessage(context, "An error occurred"); + } + + return _getEmptyQueueMessage(context, "Queue is empty!"); + }, + emptyItemStateBuilder: (context, isLoading, error) { + if (isLoading) { + return _getEmptyItemMessage(context, "Loading..."); + } + + if (error != null) { + debugPrint( + "MediaQueue - error while retrieving items count $error", + ); + return _getEmptyItemMessage(context, "An error occurred"); + } + + return _getEmptyItemMessage(context, "Item is empty!"); + }, + ), ); } } diff --git a/example/lib/widgets/QueueItemHeading.dart b/example/lib/widgets/QueueItemHeading.dart new file mode 100644 index 0000000..a86ea66 --- /dev/null +++ b/example/lib/widgets/QueueItemHeading.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_cast_framework/cast.dart'; + +class QueueItemHeading extends StatelessWidget { + final MediaInfo? mediaInfo; + + const QueueItemHeading({ + Key? key, + required this.mediaInfo, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + // final titleText = mediaInfo?.mediaMetadata?.strings[MediaMetadataKey.title] + // final subtitleText = mediaInfo?.mediaMetadata?.strings[MediaMetadataKey.subtitle] + final titleText = ""; + final subtitleText = ""; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + titleText, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + Container(height: 2), + Text( + subtitleText, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ) + ], + ); + } +} diff --git a/example/lib/widgets/QueueListItem.dart b/example/lib/widgets/QueueListItem.dart new file mode 100644 index 0000000..377eb6c --- /dev/null +++ b/example/lib/widgets/QueueListItem.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_cast_framework/cast.dart'; +import 'package:flutter_cast_framework_example/widgets/QueueItemHeading.dart'; +import 'package:flutter_cast_framework_example/widgets/Thumbnail.dart'; + +class QueueListItem extends StatelessWidget { + final MediaQueueItem item; + + const QueueListItem({ + Key? key, + required this.item, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final webImage = item.media?.mediaMetadata?.webImages?.first; + + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: Colors.grey, + ), + ), + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: Thumbnail(image: webImage), + ), + Container(width: 10), // this is a spacer + Expanded( + flex: 2, + child: QueueItemHeading( + mediaInfo: item.media, + ), + ), + // isCastConnected ? moreMenuButton : SizedBox.shrink(), + ], + ), + ); + } +} diff --git a/example/lib/widgets/Thumbnail.dart b/example/lib/widgets/Thumbnail.dart new file mode 100644 index 0000000..d4be0cb --- /dev/null +++ b/example/lib/widgets/Thumbnail.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_cast_framework/cast.dart'; +import 'package:transparent_image/transparent_image.dart'; + +class Thumbnail extends StatelessWidget { + final WebImage? image; + + const Thumbnail({ + Key? key, + required this.image, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final imageUrl = image?.url; + + return AspectRatio( + aspectRatio: 480.0 / 270.0, + child: imageUrl == null + ? SizedBox.shrink() + : FadeInImage.memoryNetwork( + placeholder: kTransparentImage, + image: imageUrl, + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index 4aecbca..a2ee27d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -170,6 +170,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.2" + transparent_image: + dependency: "direct main" + description: + name: transparent_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" typed_data: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 6503dd0..bb60efa 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 + transparent_image: ^2.0.0 dev_dependencies: flutter_test: