diff --git a/lib/api.dart b/lib/api.dart index 51ebed4..84013d5 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -11,11 +11,15 @@ class MusicEntry { required this.artist, required this.title, }); + + String get coverCacheKey => "cover-$id"; } +typedef MusicsList = List; + class API { /// Get the list of music - static Future> getList() async { + static Future getList() async { final response = await Dio().get(config.apiURL + "/list", options: Options(headers: {"Token": config.apiToken})); @@ -30,3 +34,7 @@ class API { .cast(); } } + +extension MusicEntryAPIExt on MusicEntry { + String get coverURL => "${config.apiURL}/cover/$id"; +} diff --git a/lib/main.dart b/lib/main.dart index b5f2cb5..521a3b4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; +import 'package:music_web_player/ui/music_player.dart'; import 'package:music_web_player/api.dart'; import 'package:music_web_player/config.dart'; @@ -88,7 +89,6 @@ class _AppHomeState extends State { return const Center(child: Text("Musics list is empty!")); } - // TODO : go on - return Text(musics![0].artist); + return MusicPlayer(musicsList: musics!); } } diff --git a/lib/ui/cover_image.dart b/lib/ui/cover_image.dart new file mode 100644 index 0000000..259d63d --- /dev/null +++ b/lib/ui/cover_image.dart @@ -0,0 +1,35 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; +import 'package:flutter/material.dart'; +import 'package:music_web_player/api.dart'; +import 'package:music_web_player/config.dart'; + +class CoverImage extends StatelessWidget { + final MusicEntry music; + final double? width; + final double? height; + final BoxFit? fit; + + const CoverImage({ + Key? key, + required this.music, + this.width, + this.height, + this.fit, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return CachedNetworkImage( + width: width, + height: height, + imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, + imageUrl: music.coverURL, + cacheKey: music.coverCacheKey, + httpHeaders: {"Token": config.apiToken}, + useOldImageOnUrlChange: false, + placeholder: (c, s) => const Center(child: CircularProgressIndicator()), + fit: fit, + ); + } +} diff --git a/lib/ui/music_player.dart b/lib/ui/music_player.dart new file mode 100644 index 0000000..b5f554f --- /dev/null +++ b/lib/ui/music_player.dart @@ -0,0 +1,55 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:music_web_player/api.dart'; +import 'package:music_web_player/ui/cover_image.dart'; + +class MusicPlayer extends StatefulWidget { + final MusicsList musicsList; + + const MusicPlayer({Key? key, required this.musicsList}) : super(key: key); + + @override + State createState() => _MusicPlayerState(); +} + +class _MusicPlayerState extends State { + final rng = Random(); + + final List stack = []; + int currMusicPos = 0; + + MusicEntry get currMusic { + if (currMusicPos < 0) currMusicPos = 0; + + // Automatically choose next music if required + if (currMusicPos >= stack.length) { + var nextId = rng.nextInt(widget.musicsList.length); + stack.add(widget.musicsList[nextId]); + } + + return stack[currMusicPos]; + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) => Stack( + children: [ + // Background image + CoverImage( + music: currMusic, + width: constraints.maxWidth, + height: constraints.maxHeight, + fit: BoxFit.cover, + ), + _buildFront(), + ], + ), + ); + } + + Widget _buildFront() { + return Text(currMusic.artist + " - " + currMusic.title); + } +} diff --git a/pubspec.lock b/pubspec.lock index 32485c0..cc73361 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -15,6 +15,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + cached_network_image_platform_interface: + dependency: "direct main" + description: + name: cached_network_image_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" characters: dependency: transitive description: @@ -43,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" cupertino_icons: dependency: "direct main" description: @@ -64,6 +92,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" fluentui_system_icons: dependency: "direct main" description: @@ -76,6 +118,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" flutter_lints: dependency: "direct dev" description: @@ -88,6 +144,13 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" http_parser: dependency: transitive description: @@ -123,6 +186,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" path: dependency: transitive description: @@ -130,6 +200,90 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.3" sky_engine: dependency: transitive description: flutter @@ -142,6 +296,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.1" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" stack_trace: dependency: transitive description: @@ -163,6 +331,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" term_glyph: dependency: transitive description: @@ -184,6 +359,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" vector_math: dependency: transitive description: @@ -191,5 +373,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" sdks: dart: ">=2.16.1 <3.0.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml index 89ba51c..146e03a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,10 @@ dependencies: # Fluent icons fluentui_system_icons: ^1.1.162 + # Image processing + cached_network_image: ^3.2.0 + cached_network_image_platform_interface: ^1.0.0 + dev_dependencies: flutter_test: sdk: flutter