mirror of
https://github.com/IllusionMan1212/lyrics-grabbr.git
synced 2026-10-24 02:12:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c834c7eed
|
||
|
|
a770033563
|
||
|
|
1417eef23c
|
||
|
|
cd39204f6a
|
||
|
|
edc1f3a88b
|
||
|
|
0d96d906a4
|
||
|
|
55025d6d11 | ||
|
|
3935faffc7
|
||
|
|
20c2862ea5
|
||
|
|
99b1035638
|
||
|
|
e69b5e6848
|
||
|
|
42077e17ee | ||
|
|
55d26abaae | ||
|
|
197be6330b | ||
|
|
100157a8d4 | ||
|
|
3bf7750e51 |
@@ -0,0 +1,3 @@
|
||||
[submodule "3rdparty/flutter"]
|
||||
path = 3rdparty/flutter
|
||||
url = https://github.com/flutter/flutter
|
||||
+1
Submodule 3rdparty/flutter added at f468f3366c
@@ -1,3 +1,8 @@
|
||||
# lyricsgrabbr
|
||||
App that listens to your notifications and provides lyrics for the currently playing song.
|
||||
|
||||
App to listen to Spotify notifications and provide lyrics for currently playing song.
|
||||
## Supported players
|
||||
Any player that uses the MediaSession API _should_ be supported
|
||||
|
||||
## License
|
||||
The project is licensed under the GPL3 license while the assets (icon) are licensed under the creative commons license.
|
||||
|
||||
@@ -21,6 +21,12 @@ if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file("key.properties")
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
@@ -42,17 +48,27 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.illusionware.lyricsgrabbr"
|
||||
applicationId "com.illusionman1212.lyricsgrabbr"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
@@ -66,3 +82,14 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
|
||||
}
|
||||
|
||||
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
|
||||
import com.android.build.OutputFile
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||
if (abiVersionCode != null) {
|
||||
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.illusionware.lyricsgrabbr">
|
||||
package="com.illusionman1212.lyricsgrabbr">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.illusionware.lyricsgrabbr">
|
||||
package="com.illusionman1212.lyricsgrabbr">
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.illusionware.lyricsgrabbr
|
||||
package com.illusionman1212.lyricsgrabbr
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
|
||||
+12
-13
@@ -1,4 +1,4 @@
|
||||
package com.illusionware.lyricsgrabbr
|
||||
package com.illusionman1212.lyricsgrabbr
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
@@ -56,21 +56,19 @@ class NotificationListener : NotificationListenerService() {
|
||||
}
|
||||
|
||||
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
||||
if (sbn.packageName == SPOTIFY_PACKAGE) {
|
||||
sendNotificationBroadcast(sbn, applicationContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStopServiceNotificationAction(): PendingIntent {
|
||||
val intent = Intent(MainActivity.ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
|
||||
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
return pendingIntent
|
||||
}
|
||||
|
||||
private fun startForegroundService() {
|
||||
val notification: Notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle("Service Running")
|
||||
.setContentText("Play some songs on spotify to get lyrics")
|
||||
.setContentText("Play some songs on a supported player to get lyrics")
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setShowWhen(false)
|
||||
.addAction(R.drawable.ic_notification, "Stop Service", getStopServiceNotificationAction())
|
||||
@@ -103,19 +101,21 @@ class NotificationListener : NotificationListenerService() {
|
||||
private fun getCurrentNotifications(context: Context) {
|
||||
val sbns = activeNotifications
|
||||
for (sbn in sbns) {
|
||||
if (sbn.packageName == SPOTIFY_PACKAGE) {
|
||||
sendNotificationBroadcast(sbn, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendNotificationBroadcast(sbn: StatusBarNotification, context: Context) {
|
||||
// get the media session stuff
|
||||
val mediaSessionToken = sbn.notification.extras.getParcelable<MediaSession.Token>(Notification.EXTRA_MEDIA_SESSION)
|
||||
val mediaCtrl = mediaSessionToken?.let { MediaController(context, it) }
|
||||
val duration = mediaCtrl?.metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.div(1000)
|
||||
val playbackState = mediaCtrl?.playbackState?.state
|
||||
val albumArt = mediaCtrl?.metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
|
||||
if (mediaSessionToken == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val mediaCtrl = mediaSessionToken.let { MediaController(context, it) }
|
||||
val duration = mediaCtrl.metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.div(1000)
|
||||
val playbackState = mediaCtrl.playbackState?.state
|
||||
val albumArt = mediaCtrl.metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
|
||||
|
||||
// Retrieve package name to set as title.
|
||||
val packageName = sbn.packageName
|
||||
@@ -134,7 +134,7 @@ class NotificationListener : NotificationListenerService() {
|
||||
if (playbackState == PlaybackState.STATE_PAUSED) {
|
||||
val notification: Notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle("Service Running")
|
||||
.setContentText("Play some songs on spotify to get lyrics")
|
||||
.setContentText("Play some songs on a supported player to get lyrics")
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setShowWhen(false)
|
||||
.addAction(R.drawable.ic_notification, "Stop Service", getStopServiceNotificationAction())
|
||||
@@ -173,7 +173,6 @@ class NotificationListener : NotificationListenerService() {
|
||||
const val NOTIFICATION_PACKAGE_DURATION = "package_duration"
|
||||
const val NOTIFICATION_ID = 12251999
|
||||
const val NOTIFICATION_CHANNEL_ID = "Lyrics"
|
||||
const val SPOTIFY_PACKAGE = "com.spotify.music"
|
||||
const val START_FOREGROUND_SERVICE_ACTION = "START_SERVICE"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.illusionware.lyricsgrabbr
|
||||
package com.illusionman1212.lyricsgrabbr
|
||||
|
||||
import io.flutter.plugin.common.EventChannel.StreamHandler
|
||||
import io.flutter.plugin.common.EventChannel.EventSink
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.illusionware.lyricsgrabbr">
|
||||
package="com.illusionman1212.lyricsgrabbr">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
ext.kotlin_version = '1.8.22'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
## F-droid release
|
||||
Initial release on f-droid
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 196 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 427 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 724 KiB |
@@ -0,0 +1 @@
|
||||
Lyrics grabbr provides lyrics for the currently playing song
|
||||
@@ -0,0 +1 @@
|
||||
Lyrics Grabbr
|
||||
+36
-18
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert' as convert;
|
||||
import 'package:web_scraper/web_scraper.dart';
|
||||
|
||||
class LyricsPage extends StatefulWidget {
|
||||
const LyricsPage({
|
||||
@@ -21,6 +20,7 @@ class LyricsPage extends StatefulWidget {
|
||||
class _LyricsPageState extends State<LyricsPage> {
|
||||
bool fetching = false;
|
||||
String? lyrics;
|
||||
bool instrumental = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -31,21 +31,23 @@ class _LyricsPageState extends State<LyricsPage> {
|
||||
Future<void> initPlatformState() async {
|
||||
setState(() => fetching = true);
|
||||
|
||||
final uri = Uri.https('api.illusionman1212.tech', '/lyrics', {
|
||||
'songUrl': widget.url,
|
||||
});
|
||||
|
||||
var response = await http.get(uri);
|
||||
if (response.statusCode == 200) {
|
||||
final jsonRes = convert.jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final webScraper = WebScraper('https://genius.com');
|
||||
|
||||
String path = widget.url.split('genius.com/')[1];
|
||||
String fullLyrics = '';
|
||||
if (await webScraper.loadWebPage('/$path?react=1')) {
|
||||
webScraper
|
||||
.loadFromString(webScraper.getPageContent().replaceAll('<br>', '\n'));
|
||||
var element = webScraper
|
||||
.getElement("div[data-lyrics-container]", ['data-lyrics-container']);
|
||||
for (var section in element) {
|
||||
fullLyrics += section['title'] + '\n';
|
||||
}
|
||||
setState(() {
|
||||
lyrics = fullLyrics;
|
||||
instrumental = fullLyrics.isEmpty;
|
||||
fetching = false;
|
||||
lyrics = jsonRes['lyrics'];
|
||||
});
|
||||
} else {
|
||||
print('error while GETing lyrics');
|
||||
setState(() => fetching = false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,24 +75,40 @@ class _LyricsPageState extends State<LyricsPage> {
|
||||
),
|
||||
body: Center(
|
||||
child: fetching
|
||||
? Column(
|
||||
? const Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: const [CircularProgressIndicator(value: null)],
|
||||
children: [CircularProgressIndicator(value: null)],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: instrumental
|
||||
? const Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.music_note, size: 50),
|
||||
SizedBox(width: 20, height: 20),
|
||||
Text(
|
||||
'This Song is an Instrumental',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 30, vertical: 30),
|
||||
child: Text(
|
||||
padding: const EdgeInsets.all(30),
|
||||
child: SelectableText(
|
||||
lyrics ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
|
||||
+88
-31
@@ -1,28 +1,61 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'notification_listener.dart';
|
||||
import 'search_result.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert' as convert;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'settings.dart';
|
||||
import 'theme_provider.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
runApp(const LyricsGrabbr());
|
||||
}
|
||||
|
||||
class LyricsGrabbr extends StatelessWidget {
|
||||
class LyricsGrabbr extends StatefulWidget {
|
||||
const LyricsGrabbr({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LyricsGrabbr> createState() => _LyricsGrabbrState();
|
||||
}
|
||||
|
||||
class _LyricsGrabbrState extends State<LyricsGrabbr> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<SharedPreferences>(
|
||||
future: SharedPreferences.getInstance(),
|
||||
builder: (_, snapshot) {
|
||||
final prefTheme = snapshot.data?.getString("theme");
|
||||
|
||||
var themeMode = ThemeMode.system;
|
||||
if (prefTheme == AppTheme.Dark.name) {
|
||||
themeMode = ThemeMode.dark;
|
||||
} else if (prefTheme == AppTheme.Light.name) {
|
||||
themeMode = ThemeMode.light;
|
||||
} else if (prefTheme == AppTheme.System.name) {
|
||||
themeMode = ThemeMode.system;
|
||||
}
|
||||
|
||||
return snapshot.hasData ? ChangeNotifierProvider(
|
||||
create: (context) => ThemeProvider(themeMode),
|
||||
builder: (context, _) {
|
||||
final themeProvider = Provider.of<ThemeProvider>(context);
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Lyrics Grabbr',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
),
|
||||
theme: MyThemes.lightTheme,
|
||||
darkTheme: MyThemes.darkTheme,
|
||||
themeMode: themeProvider.themeMode,
|
||||
home: const HomePage(title: 'Lyrics Grabbr'),
|
||||
);
|
||||
}
|
||||
) : Container();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@@ -44,14 +77,14 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance?.addObserver(this);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
super.initState();
|
||||
initPlatformState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance?.removeObserver(this);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -90,7 +123,6 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
),
|
||||
content: const Text(
|
||||
'This app requires permission to listen to notifications to function properly. Please grant permission in the next screen. BEWARE that the only way to stop the service is to revoke the notification listening permission.',
|
||||
style: TextStyle(color: Colors.blueGrey),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
@@ -148,7 +180,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
|
||||
// TODO: build an httpclient to reuse between requests.
|
||||
final uri = Uri.https(
|
||||
'api.illusionman1212.tech',
|
||||
'api.illusionman1212.com',
|
||||
'/lyrics/search',
|
||||
{'q': '$song $artist'},
|
||||
);
|
||||
@@ -178,35 +210,28 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
Widget _buildResults() {
|
||||
return ListView.builder(
|
||||
return ListView.separated(
|
||||
itemBuilder: (_, i) {
|
||||
return searchResults[i];
|
||||
},
|
||||
itemCount: searchResults.length,
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider(height: 1);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (running)
|
||||
List<Widget> _buildPage() {
|
||||
if (lastNotification != null) {
|
||||
return [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: lastNotification?.songTitle,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.black,
|
||||
color: Theme.of(context).textTheme.bodyLarge?.color,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
children: [
|
||||
@@ -215,9 +240,9 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
),
|
||||
TextSpan(
|
||||
text: lastNotification?.artist,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: Color(0xFF303030),
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
)
|
||||
]),
|
||||
@@ -230,9 +255,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: const [
|
||||
CircularProgressIndicator(value: null)
|
||||
]))
|
||||
children: const [CircularProgressIndicator(value: null)]))
|
||||
: Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -244,9 +267,9 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
running
|
||||
? 'Found ${searchResults.length} result(s)'
|
||||
: "",
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: Colors.black87,
|
||||
color: Theme.of(context).textTheme.bodyText1?.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
@@ -257,8 +280,42 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text("No supported player is running",
|
||||
style: TextStyle(fontSize: 24)),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
tooltip: "Settings",
|
||||
splashRadius: 20,
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => const SettingsPage()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: _buildPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
+62
-63
@@ -3,7 +3,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'lyrics.dart';
|
||||
|
||||
class SearchResult extends StatefulWidget {
|
||||
class SearchResult extends StatelessWidget {
|
||||
const SearchResult(
|
||||
{Key? key,
|
||||
required this.title,
|
||||
@@ -16,79 +16,42 @@ class SearchResult extends StatefulWidget {
|
||||
|
||||
final String title;
|
||||
final String artist;
|
||||
final String url;
|
||||
final String artistUrl;
|
||||
final Uri url;
|
||||
final Uri artistUrl;
|
||||
final String thumbnail;
|
||||
final int id;
|
||||
|
||||
SearchResult.fromJson(Map<String, dynamic> json, {Key? key})
|
||||
: title = json['meta']['title'],
|
||||
artist = json['meta']['primaryArtist']['name'],
|
||||
url = json['url'],
|
||||
artistUrl = json['meta']['primaryArtist']['url'],
|
||||
url = Uri.parse(json['url']),
|
||||
artistUrl = Uri.parse(json['meta']['primaryArtist']['url']),
|
||||
thumbnail = json['resources']['thumbnail'],
|
||||
id = json['id'],
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SearchResultState();
|
||||
}
|
||||
|
||||
class OptionItem extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final void Function()? tapFunction;
|
||||
|
||||
const OptionItem(
|
||||
{Key? key, required this.title, required this.icon, this.tapFunction})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: tapFunction,
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 15),
|
||||
child: Icon(icon),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SearchResultState extends State<SearchResult> {
|
||||
List<Widget> _buildOptions() {
|
||||
return <OptionItem>[
|
||||
OptionItem(
|
||||
title: "Open Artist Page in Genius",
|
||||
icon: Icons.library_music,
|
||||
tapFunction: () async {
|
||||
if (!await launch(widget.artistUrl)) {
|
||||
throw 'could not launch ${widget.artistUrl}';
|
||||
onTap: () async {
|
||||
if (!await launchUrl(artistUrl)) {
|
||||
throw 'could not launch $artistUrl';
|
||||
}
|
||||
}),
|
||||
OptionItem(
|
||||
title: "Open Song Page in Genius",
|
||||
icon: Icons.music_note,
|
||||
tapFunction: () async {
|
||||
if (!await launch(widget.url)) {
|
||||
throw 'could not launch ${widget.url}';
|
||||
onTap: () async {
|
||||
if (!await launchUrl(url)) {
|
||||
throw 'could not launch $url';
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
void _showOptionsBottomSheet() {
|
||||
void _showOptionsBottomSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
@@ -108,33 +71,38 @@ class _SearchResultState extends State<SearchResult> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 0.5),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LyricsPage(
|
||||
title: widget.title,
|
||||
artist: widget.artist,
|
||||
url: widget.url,
|
||||
title: title,
|
||||
artist: artist,
|
||||
url: url.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onLongPress: () => _showOptionsBottomSheet(),
|
||||
onLongPress: () => _showOptionsBottomSheet(context),
|
||||
child: Ink(
|
||||
height: 75,
|
||||
color: Colors.black,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 20),
|
||||
child: Image.network(
|
||||
widget.thumbnail,
|
||||
thumbnail,
|
||||
width: 75,
|
||||
height: 75,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 75,
|
||||
height: 75,
|
||||
child: Icon(Icons.broken_image, size: 40)),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
@@ -143,18 +111,18 @@ class _SearchResultState extends State<SearchResult> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.bodyLarge?.color,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
widget.artist,
|
||||
style: const TextStyle(
|
||||
color: Colors.white70,
|
||||
artist,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.titleMedium?.color,
|
||||
fontSize: 14,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -166,6 +134,37 @@ class _SearchResultState extends State<SearchResult> {
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OptionItem extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final void Function()? onTap;
|
||||
|
||||
const OptionItem(
|
||||
{Key? key, required this.title, required this.icon, this.onTap})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 15),
|
||||
child: Icon(icon),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'theme_provider.dart';
|
||||
|
||||
enum AppTheme {
|
||||
Dark,
|
||||
Light,
|
||||
System
|
||||
}
|
||||
|
||||
class Setting extends StatelessWidget {
|
||||
final String title;
|
||||
final String? desc;
|
||||
final void Function() onTap;
|
||||
|
||||
const Setting({
|
||||
Key? key,
|
||||
required this.title,
|
||||
this.desc,
|
||||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Ink(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: const TextStyle(fontSize: 16)),
|
||||
if (desc != null)
|
||||
Text(desc!,
|
||||
style: const TextStyle(color: Colors.grey)),
|
||||
]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsPage extends StatefulWidget {
|
||||
const SettingsPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SettingsPage> createState() => _SettingsPageState();
|
||||
}
|
||||
|
||||
class _SettingsPageState extends State<SettingsPage> {
|
||||
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||
late SharedPreferences prefs;
|
||||
String? activeTheme;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initPlatformState();
|
||||
}
|
||||
|
||||
Future<void> initPlatformState() async {
|
||||
prefs = await _prefs;
|
||||
|
||||
setState(() {
|
||||
activeTheme = prefs.getString("theme") ?? AppTheme.System.name;
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSettingsGroup(String groupTitle, List<Setting> items) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(groupTitle,
|
||||
style: const TextStyle(color: Colors.deepPurple)),
|
||||
),
|
||||
...items,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildSettingsList(BuildContext context) {
|
||||
return [
|
||||
_buildSettingsGroup("Customization", [
|
||||
Setting(
|
||||
title: "Theme",
|
||||
desc: activeTheme,
|
||||
onTap: () {
|
||||
final provider = Provider.of<ThemeProvider>(context, listen: false);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext buildCtx) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(2)),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Text("Theme",
|
||||
style: Theme.of(context).textTheme.headline6),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Column(
|
||||
children: [
|
||||
RadioListTile(
|
||||
value: AppTheme.System.name,
|
||||
groupValue: activeTheme,
|
||||
title: Text("Use System Default", style: Theme.of(context).textTheme.bodyText1),
|
||||
onChanged: (String? value) => setState(() {
|
||||
provider.changeTheme(ThemeMode.system);
|
||||
activeTheme = AppTheme.System.name;
|
||||
prefs.setString("theme", AppTheme.System.name);
|
||||
Navigator.pop(context);
|
||||
})),
|
||||
RadioListTile(
|
||||
value: AppTheme.Dark.name,
|
||||
groupValue: activeTheme,
|
||||
title: Text("Dark", style: Theme.of(context).textTheme.bodyText1),
|
||||
onChanged: (String? value) => setState(() {
|
||||
provider.changeTheme(ThemeMode.dark);
|
||||
activeTheme = AppTheme.Dark.name;
|
||||
prefs.setString("theme", AppTheme.Dark.name);
|
||||
Navigator.pop(context);
|
||||
})),
|
||||
RadioListTile(
|
||||
value: AppTheme.Light.name,
|
||||
groupValue: activeTheme,
|
||||
title: Text("Light", style: Theme.of(context).textTheme.bodyText1),
|
||||
onChanged: (String? value) => setState(() {
|
||||
provider.changeTheme(ThemeMode.light);
|
||||
activeTheme = AppTheme.Light.name;
|
||||
prefs.setString("theme", AppTheme.Light.name);
|
||||
Navigator.pop(context);
|
||||
})),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
})
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Settings"),
|
||||
),
|
||||
body: ListView.separated(
|
||||
itemBuilder: (_, i) {
|
||||
return _buildSettingsList(context)[i];
|
||||
},
|
||||
separatorBuilder: (_, __) {
|
||||
return const SizedBox(height: 15);
|
||||
},
|
||||
itemCount: 1,
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ThemeProvider extends ChangeNotifier {
|
||||
ThemeMode themeMode;
|
||||
|
||||
ThemeProvider(this.themeMode);
|
||||
|
||||
bool get isDarkMode => themeMode == ThemeMode.dark;
|
||||
|
||||
void changeTheme(ThemeMode mode) {
|
||||
themeMode = mode;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class MyThemes {
|
||||
static final darkTheme = ThemeData(
|
||||
primarySwatch: Colors.deepPurple,
|
||||
dialogTheme: const DialogTheme(backgroundColor: Color(0xFF202020)),
|
||||
unselectedWidgetColor: Colors.white,
|
||||
canvasColor: Colors.black,
|
||||
bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Color(0xFF202020)),
|
||||
dividerColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
textTheme: const TextTheme(
|
||||
bodyText1: TextStyle(color: Colors.white, fontSize: 16),
|
||||
bodyText2: TextStyle(color: Colors.white),
|
||||
subtitle1: TextStyle(color: Colors.grey),
|
||||
subtitle2: TextStyle(color: Colors.white),
|
||||
headline6: TextStyle(color: Colors.white),
|
||||
),
|
||||
);
|
||||
|
||||
static final lightTheme = ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
dividerColor: Colors.black,
|
||||
textTheme: const TextTheme(
|
||||
bodyText1: TextStyle(fontSize: 16),
|
||||
subtitle1: TextStyle(color: Colors.black54),
|
||||
),
|
||||
);
|
||||
}
|
||||
+228
-134
@@ -1,83 +1,78 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.8.2"
|
||||
version: "2.11.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: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
dbus:
|
||||
version: "1.17.1"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dbus
|
||||
url: "https://pub.dartlang.org"
|
||||
name: csslib
|
||||
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
version: "1.0.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
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.2"
|
||||
version: "6.1.4"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -87,30 +82,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
flutter_local_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.3.2"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
flutter_local_notifications_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "2.0.2"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -121,90 +96,190 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.4"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.4"
|
||||
version: "0.13.6"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "4.0.2"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.3"
|
||||
version: "0.6.7"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "2.1.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.11"
|
||||
version: "0.12.15"
|
||||
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.3"
|
||||
version: "0.2.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.9.1"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
petitparser:
|
||||
version: "1.8.3"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
name: path_provider_linux
|
||||
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
version: "2.1.11"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
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: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
process:
|
||||
version: "2.1.4"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.5"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
url: "https://pub.dartlang.org"
|
||||
name: shared_preferences_android
|
||||
sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
version: "2.2.0"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -214,135 +289,154 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
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.0"
|
||||
version: "1.2.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.0"
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.8"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timezone
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.5.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.20"
|
||||
version: "6.1.12"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.15"
|
||||
version: "6.0.36"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.15"
|
||||
version: "6.1.4"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.5"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.6"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.1.3"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.9"
|
||||
version: "2.0.18"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
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.1"
|
||||
version: "2.1.4"
|
||||
web_scraper:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: web_scraper
|
||||
sha256: "34d41de25c1aaeeca2cd91c1d062ee3126f59db6c84ec32da17d44d082ecc177"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.5"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
url: "https://pub.dartlang.org"
|
||||
sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0+1"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
version: "1.0.1"
|
||||
sdks:
|
||||
dart: ">=2.16.1 <3.0.0"
|
||||
flutter: ">=2.10.0"
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
name: lyricsgrabbr
|
||||
description: A new Flutter project.
|
||||
description: App that listens to your notifications and provides lyrics for the currently playing song.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
@@ -15,10 +15,10 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.1.1+1
|
||||
version: 0.3.5+4
|
||||
|
||||
environment:
|
||||
sdk: ">=2.16.1 <3.0.0"
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
@@ -31,8 +31,10 @@ dependencies:
|
||||
sdk: flutter
|
||||
|
||||
http: ^0.13.4
|
||||
flutter_local_notifications: ^9.3.2
|
||||
url_launcher: ^6.0.20
|
||||
web_scraper: ^0.1.4
|
||||
shared_preferences: ^2.0.15
|
||||
provider: ^6.0.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -43,7 +45,7 @@ dev_dependencies:
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^1.0.0
|
||||
flutter_lints: ^2.0.2
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
Reference in New Issue
Block a user