Merge pull request 'master' (#21) from master into WORK-LDE

Reviewed-on: #21
WORK-LDE
Emre KARTAL 2 years ago
commit 82bb2f4ecd

@ -1,25 +1,26 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://$PROJECT_DIR$/../../../../../src/flutter/bin/cache/dart-sdk/lib/web_gl" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/js_interop" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://$PROJECT_DIR$/../../../../src/flutter/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

@ -9,7 +9,7 @@
---
[Présentation](#présentation) | [Répartion](#répartition-du-gitlab) | [Fonctionnement](#fonctionnement) | [Deploiement](#deploiement) | [Remerciements](#remerciements) | [Wiki](https://codefirst.iut.uca.fr/git/justDEV/justMusic/wiki)
[Présentation](#présentation) | [Répartion](#répartition-du-gitlab) | [Fonctionnement](#fonctionnement) | [Deploiement](#deploiement) | [Remerciements](#remerciements) | [Wiki](https://codefirst.iut.uca.fr/git/justDEV/justMusic/wiki) | [Site officiel](https://justmusicapp.com)
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@ -1,10 +1,15 @@
import 'package:flutter/Material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:justmusic/components/play_button_component.dart';
import 'package:justmusic/values/constants.dart';
import 'package:text_scroll/text_scroll.dart';
import '../model/Music.dart';
class MusicListComponent extends StatelessWidget {
const MusicListComponent({Key? key}) : super(key: key);
final Music music;
const MusicListComponent({
Key? key,
required this.music,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -12,58 +17,82 @@ class MusicListComponent extends StatelessWidget {
padding: const EdgeInsets.only(bottom: 14),
child: Row(
children: [
const ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5)),
child: Image(
image: AssetImage("assets/images/exemple_cover.png"),
width: 60,
height: 60,
),
),
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (music.cover != null) {
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5)),
child: music.cover != null
? FadeInImage.assetNetwork(
height: 60,
width: 60,
fit: BoxFit.cover,
placeholder: "assets/images/loadingPlaceholder.gif",
image: music.cover!)
: Container(
height: 60,
width: 60,
color: Colors.grey,
),
);
} else {
return Image(
image: AssetImage("assets/images/exemple_cover.png"),
height: 60,
width: 60,
);
}
}),
const SizedBox(
width: 10,
),
Expanded(
flex: 10,
child: Wrap(
alignment: WrapAlignment.center,
direction: Axis.vertical,
runSpacing: 3,
spacing: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Wrap(
verticalDirection: VerticalDirection.up,
spacing: 5,
runSpacing: 8,
runAlignment: WrapAlignment.end,
alignment: WrapAlignment.end,
Row(
children: [
Text(
"A.C. Milan",
overflow: TextOverflow.ellipsis,
style: GoogleFonts.plusJakartaSans(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w700),
),
Flexible(
flex: 8,
child: ScrollConfiguration(
behavior:
ScrollBehavior().copyWith(scrollbars: false),
child: TextScroll(
music.title ?? "Unknown",
style: GoogleFonts.plusJakartaSans(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w700),
mode: TextScrollMode.endless,
pauseBetween: Duration(milliseconds: 2500),
velocity: Velocity(pixelsPerSecond: Offset(30, 0)),
intervalSpaces: 10,
),
)),
Icon(
Icons.explicit,
color: Colors.grey.withOpacity(0.7),
size: 17,
)
),
],
),
Text(
"Booba",
overflow: TextOverflow.ellipsis,
style: GoogleFonts.plusJakartaSans(
color: Colors.grey, fontWeight: FontWeight.w400),
)
ScrollConfiguration(
behavior: ScrollBehavior().copyWith(scrollbars: false),
child: Text(
music.artists.first.name ?? "Unknown",
overflow: TextOverflow.ellipsis,
style: GoogleFonts.plusJakartaSans(
color: Colors.grey, fontWeight: FontWeight.w400),
))
],
),
),
Spacer(),
PlayButtonComponent()
PlayButtonComponent(
urlPreview: music.previewUrl,
)
],
),
);

