mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
Initial accent theme & background theme support
This commit is contained in:
parent
211affa79d
commit
94db62baa5
3
.vscode/c_cpp_properties.json
vendored
3
.vscode/c_cpp_properties.json
vendored
@ -14,7 +14,8 @@
|
||||
"compilerPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64\\cl.exe",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "windows-msvc-x64"
|
||||
"intelliSenseMode": "windows-msvc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
|
@ -12,6 +12,8 @@ import 'package:spotube/provider/AudioPlayer.dart';
|
||||
import 'package:spotube/provider/UserPreferences.dart';
|
||||
import 'package:spotube/provider/YouTube.dart';
|
||||
import 'package:just_audio_background/just_audio_background.dart';
|
||||
import 'package:spotube/themes/dark-theme.dart';
|
||||
import 'package:spotube/themes/light-theme.dart';
|
||||
|
||||
void main() async {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
@ -59,98 +61,13 @@ class MyApp extends HookConsumerWidget {
|
||||
routerDelegate: _router.routerDelegate,
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Spotube',
|
||||
theme: ThemeData(
|
||||
primaryColor: Colors.green,
|
||||
primarySwatch: Colors.green,
|
||||
buttonTheme: const ButtonThemeData(
|
||||
buttonColor: Colors.green,
|
||||
theme: lightTheme(
|
||||
accentMaterialColor: Colors.deepPurple,
|
||||
backgroundMaterialColor: Colors.grey,
|
||||
),
|
||||
shadowColor: Colors.grey[300],
|
||||
backgroundColor: Colors.white,
|
||||
textTheme: TextTheme(
|
||||
bodyText1: TextStyle(color: Colors.grey[850]),
|
||||
headline1: TextStyle(color: Colors.grey[850]),
|
||||
headline2: TextStyle(color: Colors.grey[850]),
|
||||
headline3: TextStyle(color: Colors.grey[850]),
|
||||
headline4: TextStyle(color: Colors.grey[850]),
|
||||
headline5: TextStyle(color: Colors.grey[850]),
|
||||
headline6: TextStyle(color: Colors.grey[850]),
|
||||
),
|
||||
listTileTheme: ListTileThemeData(
|
||||
iconColor: Colors.grey[850],
|
||||
horizontalTitleGap: 0,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.green[400]!,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey[800]!,
|
||||
),
|
||||
),
|
||||
),
|
||||
navigationRailTheme: NavigationRailThemeData(
|
||||
backgroundColor: Colors.blueGrey[50],
|
||||
unselectedIconTheme:
|
||||
IconThemeData(color: Colors.grey[850], opacity: 1),
|
||||
unselectedLabelTextStyle: TextStyle(
|
||||
color: Colors.grey[850],
|
||||
),
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: Colors.blueGrey[50],
|
||||
height: 55,
|
||||
),
|
||||
cardTheme: CardTheme(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: Colors.green,
|
||||
primarySwatch: Colors.green,
|
||||
backgroundColor: Colors.blueGrey[900],
|
||||
scaffoldBackgroundColor: Colors.blueGrey[900],
|
||||
dialogBackgroundColor: Colors.blueGrey[800],
|
||||
shadowColor: Colors.black26,
|
||||
popupMenuTheme: PopupMenuThemeData(color: Colors.blueGrey[800]),
|
||||
buttonTheme: const ButtonThemeData(
|
||||
buttonColor: Colors.green,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.green[400]!,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey[800]!,
|
||||
),
|
||||
),
|
||||
),
|
||||
navigationRailTheme: NavigationRailThemeData(
|
||||
backgroundColor: Colors.blueGrey[800],
|
||||
unselectedIconTheme: const IconThemeData(opacity: 1),
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: Colors.blueGrey[800],
|
||||
height: 55,
|
||||
),
|
||||
cardTheme: CardTheme(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
color: Colors.blueGrey[900],
|
||||
elevation: 20,
|
||||
),
|
||||
canvasColor: Colors.blueGrey[900],
|
||||
darkTheme: darkTheme(
|
||||
accentMaterialColor: Colors.purple,
|
||||
backgroundMaterialColor: Colors.grey,
|
||||
),
|
||||
themeMode: themeMode,
|
||||
);
|
||||
|
@ -68,8 +68,8 @@ class Auth extends PersistedChangeNotifier {
|
||||
|
||||
@override
|
||||
FutureOr<void> loadFromLocal(Map<String, dynamic> map) {
|
||||
_clientId = map["clientId"].isNotEmpty ? map["clientId"] : null;
|
||||
_clientSecret = map["clientSecret"].isNotEmpty ? map["clientSecret"] : null;
|
||||
_clientId = map["clientId"];
|
||||
_clientSecret = map["clientSecret"];
|
||||
_accessToken = map["accessToken"];
|
||||
_refreshToken = map["refreshToken"];
|
||||
_expiration = DateTime.tryParse(map["expiration"]);
|
||||
|
@ -1,24 +1,25 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotube/helpers/get-random-element.dart';
|
||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||
import 'package:spotube/models/Logger.dart';
|
||||
import 'package:spotube/models/generated_secrets.dart';
|
||||
import 'package:spotube/utils/PersistedChangeNotifier.dart';
|
||||
|
||||
class UserPreferences extends ChangeNotifier {
|
||||
class UserPreferences extends PersistedChangeNotifier {
|
||||
ThemeMode themeMode;
|
||||
String ytSearchFormat;
|
||||
String recommendationMarket;
|
||||
bool saveTrackLyrics;
|
||||
String geniusAccessToken;
|
||||
SharedPreferences? localStorage;
|
||||
HotKey? nextTrackHotKey;
|
||||
HotKey? prevTrackHotKey;
|
||||
HotKey? playPauseHotKey;
|
||||
|
||||
MaterialColor? accentColorScheme;
|
||||
MaterialColor? backgroundColorScheme;
|
||||
UserPreferences({
|
||||
required this.geniusAccessToken,
|
||||
required this.recommendationMarket,
|
||||
@ -28,135 +29,101 @@ class UserPreferences extends ChangeNotifier {
|
||||
this.nextTrackHotKey,
|
||||
this.prevTrackHotKey,
|
||||
this.playPauseHotKey,
|
||||
}) {
|
||||
onInit();
|
||||
}
|
||||
|
||||
final logger = getLogger(UserPreferences);
|
||||
|
||||
Future<HotKey?> _getHotKeyFromLocalStorage(String key) async {
|
||||
String? str = localStorage?.getString(key);
|
||||
if (str != null) {
|
||||
Map<String, dynamic> json = await jsonDecode(str);
|
||||
if (json.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return HotKey.fromJson(json);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> onInit() async {
|
||||
try {
|
||||
localStorage = await SharedPreferences.getInstance();
|
||||
String? accessToken =
|
||||
localStorage?.getString(LocalStorageKeys.geniusAccessToken);
|
||||
|
||||
saveTrackLyrics =
|
||||
localStorage?.getBool(LocalStorageKeys.saveTrackLyrics) ?? false;
|
||||
|
||||
final themeModeRaw = localStorage?.getString(LocalStorageKeys.themeMode);
|
||||
switch (themeModeRaw) {
|
||||
case "light":
|
||||
themeMode = ThemeMode.light;
|
||||
break;
|
||||
case "dark":
|
||||
themeMode = ThemeMode.dark;
|
||||
break;
|
||||
default:
|
||||
themeMode = ThemeMode.system;
|
||||
}
|
||||
|
||||
recommendationMarket =
|
||||
localStorage?.getString(LocalStorageKeys.recommendationMarket) ??
|
||||
'US';
|
||||
geniusAccessToken = accessToken != null && accessToken.isNotEmpty
|
||||
? accessToken
|
||||
: getRandomElement(lyricsSecrets);
|
||||
|
||||
nextTrackHotKey ??= (await _getHotKeyFromLocalStorage(
|
||||
LocalStorageKeys.nextTrackHotKey,
|
||||
)) ??
|
||||
HotKey(
|
||||
KeyCode.slash,
|
||||
modifiers: [KeyModifier.control, KeyModifier.alt],
|
||||
);
|
||||
|
||||
prevTrackHotKey ??= (await _getHotKeyFromLocalStorage(
|
||||
LocalStorageKeys.prevTrackHotKey,
|
||||
)) ??
|
||||
HotKey(
|
||||
KeyCode.comma,
|
||||
modifiers: [KeyModifier.control, KeyModifier.alt],
|
||||
);
|
||||
|
||||
playPauseHotKey ??= (await _getHotKeyFromLocalStorage(
|
||||
LocalStorageKeys.playPauseHotKey,
|
||||
)) ??
|
||||
HotKey(
|
||||
KeyCode.period,
|
||||
modifiers: [KeyModifier.control, KeyModifier.alt],
|
||||
);
|
||||
notifyListeners();
|
||||
} catch (e, stack) {
|
||||
logger.e("onInit", e, stack);
|
||||
}
|
||||
}
|
||||
this.accentColorScheme,
|
||||
this.backgroundColorScheme,
|
||||
}) : super();
|
||||
|
||||
void setThemeMode(ThemeMode mode) {
|
||||
themeMode = mode;
|
||||
localStorage?.setString(LocalStorageKeys.themeMode, mode.name);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setSaveTrackLyrics(bool shouldSave) {
|
||||
saveTrackLyrics = shouldSave;
|
||||
localStorage?.setBool(LocalStorageKeys.saveTrackLyrics, shouldSave);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setRecommendationMarket(String country) {
|
||||
recommendationMarket = country;
|
||||
localStorage?.setString(LocalStorageKeys.recommendationMarket, country);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setGeniusAccessToken(String token) {
|
||||
geniusAccessToken = token;
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setNextTrackHotKey(HotKey? value) {
|
||||
nextTrackHotKey = value;
|
||||
localStorage?.setString(
|
||||
LocalStorageKeys.nextTrackHotKey,
|
||||
jsonEncode(value?.toJson() ?? {}),
|
||||
);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setPrevTrackHotKey(HotKey? value) {
|
||||
prevTrackHotKey = value;
|
||||
localStorage?.setString(
|
||||
LocalStorageKeys.prevTrackHotKey,
|
||||
jsonEncode(value?.toJson() ?? {}),
|
||||
);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setPlayPauseHotKey(HotKey? value) {
|
||||
playPauseHotKey = value;
|
||||
localStorage?.setString(
|
||||
LocalStorageKeys.playPauseHotKey,
|
||||
jsonEncode(value?.toJson() ?? {}),
|
||||
);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setYtSearchFormat(String format) {
|
||||
ytSearchFormat = format;
|
||||
localStorage?.setString(LocalStorageKeys.ytSearchFormate, format);
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setAccentColorScheme(MaterialColor color) {
|
||||
accentColorScheme = color;
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
void setBackgroundColorScheme(MaterialColor color) {
|
||||
backgroundColorScheme = color;
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> loadFromLocal(Map<String, dynamic> map) {
|
||||
saveTrackLyrics = map["saveTrackLyrics"] ?? false;
|
||||
recommendationMarket = map["recommendationMarket"] ?? recommendationMarket;
|
||||
geniusAccessToken =
|
||||
map["geniusAccessToken"] ?? getRandomElement(lyricsSecrets);
|
||||
nextTrackHotKey = map["nextTrackHotKey"] != null
|
||||
? HotKey.fromJson(jsonDecode(map["nextTrackHotKey"]))
|
||||
: null;
|
||||
prevTrackHotKey = map["prevTrackHotKey"] != null
|
||||
? HotKey.fromJson(jsonDecode(map["prevTrackHotKey"]))
|
||||
: null;
|
||||
playPauseHotKey = map["playPauseHotKey"] != null
|
||||
? HotKey.fromJson(jsonDecode(map["playPauseHotKey"]))
|
||||
: null;
|
||||
ytSearchFormat = map["ytSearchFormat"] ?? ytSearchFormat;
|
||||
themeMode = ThemeMode.values[map["themeMode"] ?? 0];
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<Map<String, dynamic>> toMap() {
|
||||
return {
|
||||
"saveTrackLyrics": saveTrackLyrics,
|
||||
"recommendationMarket": recommendationMarket,
|
||||
"geniusAccessToken": geniusAccessToken,
|
||||
"nextTrackHotKey": jsonEncode(nextTrackHotKey?.toJson() ?? {}),
|
||||
"prevTrackHotKey": jsonEncode(prevTrackHotKey?.toJson() ?? {}),
|
||||
"playPauseHotKey": jsonEncode(playPauseHotKey?.toJson() ?? {}),
|
||||
"ytSearchFormat": ytSearchFormat,
|
||||
"themeMode": themeMode.index,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
62
lib/themes/dark-theme.dart
Normal file
62
lib/themes/dark-theme.dart
Normal file
@ -0,0 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ThemeData darkTheme({
|
||||
MaterialColor accentMaterialColor = Colors.green,
|
||||
MaterialColor backgroundMaterialColor = Colors.blueGrey,
|
||||
}) {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: accentMaterialColor,
|
||||
primarySwatch: accentMaterialColor,
|
||||
backgroundColor: backgroundMaterialColor[900],
|
||||
scaffoldBackgroundColor: backgroundMaterialColor[900],
|
||||
dialogBackgroundColor: backgroundMaterialColor[800],
|
||||
shadowColor: Colors.black26,
|
||||
popupMenuTheme: PopupMenuThemeData(color: backgroundMaterialColor[800]),
|
||||
buttonTheme: ButtonThemeData(
|
||||
buttonColor: accentMaterialColor,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: accentMaterialColor[400]!,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: backgroundMaterialColor[800]!,
|
||||
),
|
||||
),
|
||||
),
|
||||
navigationRailTheme: NavigationRailThemeData(
|
||||
backgroundColor: backgroundMaterialColor[800],
|
||||
unselectedIconTheme: IconThemeData(color: Colors.grey[300], opacity: 1),
|
||||
selectedIconTheme: IconThemeData(color: backgroundMaterialColor[850]),
|
||||
selectedLabelTextStyle: TextStyle(color: accentMaterialColor[300]),
|
||||
unselectedLabelTextStyle: TextStyle(color: Colors.grey[300]),
|
||||
indicatorColor: accentMaterialColor[300],
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: backgroundMaterialColor[800],
|
||||
height: 55,
|
||||
indicatorColor: accentMaterialColor[300],
|
||||
),
|
||||
cardTheme: CardTheme(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
color: backgroundMaterialColor[900],
|
||||
elevation: 20,
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
onPrimary: accentMaterialColor[300],
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
cardColor: backgroundMaterialColor[800],
|
||||
canvasColor: backgroundMaterialColor[900],
|
||||
);
|
||||
}
|
88
lib/themes/light-theme.dart
Normal file
88
lib/themes/light-theme.dart
Normal file
@ -0,0 +1,88 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final materialWhite = MaterialColor(Colors.white.value, {
|
||||
50: Colors.white,
|
||||
100: Colors.blueGrey[50]!,
|
||||
200: Colors.white,
|
||||
300: Colors.white,
|
||||
400: Colors.white,
|
||||
500: Colors.blueGrey,
|
||||
600: Colors.white,
|
||||
700: Colors.white,
|
||||
800: Colors.white,
|
||||
900: Colors.white,
|
||||
});
|
||||
|
||||
ThemeData lightTheme({
|
||||
MaterialColor accentMaterialColor = Colors.green,
|
||||
MaterialColor? backgroundMaterialColor,
|
||||
}) {
|
||||
backgroundMaterialColor ??= materialWhite;
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
primaryColor: accentMaterialColor,
|
||||
primarySwatch: accentMaterialColor,
|
||||
buttonTheme: ButtonThemeData(
|
||||
buttonColor: accentMaterialColor,
|
||||
),
|
||||
shadowColor: Colors.grey[300],
|
||||
backgroundColor: backgroundMaterialColor[50],
|
||||
textTheme: TextTheme(
|
||||
bodyText1: TextStyle(color: Colors.grey[850]),
|
||||
headline1: TextStyle(color: Colors.grey[850]),
|
||||
headline2: TextStyle(color: Colors.grey[850]),
|
||||
headline3: TextStyle(color: Colors.grey[850]),
|
||||
headline4: TextStyle(color: Colors.grey[850]),
|
||||
headline5: TextStyle(color: Colors.grey[850]),
|
||||
headline6: TextStyle(color: Colors.grey[850]),
|
||||
),
|
||||
listTileTheme: ListTileThemeData(
|
||||
iconColor: Colors.grey[850],
|
||||
horizontalTitleGap: 0,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: accentMaterialColor[400]!,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey[800]!,
|
||||
),
|
||||
),
|
||||
),
|
||||
navigationRailTheme: NavigationRailThemeData(
|
||||
backgroundColor: backgroundMaterialColor[100],
|
||||
indicatorColor: accentMaterialColor[300],
|
||||
selectedIconTheme: IconThemeData(color: accentMaterialColor[850]),
|
||||
unselectedIconTheme: IconThemeData(color: Colors.grey[850], opacity: 1),
|
||||
unselectedLabelTextStyle: TextStyle(
|
||||
color: Colors.grey[850],
|
||||
),
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: backgroundMaterialColor[100],
|
||||
height: 55,
|
||||
indicatorColor: accentMaterialColor[300],
|
||||
iconTheme: MaterialStateProperty.all(
|
||||
IconThemeData(color: Colors.grey[850]),
|
||||
),
|
||||
),
|
||||
cardTheme: CardTheme(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
color: backgroundMaterialColor[50],
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
onPrimary: accentMaterialColor[800],
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
cardColor: backgroundMaterialColor[50],
|
||||
canvasColor: backgroundMaterialColor[50],
|
||||
);
|
||||
}
|
@ -14,13 +14,13 @@ abstract class PersistedChangeNotifier extends ChangeNotifier {
|
||||
.fold<Map<String, dynamic>>({}, (acc, entry) {
|
||||
if (entry.value != null) {
|
||||
if (entry.value is bool) {
|
||||
acc[entry.key] = _localStorage.getBool(entry.key) ?? false;
|
||||
acc[entry.key] = _localStorage.getBool(entry.key);
|
||||
} else if (entry.value is int) {
|
||||
acc[entry.key] = _localStorage.getInt(entry.key) ?? -1;
|
||||
acc[entry.key] = _localStorage.getInt(entry.key);
|
||||
} else if (entry.value is double) {
|
||||
acc[entry.key] = _localStorage.getDouble(entry.key) ?? -1.0;
|
||||
acc[entry.key] = _localStorage.getDouble(entry.key);
|
||||
} else if (entry.value is String) {
|
||||
acc[entry.key] = _localStorage.getString(entry.key) ?? "";
|
||||
acc[entry.key] = _localStorage.getString(entry.key);
|
||||
}
|
||||
} else {
|
||||
acc[entry.key] = _localStorage.get(entry.key);
|
||||
|
Loading…
Reference in New Issue
Block a user