@ -2,10 +2,11 @@ import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/Material.dart';
import 'package:flutter_animated_play_button/flutter_animated_play_button.dart';
import 'package:ionicons/ionicons.dart';
import 'package:justmusic/values/constants.dart';
class PlayButtonComponent extends StatefulWidget {
const PlayButtonComponent({Key? key}) : super(key: key);
final String? urlPreview;
const PlayButtonComponent({Key? key, required this.urlPreview})
: super(key: key);
@override
State<PlayButtonComponent> createState() => _PlayButtonComponentState();
@ -37,11 +38,14 @@ class _PlayButtonComponentState extends State<PlayButtonComponent> {
return isPlaying
? GestureDetector(
onTap: switchStatePlaying,
child: Icon(
Ionicons.play_circle_outline,
color: Colors.grey.withOpacity(0.3),
size: 30,
),
child: Container(
width: 30,
height: 30,
child: Icon(
Ionicons.play_circle_outline,
color: Colors.grey.withOpacity(0.3),
size: 30,
)),
)
: GestureDetector(
onTap: switchStatePlaying,
@ -57,8 +61,9 @@ class _PlayButtonComponentState extends State<PlayButtonComponent> {
}
Future<void> playSong() async {
await player.play(UrlSource(
'https://p.scdn.co/mp3-preview/d38052978a79adced2187cd8b6497bb10bedc452?cid=eb2aab666a43490f82eef0bb064d363f'));
if (widget.urlPreview != null) {
await player.play(UrlSource(widget.urlPreview ?? ""));
}
}
Future<void> stopSong() async {

@ -8,6 +8,7 @@ import 'package:justmusic/screens/post_screen.dart';
import 'package:justmusic/screens/profile_screen.dart';
import 'package:justmusic/screens/registration_screen.dart';
import 'package:justmusic/screens/welcome_screen.dart';
import 'package:justmusic/view_model/MusicViewModel.dart';
import 'package:justmusic/view_model/UserViewModel.dart';
void main() {
@ -16,6 +17,8 @@ void main() {
class MyApp extends StatelessWidget {
static UserViewModel userViewModel = UserViewModel();
static MusicViewModel musicViewModel = MusicViewModel();
const MyApp({super.key});
// This widget is the root of your application.

@ -6,11 +6,13 @@ class Music {
String? _cover;
String? _previewUrl;
DateTime? _date;
double? _duration;
bool _explicit = false;
List<Artist> _artists;
// Constructor
Music(this._id, this._title, this._cover, this._previewUrl, this._date,
this._artists);
this._duration, this._explicit, this._artists);
//Getters and setters
String? get id => _id;
@ -39,6 +41,18 @@ class Music {
_date = value;
}
double? get duration => _duration;
set duration(double? value) {
_duration = value;
}
bool get explicit => _explicit;
set explicit(bool value) {
_explicit = value;
}
List<Artist> get artists => _artists;
set artists(List<Artist> value) {

@ -1,11 +1,14 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/Material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:justmusic/model/Music.dart';
import '../components/music_list_component.dart';
import '../values/constants.dart';
import '../main.dart';
class SearchSongScreen extends StatefulWidget {
const SearchSongScreen({Key? key}) : super(key: key);
@ -15,12 +18,50 @@ class SearchSongScreen extends StatefulWidget {
}
class _SearchSongScreenState extends State<SearchSongScreen> {
final ScrollController _scrollController = ScrollController();
final TextEditingController _textEditingController = TextEditingController();
Future<void> resetFullScreen() async {
await SystemChannels.platform.invokeMethod<void>(
'SystemChrome.restoreSystemUIOverlays',
);
}
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
Future<void> _scrollListener() async {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
filteredData.addAll(await MyApp.musicViewModel.getMusicsWithName(
_textEditingController.text,
limit: 10,
offset: filteredData.length));
setState(() {
filteredData = filteredData;
});
}
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {
setState(() {
//you can do anything here
});
}
if (_scrollController.offset <=
_scrollController.position.minScrollExtent &&
!_scrollController.position.outOfRange) {
setState(() {
Timer(Duration(milliseconds: 1), () => _scrollController.jumpTo(0));
});
}
}
List<Music> filteredData = [];
@override
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
@ -40,8 +81,7 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
child: Container(
color: bgAppBar.withOpacity(0.5),
height: screenHeight - 50,
padding: const EdgeInsets.only(
top: 10, left: defaultPadding, right: defaultPadding),
padding: const EdgeInsets.only(top: 10),
child: Column(
children: [
Align(
@ -56,17 +96,25 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
height: 10,
),
Padding(
padding: const EdgeInsets.only(bottom: 10),
padding:
const EdgeInsets.only(bottom: 10, left: 20, right: 20),
child: SizedBox(
height: 40,
child: TextFormField(
child: TextField(
controller: _textEditingController,
keyboardAppearance: Brightness.dark,
onEditingComplete: resetFullScreen,
validator: (value) {
if (value == null || value.isEmpty) {
return 'TODO';
onChanged: (value) async {
if (_textEditingController.text.isEmpty) {
} else if (value == " ") {
print("popular");
} else {
filteredData = await MyApp.musicViewModel
.getMusicsWithName(value);
setState(() {
filteredData = filteredData;
});
}
return null;
},
cursorColor: Colors.white,
keyboardType: TextInputType.text,
@ -100,31 +148,18 @@ class _SearchSongScreenState extends State<SearchSongScreen> {
),
),
),
Expanded(
Flexible(
child: ScrollConfiguration(
behavior: ScrollBehavior().copyWith(scrollbars: false),
child: SingleChildScrollView(
child: Column(
children: const [
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
MusicListComponent(),
],
),
),
behavior: ScrollBehavior().copyWith(scrollbars: true),
child: ListView.builder(
controller: _scrollController,
itemCount: filteredData.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: MusicListComponent(music: filteredData[index]),
);
}),
))
],
),

@ -24,7 +24,7 @@ class MusicViewModel {
final responseData = jsonDecode(response.body);
List<Artist> artists =
List<Artist>.from(responseData['artists'].map((artist) {
return Artist(artist['id'], artist['name'],'');
return Artist(artist['id'], artist['name'], '');
}));
return Music(
@ -33,6 +33,8 @@ class MusicViewModel {
responseData['album']['images'][0]['url'],
responseData['preview_url'],
DateTime.parse(responseData['album']['release_date']),
responseData['duration_ms'] / 1000,
responseData['explicit'],
artists);
} else {
throw Exception(
@ -46,7 +48,7 @@ class MusicViewModel {
List<dynamic> tracks = responseData['tracks']['items'];
for (var track in tracks) {
List<Artist> artists = List<Artist>.from(track['artists'].map((artist) {
return Artist(artist['id'], artist['name'],'');
return Artist(artist['id'], artist['name'], '');
}));
musics.add(Music(
@ -55,18 +57,23 @@ class MusicViewModel {
track['album']['images'][0]['url'],
track['preview_url'],
DateTime.now(),
track['duration_ms'] / 1000,
track['explicit'],
artists));
}
return musics;
}
Future<List<Music>> getMusicsWithName(String name, {int limit = 20, int offset = 0, String market = "FR"}) async {
Future<List<Music>> getMusicsWithName(String name,
{int limit = 20, int offset = 0, String market = "FR"}) async {
var accessToken = await _token.getAccessToken();
var response = await http
.get(Uri.parse('$API_URL/search?q=track%3A$name&type=track&market=fr&limit=$limit&offset=$offset'), headers: {
'Authorization': 'Bearer $accessToken',
});
var response = await http.get(
Uri.parse(
'$API_URL/search?q=track%3A$name&type=track&market=fr&limit=$limit&offset=$offset'),
headers: {
'Authorization': 'Bearer $accessToken',
});
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
@ -77,10 +84,12 @@ class MusicViewModel {
}
}
Future<List<Music>> getMusicsWithArtistName(String name, {int limit = 20, int offset = 0, String market = "FR"}) async {
Future<List<Music>> getMusicsWithArtistName(String name,
{int limit = 20, int offset = 0, String market = "FR"}) async {
var accessToken = await _token.getAccessToken();
var response = await http.get(
Uri.parse('$API_URL/search?q=artist%3A$name&type=track&market=fr&limit=$limit&offset=$offset'),
Uri.parse(
'$API_URL/search?q=artist%3A$name&type=track&market=fr&limit=$limit&offset=$offset'),
headers: {
'Authorization': 'Bearer $accessToken',
});
@ -93,4 +102,102 @@ class MusicViewModel {
'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}');
}
}
Future<Artist> getArtistWithName(String name, {String market = "FR"}) async {
var accessToken = await _token.getAccessToken();
var response = await http.get(
Uri.parse(
'$API_URL/search?q=artist%3A$name&type=artist&market=$market'),
headers: {
'Authorization': 'Bearer $accessToken',
});
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
List<Artist> artists =
List<Artist>.from(responseData['artists']['items'].map((artist) {
String image = '';
if (!artist['images'].isEmpty) {
image = artist['images'][0]['url'];
}
return Artist(artist['id'], artist['name'], image);
}));
for (Artist a in artists) {
if (a.name?.toLowerCase() == name.toLowerCase()) {
return a;
}
}
throw Exception('Artist not found : ${name}');
} else {
throw Exception(
'Error retrieving artist information : ${response.statusCode} ${response.reasonPhrase}');
}
}
Future<List<Artist>> getArtistsWithName(String name,
{int limit = 20, int offset = 0, String market = "FR"}) async {
var accessToken = await _token.getAccessToken();
var response = await http.get(
Uri.parse(
'$API_URL/search?q=artist%3A$name&type=artist&market=$market&limit=$limit&offset=$offset'),
headers: {
'Authorization': 'Bearer $accessToken',
});
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
List<Artist> artists =
List<Artist>.from(responseData['artists']['items'].map((artist) {
String image = '';
if (!artist['images'].isEmpty) {
image = artist['images'][0]['url'];
}
return Artist(artist['id'], artist['name'], image);
}));
return artists;
} else {
throw Exception(
'Error while retrieving artist : ${response.statusCode} ${response.reasonPhrase}');
}
}
Future<List<Music>> getTopMusicsWithArtistId(String id,
{String market = "FR"}) async {
var accessToken = await _token.getAccessToken();
var response = await http.get(
Uri.parse('$API_URL/artists/$id/top-tracks?market=$market'),
headers: {
'Authorization': 'Bearer $accessToken',
});
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
List<Music> musics = [];
List<dynamic> tracks = responseData['tracks'];
for (var track in tracks) {
List<Artist> artists = List<Artist>.from(track['artists'].map((artist) {
return Artist(artist['id'], artist['name'], '');
}));
musics.add(Music(
track['id'],
track['name'],
track['album']['images'][0]['url'],
track['preview_url'],
DateTime.now(),
track['duration_ms'] / 1000,
track['explicit'],
artists));
}
return musics;
} else {
throw Exception(
'Error while retrieving music : ${response.statusCode} ${response.reasonPhrase}');
}
}
}

@ -5,140 +5,160 @@ packages:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
url: "https://pub.dev"
source: hosted
version: "2.9.0"
version: "2.10.0"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
url: "https://pub.dartlang.org"
sha256: "61583554386721772f9309f509e17712865b38565a903c761f96b1115a979282"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
audioplayers_android:
dependency: transitive
description:
name: audioplayers_android
url: "https://pub.dartlang.org"
sha256: dbdc9b7f2aa2440314c638aa55aadd45c7705e8340d5eddf2e3fb8da32d4ae2c
url: "https://pub.dev"
source: hosted
version: "3.0.2"
audioplayers_darwin:
dependency: transitive
description:
name: audioplayers_darwin
url: "https://pub.dartlang.org"
sha256: "6aea96df1d12f7ad5a71d88c6d1b22a216211a9564219920124c16768e456e9d"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
url: "https://pub.dartlang.org"
sha256: "396b62ac62c92dd26c3bc5106583747f57a8b325ebd2b41e5576f840cfc61338"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
url: "https://pub.dartlang.org"
sha256: f7daaed4659143094151ecf6bacd927d29ab8acffba98c110c59f0b81ae51143
url: "https://pub.dev"
source: hosted
version: "5.0.1"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
url: "https://pub.dartlang.org"
sha256: ec84fd46eed1577148ed4113f5998a36a18da4fce7170c37ce3e21b631393339
url: "https://pub.dev"
source: hosted
version: "3.1.0"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
url: "https://pub.dartlang.org"
sha256: "1d3aaac98a192b8488167711ba1e67d8b96333e8d0572ede4e2912e5bbce69a3"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
auto_size_text:
dependency: "direct main"
description:
name: auto_size_text
url: "https://pub.dartlang.org"
sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
url: "https://pub.dev"
source: hosted
version: "1.2.1"
circular_reveal_animation:
dependency: "direct main"
description:
name: circular_reveal_animation
url: "https://pub.dartlang.org"
sha256: "198f5a1fa27384dcf950807e0ae07a0da857c04df6233f7468755ee9db102b0c"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
url: "https://pub.dev"
source: hosted
version: "3.0.2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
url: "https://pub.dev"
source: hosted
version: "1.0.5"
custom_draggable_widget:
dependency: "direct main"
description:
name: custom_draggable_widget
url: "https://pub.dartlang.org"
sha256: "15718003ebcebb84acdf0c625831a880d139a284d8de9d943ef0d0a669f10159"
url: "https://pub.dev"
source: hosted
version: "0.0.2"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
url: "https://pub.dev"
source: hosted
version: "2.0.2"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
flutter:
@ -150,28 +170,32 @@ packages:
dependency: "direct main"
description:
name: flutter_animated_play_button
url: "https://pub.dartlang.org"
sha256: dd955a450a4514e935e2f410afbd03b3bcf5f31b933a8f1334199caa3c6a81e2
url: "https://pub.dev"
source: hosted
version: "0.3.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
flutter_screenutil:
dependency: "direct main"
description:
name: flutter_screenutil
url: "https://pub.dartlang.org"
sha256: "0a122936b450324cbdfd51be0819cc6fcebb093eb65585e9cd92263f7a1a8a39"
url: "https://pub.dev"
source: hosted
version: "5.7.0"
flutter_signin_button:
dependency: "direct main"
description:
name: flutter_signin_button
url: "https://pub.dartlang.org"
sha256: a063ecc5d5308377e103c9c3a89084abf15fca4440636233af6a13abacd5dcae
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_test:
@ -188,154 +212,176 @@ packages:
dependency: transitive
description:
name: font_awesome_flutter
url: "https://pub.dartlang.org"
sha256: "1f93e5799f0e6c882819e8393a05c6ca5226010f289190f2242ec19f3f0fdba5"
url: "https://pub.dev"
source: hosted
version: "9.2.0"
google_fonts:
dependency: "direct main"
description:
name: google_fonts
url: "https://pub.dartlang.org"
sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
gradiantbutton:
dependency: "direct main"
description:
name: gradiantbutton
url: "https://pub.dartlang.org"
sha256: c88ac8567242630cd14231e2a6a861da4e40a02a9a0310af360e05634890d172
url: "https://pub.dev"
source: hosted
version: "0.0.1"
gradient_borders:
dependency: "direct main"
description:
name: gradient_borders
url: "https://pub.dartlang.org"
sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
ionicons:
dependency: "direct main"
description:
name: ionicons
url: "https://pub.dartlang.org"
sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
url: "https://pub.dev"
source: hosted
version: "0.6.4"
version: "0.6.5"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
url: "https://pub.dev"
source: hosted
version: "0.12.12"
version: "0.12.13"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.1.5"
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
modal_bottom_sheet:
dependency: "direct main"
description:
name: modal_bottom_sheet
url: "https://pub.dartlang.org"
sha256: ef533916a2c3089571c32bd34e410faca77a6849a3f28f748e0794525c5658a0
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
url: "https://pub.dev"
source: hosted
version: "1.8.2"
path_provider:
dependency: transitive
description:
name: path_provider
url: "https://pub.dartlang.org"
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
url: "https://pub.dev"
source: hosted
version: "2.0.27"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
url: "https://pub.dartlang.org"
sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
url: "https://pub.dev"
source: hosted
version: "2.1.11"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
url: "https://pub.dev"
source: hosted
version: "2.0.6"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
url: "https://pub.dev"
source: hosted
version: "2.1.7"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
sky_engine:
@ -347,112 +393,128 @@ packages:
dependency: "direct main"
description:
name: smooth_corner
url: "https://pub.dartlang.org"
sha256: "1e920cffd9644d6f51f9a99674652f8c00f2e9074b275f3edde0de1441ba78e9"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.9.0"
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.dartlang.org"
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
url: "https://pub.dev"
source: hosted
version: "0.4.12"
version: "0.4.16"
text_scroll:
dependency: "direct main"
description:
name: text_scroll
url: "https://pub.dartlang.org"
sha256: "7869d86a6fdd725dee56bdd150216a99f0372b82fbfcac319214dbd5f36e1908"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
top_snackbar_flutter:
dependency: "direct main"
description:
name: top_snackbar_flutter
url: "https://pub.dartlang.org"
sha256: "22d14664a13db6ac714934c3382bd8d4daa57fb888a672f922df71981c5a5cb2"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "3.0.7"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
url: "https://pub.dev"
source: hosted
version: "4.1.4"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
url: "https://pub.dev"
source: hosted
version: "1.0.1"
zoom_tap_animation:
dependency: "direct main"
description:
name: zoom_tap_animation
url: "https://pub.dartlang.org"
sha256: d9f7a73cab65aa1546ba6886b5e21d3c8ccccb34e4e5f770301c306d4868bee0
url: "https://pub.dev"
source: hosted
version: "1.1.0"
sdks:

@ -79,6 +79,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
- assets/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware

@ -7,7 +7,7 @@ Future<void> main() async {
Music m = await musicVM.getMusic('295SxdR1DqunCNwd0U767w');
print("id : ${m.id.toString()}, cover : ${m.cover}, title : ${m.title}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}, duration : ${m.duration}, explicit : ${m.explicit}");
for (Artist a in m.artists) {
print("id : ${a.id}, name : ${a.name}");
}
@ -17,7 +17,7 @@ Future<void> main() async {
List<Music> musics = await musicVM.getMusicsWithName('Shavkat');
for (Music m in musics) {
print("id : ${m.id.toString()}, cover : ${m.cover}, title : ${m.title}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}, duration : ${m.duration}, explicit : ${m.explicit}");
for (Artist a in m.artists) {
print("id : ${a.id}, name : ${a.name}");
}
@ -28,9 +28,33 @@ Future<void> main() async {
List<Music> musics2 = await musicVM.getMusicsWithArtistName('jul');
for (Music m in musics2) {
print("id : ${m.id.toString()}, cover : ${m.cover}, title : ${m.title}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}, duration : ${m.duration}, explicit : ${m.explicit}");
for (Artist a in m.artists) {
print("id : ${a.id}, name : ${a.name}");
}
}
print('\nArtist with Name:');
Artist a = await musicVM.getArtistWithName('jul');
print("id : ${a.id}, name : ${a.name}, image : ${a.image}");
print('\nArtists with Name:');
List<Artist> artists = await musicVM.getArtistsWithName('jul');
for (Artist a in artists) {
print("id : ${a.id}, name : ${a.name}, image : ${a.image}");
}
print('\nTop Musics :');
List<Music> topMusics = await musicVM.getTopMusicsWithArtistId('3NH8t45zOTqzlZgBvZRjvB');
for (Music m in topMusics) {
print("id : ${m.id.toString()}, cover : ${m.cover}, title : ${m.title}");
print("date : ${m.date.toString()}, preview : ${m.previewUrl}, duration : ${m.duration}, explicit : ${m.explicit}");
for (Artist a in m.artists) {
print("id : ${a.id}, name : ${a.name}");
}
}
}

@ -11,7 +11,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:justmusic/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
/*testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
@ -26,5 +26,5 @@ void main() {
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
});*/
}

Loading…
Cancel
Save