mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-14 00:15:17 +00:00
feat: better language picker, adaptive select tile and settings section contrast
This commit is contained in:
parent
12915f3e5a
commit
6430a25870
262
lib/collections/language_codes.dart
Normal file
262
lib/collections/language_codes.dart
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
class ISOLanguageName {
|
||||||
|
final String name;
|
||||||
|
final String nativeName;
|
||||||
|
|
||||||
|
const ISOLanguageName({
|
||||||
|
required this.name,
|
||||||
|
required this.nativeName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class LanguageLocals {
|
||||||
|
static final Map isoLangs = {
|
||||||
|
"ab": const ISOLanguageName(name: "Abkhaz", nativeName: "аҧсуа"),
|
||||||
|
"aa": const ISOLanguageName(name: "Afar", nativeName: "Afaraf"),
|
||||||
|
"af": const ISOLanguageName(name: "Afrikaans", nativeName: "Afrikaans"),
|
||||||
|
"ak": const ISOLanguageName(name: "Akan", nativeName: "Akan"),
|
||||||
|
"sq": const ISOLanguageName(name: "Albanian", nativeName: "Shqip"),
|
||||||
|
"am": const ISOLanguageName(name: "Amharic", nativeName: "አማርኛ"),
|
||||||
|
"ar": const ISOLanguageName(name: "Arabic", nativeName: "العربية"),
|
||||||
|
"an": const ISOLanguageName(name: "Aragonese", nativeName: "Aragonés"),
|
||||||
|
"hy": const ISOLanguageName(name: "Armenian", nativeName: "Հայերեն"),
|
||||||
|
"as": const ISOLanguageName(name: "Assamese", nativeName: "অসমীয়া"),
|
||||||
|
"av": const ISOLanguageName(
|
||||||
|
name: "Avaric",
|
||||||
|
nativeName: "авар мацӀ, магӀарул мацӀ",
|
||||||
|
),
|
||||||
|
"ae": const ISOLanguageName(name: "Avestan", nativeName: "avesta"),
|
||||||
|
"ay": const ISOLanguageName(name: "Aymara", nativeName: "aymar aru"),
|
||||||
|
"az": const ISOLanguageName(
|
||||||
|
name: "Azerbaijani",
|
||||||
|
nativeName: "azərbaycan dili",
|
||||||
|
),
|
||||||
|
"bm": const ISOLanguageName(name: "Bambara", nativeName: "bamanankan"),
|
||||||
|
"ba": const ISOLanguageName(name: "Bashkir", nativeName: "башҡорт теле"),
|
||||||
|
"eu": const ISOLanguageName(name: "Basque", nativeName: "euskara,"),
|
||||||
|
"be": const ISOLanguageName(name: "Belarusian", nativeName: "Беларуская"),
|
||||||
|
"bn": const ISOLanguageName(name: "Bengali", nativeName: "বাংলা"),
|
||||||
|
"bh": const ISOLanguageName(name: "Bihari", nativeName: "भोजपुरी"),
|
||||||
|
"bi": const ISOLanguageName(name: "Bislama", nativeName: "Bislama"),
|
||||||
|
"bs": const ISOLanguageName(name: "Bosnian", nativeName: "bosanski jezik"),
|
||||||
|
"br": const ISOLanguageName(name: "Breton", nativeName: "brezhoneg"),
|
||||||
|
"bg":
|
||||||
|
const ISOLanguageName(name: "Bulgarian", nativeName: "български език"),
|
||||||
|
"my": const ISOLanguageName(name: "Burmese", nativeName: "ဗမာစာ"),
|
||||||
|
"ca":
|
||||||
|
const ISOLanguageName(name: "Catalan; Valencian", nativeName: "Català"),
|
||||||
|
"ch": const ISOLanguageName(name: "Chamorro", nativeName: "Chamoru"),
|
||||||
|
"ce": const ISOLanguageName(name: "Chechen", nativeName: "нохчийн мотт"),
|
||||||
|
"ny": const ISOLanguageName(name: "Chichewa", nativeName: "chiCheŵa"),
|
||||||
|
"zh": const ISOLanguageName(name: "Chinese", nativeName: "汉语"),
|
||||||
|
"cv": const ISOLanguageName(name: "Chuvash", nativeName: "чӑваш чӗлхи"),
|
||||||
|
"kw": const ISOLanguageName(name: "Cornish", nativeName: "Kernewek"),
|
||||||
|
"co": const ISOLanguageName(name: "Corsican", nativeName: "lingua corsa"),
|
||||||
|
"cr": const ISOLanguageName(name: "Cree", nativeName: "ᓀᐦᐃᔭᐍᐏᐣ"),
|
||||||
|
"hr": const ISOLanguageName(name: "Croatian", nativeName: "hrvatski"),
|
||||||
|
"cs": const ISOLanguageName(name: "Czech", nativeName: "česky, čeština"),
|
||||||
|
"da": const ISOLanguageName(name: "Danish", nativeName: "dansk"),
|
||||||
|
"dv": const ISOLanguageName(name: "Maldivian;", nativeName: "ދިވެހި"),
|
||||||
|
"nl": const ISOLanguageName(name: "Dutch", nativeName: "Vlaams"),
|
||||||
|
"en": const ISOLanguageName(name: "English", nativeName: "English"),
|
||||||
|
"eo": const ISOLanguageName(name: "Esperanto", nativeName: "Esperanto"),
|
||||||
|
"et": const ISOLanguageName(name: "Estonian", nativeName: "eesti"),
|
||||||
|
"ee": const ISOLanguageName(name: "Ewe", nativeName: "Eʋegbe"),
|
||||||
|
"fo": const ISOLanguageName(name: "Faroese", nativeName: "føroyskt"),
|
||||||
|
"fj": const ISOLanguageName(name: "Fijian", nativeName: "vosa Vakaviti"),
|
||||||
|
"fi": const ISOLanguageName(name: "Finnish", nativeName: "suomi"),
|
||||||
|
"fr": const ISOLanguageName(name: "French", nativeName: "français"),
|
||||||
|
"ff": const ISOLanguageName(
|
||||||
|
name: "Fula; Fulah; Pulaar; Pular",
|
||||||
|
nativeName: "Fulfulde, Pulaar, Pular"),
|
||||||
|
"gl": const ISOLanguageName(name: "Galician", nativeName: "Galego"),
|
||||||
|
"ka": const ISOLanguageName(name: "Georgian", nativeName: "ქართული"),
|
||||||
|
"de": const ISOLanguageName(name: "German", nativeName: "Deutsch"),
|
||||||
|
"el": const ISOLanguageName(name: "Greek, Modern", nativeName: "Ελληνικά"),
|
||||||
|
"gn": const ISOLanguageName(name: "Guaraní", nativeName: "Avañeẽ"),
|
||||||
|
"gu": const ISOLanguageName(name: "Gujarati", nativeName: "ગુજરાતી"),
|
||||||
|
"ht": const ISOLanguageName(
|
||||||
|
name: "Haitian; Haitian Creole", nativeName: "Kreyòl ayisyen"),
|
||||||
|
"ha": const ISOLanguageName(name: "Hausa", nativeName: "Hausa, هَوُسَ"),
|
||||||
|
"he": const ISOLanguageName(name: "Hebrew (modern)", nativeName: "עברית"),
|
||||||
|
"hz": const ISOLanguageName(name: "Herero", nativeName: "Otjiherero"),
|
||||||
|
"hi": const ISOLanguageName(name: "Hindi", nativeName: "हिन्दी, हिंदी"),
|
||||||
|
"ho": const ISOLanguageName(name: "Hiri Motu", nativeName: "Hiri Motu"),
|
||||||
|
"hu": const ISOLanguageName(name: "Hungarian", nativeName: "Magyar"),
|
||||||
|
"ia": const ISOLanguageName(name: "Interlingua", nativeName: "Interlingua"),
|
||||||
|
"id": const ISOLanguageName(
|
||||||
|
name: "Indonesian", nativeName: "Bahasa Indonesia"),
|
||||||
|
"ie": const ISOLanguageName(name: "Interlingue", nativeName: "Occidental"),
|
||||||
|
"ga": const ISOLanguageName(name: "Irish", nativeName: "Gaeilge"),
|
||||||
|
"ig": const ISOLanguageName(name: "Igbo", nativeName: "Asụsụ Igbo"),
|
||||||
|
"ik": const ISOLanguageName(
|
||||||
|
name: "Inupiaq", nativeName: "Iñupiaq, Iñupiatun"),
|
||||||
|
"io": const ISOLanguageName(name: "Ido", nativeName: "Ido"),
|
||||||
|
"is": const ISOLanguageName(name: "Icelandic", nativeName: "Íslenska"),
|
||||||
|
"it": const ISOLanguageName(name: "Italian", nativeName: "Italiano"),
|
||||||
|
"iu": const ISOLanguageName(name: "Inuktitut", nativeName: "ᐃᓄᒃᑎᑐᑦ"),
|
||||||
|
"ja":
|
||||||
|
const ISOLanguageName(name: "Japanese", nativeName: "日本語 (にほんご/にっぽんご)"),
|
||||||
|
"jv": const ISOLanguageName(name: "Javanese", nativeName: "basa Jawa"),
|
||||||
|
"kl": const ISOLanguageName(
|
||||||
|
name: "Kalaallisut, Greenlandic",
|
||||||
|
nativeName: "kalaallisut, kalaallit oqaasii"),
|
||||||
|
"kn": const ISOLanguageName(name: "Kannada", nativeName: "ಕನ್ನಡ"),
|
||||||
|
"kr": const ISOLanguageName(name: "Kanuri", nativeName: "Kanuri"),
|
||||||
|
"ks":
|
||||||
|
const ISOLanguageName(name: "Kashmiri", nativeName: "कश्मीरी, كشميري"),
|
||||||
|
"kk": const ISOLanguageName(name: "Kazakh", nativeName: "Қазақ тілі"),
|
||||||
|
"km": const ISOLanguageName(name: "Khmer", nativeName: "ភាសាខ្មែរ"),
|
||||||
|
"ki": const ISOLanguageName(name: "Kikuyu, Gikuyu", nativeName: "Gĩkũyũ"),
|
||||||
|
"rw":
|
||||||
|
const ISOLanguageName(name: "Kinyarwanda", nativeName: "Ikinyarwanda"),
|
||||||
|
"ky": const ISOLanguageName(
|
||||||
|
name: "Kirghiz, Kyrgyz", nativeName: "кыргыз тили"),
|
||||||
|
"kv": const ISOLanguageName(name: "Komi", nativeName: "коми кыв"),
|
||||||
|
"kg": const ISOLanguageName(name: "Kongo", nativeName: "KiKongo"),
|
||||||
|
"ko": const ISOLanguageName(
|
||||||
|
name: "Korean", nativeName: "한국어 (韓國語), 조선말 (朝鮮語)"),
|
||||||
|
"ku": const ISOLanguageName(name: "Kurdish", nativeName: "Kurdî, كوردی"),
|
||||||
|
"kj": const ISOLanguageName(
|
||||||
|
name: "Kwanyama, Kuanyama", nativeName: "Kuanyama"),
|
||||||
|
"la": const ISOLanguageName(
|
||||||
|
name: "Latin", nativeName: "latine, lingua latina"),
|
||||||
|
"lb": const ISOLanguageName(
|
||||||
|
name: "Luxembourgish, Letzeburgesch", nativeName: "Lëtzebuergesch"),
|
||||||
|
"lg": const ISOLanguageName(name: "Luganda", nativeName: "Luganda"),
|
||||||
|
"li": const ISOLanguageName(
|
||||||
|
name: "Limburgish, Limburgan, Limburger", nativeName: "Limburgs"),
|
||||||
|
"ln": const ISOLanguageName(name: "Lingala", nativeName: "Lingála"),
|
||||||
|
"lo": const ISOLanguageName(name: "Lao", nativeName: "ພາສາລາວ"),
|
||||||
|
"lt":
|
||||||
|
const ISOLanguageName(name: "Lithuanian", nativeName: "lietuvių kalba"),
|
||||||
|
"lu": const ISOLanguageName(name: "Luba-Katanga", nativeName: ""),
|
||||||
|
"lv": const ISOLanguageName(name: "Latvian", nativeName: "latviešu valoda"),
|
||||||
|
"gv": const ISOLanguageName(name: "Manx", nativeName: "Gaelg, Gailck"),
|
||||||
|
"mk": const ISOLanguageName(
|
||||||
|
name: "Macedonian", nativeName: "македонски јазик"),
|
||||||
|
"mg":
|
||||||
|
const ISOLanguageName(name: "Malagasy", nativeName: "Malagasy fiteny"),
|
||||||
|
"ms": const ISOLanguageName(
|
||||||
|
name: "Malay", nativeName: "bahasa Melayu, بهاس ملايو"),
|
||||||
|
"ml": const ISOLanguageName(name: "Malayalam", nativeName: "മലയാളം"),
|
||||||
|
"mt": const ISOLanguageName(name: "Maltese", nativeName: "Malti"),
|
||||||
|
"mi": const ISOLanguageName(name: "Māori", nativeName: "te reo Māori"),
|
||||||
|
"mr": const ISOLanguageName(name: "Marathi (Marāṭhī)", nativeName: "मराठी"),
|
||||||
|
"mh":
|
||||||
|
const ISOLanguageName(name: "Marshallese", nativeName: "Kajin M̧ajeļ"),
|
||||||
|
"mn": const ISOLanguageName(name: "Mongolian", nativeName: "монгол"),
|
||||||
|
"na": const ISOLanguageName(name: "Nauru", nativeName: "Ekakairũ Naoero"),
|
||||||
|
"nv": const ISOLanguageName(
|
||||||
|
name: "Navajo, Navaho", nativeName: "Diné bizaad, Dinékʼehǰí"),
|
||||||
|
"nb": const ISOLanguageName(
|
||||||
|
name: "Norwegian Bokmål", nativeName: "Norsk bokmål"),
|
||||||
|
"nd":
|
||||||
|
const ISOLanguageName(name: "North Ndebele", nativeName: "isiNdebele"),
|
||||||
|
"ne": const ISOLanguageName(name: "Nepali", nativeName: "नेपाली"),
|
||||||
|
"ng": const ISOLanguageName(name: "Ndonga", nativeName: "Owambo"),
|
||||||
|
"nn": const ISOLanguageName(
|
||||||
|
name: "Norwegian Nynorsk", nativeName: "Norsk nynorsk"),
|
||||||
|
"no": const ISOLanguageName(name: "Norwegian", nativeName: "Norsk"),
|
||||||
|
"ii": const ISOLanguageName(name: "Nuosu", nativeName: "ꆈꌠ꒿ Nuosuhxop"),
|
||||||
|
"nr":
|
||||||
|
const ISOLanguageName(name: "South Ndebele", nativeName: "isiNdebele"),
|
||||||
|
"oc": const ISOLanguageName(name: "Occitan", nativeName: "Occitan"),
|
||||||
|
"oj": const ISOLanguageName(name: "Ojibwe, Ojibwa", nativeName: "ᐊᓂᔑᓈᐯᒧᐎᓐ"),
|
||||||
|
"cu": const ISOLanguageName(
|
||||||
|
name: "Old Church Slavonic", nativeName: "ѩзыкъ словѣньскъ"),
|
||||||
|
"om": const ISOLanguageName(name: "Oromo", nativeName: "Afaan Oromoo"),
|
||||||
|
"or": const ISOLanguageName(name: "Oriya", nativeName: "ଓଡ଼ିଆ"),
|
||||||
|
"os": const ISOLanguageName(
|
||||||
|
name: "Ossetian, Ossetic", nativeName: "ирон æвзаг"),
|
||||||
|
"pa": const ISOLanguageName(
|
||||||
|
name: "Panjabi, Punjabi", nativeName: "ਪੰਜਾਬੀ, پنجابی"),
|
||||||
|
"pi": const ISOLanguageName(name: "Pāli", nativeName: "पाऴि"),
|
||||||
|
"fa": const ISOLanguageName(name: "Persian", nativeName: "فارسی"),
|
||||||
|
"pl": const ISOLanguageName(name: "Polish", nativeName: "polski"),
|
||||||
|
"ps": const ISOLanguageName(name: "Pashto, Pushto", nativeName: "پښتو"),
|
||||||
|
"pt": const ISOLanguageName(name: "Portuguese", nativeName: "Português"),
|
||||||
|
"qu":
|
||||||
|
const ISOLanguageName(name: "Quechua", nativeName: "Runa Simi, Kichwa"),
|
||||||
|
"rm": const ISOLanguageName(
|
||||||
|
name: "Romansh", nativeName: "rumantsch grischun"),
|
||||||
|
"rn": const ISOLanguageName(name: "Kirundi", nativeName: "kiRundi"),
|
||||||
|
"ro": const ISOLanguageName(
|
||||||
|
name: "Romanian, Moldavian, Moldovan", nativeName: "română"),
|
||||||
|
"ru": const ISOLanguageName(name: "Russian", nativeName: "русский язык"),
|
||||||
|
"sa": const ISOLanguageName(
|
||||||
|
name: "Sanskrit (Saṁskṛta)", nativeName: "संस्कृतम्"),
|
||||||
|
"sc": const ISOLanguageName(name: "Sardinian", nativeName: "sardu"),
|
||||||
|
"sd": const ISOLanguageName(
|
||||||
|
name: "Sindhi", nativeName: "सिन्धी, سنڌي، سندھی"),
|
||||||
|
"se": const ISOLanguageName(
|
||||||
|
name: "Northern Sami", nativeName: "Davvisámegiella"),
|
||||||
|
"sm": const ISOLanguageName(name: "Samoan", nativeName: "gagana faa Samoa"),
|
||||||
|
"sg": const ISOLanguageName(name: "Sango", nativeName: "yângâ tî sängö"),
|
||||||
|
"sr": const ISOLanguageName(name: "Serbian", nativeName: "српски језик"),
|
||||||
|
"gd": const ISOLanguageName(
|
||||||
|
name: "Scottish Gaelic; Gaelic", nativeName: "Gàidhlig"),
|
||||||
|
"sn": const ISOLanguageName(name: "Shona", nativeName: "chiShona"),
|
||||||
|
"si":
|
||||||
|
const ISOLanguageName(name: "Sinhala, Sinhalese", nativeName: "සිංහල"),
|
||||||
|
"sk": const ISOLanguageName(name: "Slovak", nativeName: "slovenčina"),
|
||||||
|
"sl": const ISOLanguageName(name: "Slovene", nativeName: "slovenščina"),
|
||||||
|
"so": const ISOLanguageName(
|
||||||
|
name: "Somali", nativeName: "Soomaaliga, af Soomaali"),
|
||||||
|
"st": const ISOLanguageName(name: "Southern Sotho", nativeName: "Sesotho"),
|
||||||
|
"es": const ISOLanguageName(
|
||||||
|
name: "Spanish; Castilian", nativeName: "español, castellano"),
|
||||||
|
"su": const ISOLanguageName(name: "Sundanese", nativeName: "Basa Sunda"),
|
||||||
|
"sw": const ISOLanguageName(name: "Swahili", nativeName: "Kiswahili"),
|
||||||
|
"ss": const ISOLanguageName(name: "Swati", nativeName: "SiSwati"),
|
||||||
|
"sv": const ISOLanguageName(name: "Swedish", nativeName: "svenska"),
|
||||||
|
"ta": const ISOLanguageName(name: "Tamil", nativeName: "தமிழ்"),
|
||||||
|
"te": const ISOLanguageName(name: "Telugu", nativeName: "తెలుగు"),
|
||||||
|
"tg": const ISOLanguageName(
|
||||||
|
name: "Tajik", nativeName: "тоҷикӣ, toğikī, تاجیکی"),
|
||||||
|
"th": const ISOLanguageName(name: "Thai", nativeName: "ไทย"),
|
||||||
|
"ti": const ISOLanguageName(name: "Tigrinya", nativeName: "ትግርኛ"),
|
||||||
|
"bo": const ISOLanguageName(
|
||||||
|
name: "Tibetan Standard, Tibetan, Central", nativeName: "བོད་ཡིག"),
|
||||||
|
"tk":
|
||||||
|
const ISOLanguageName(name: "Turkmen", nativeName: "Türkmen, Түркмен"),
|
||||||
|
"tl": const ISOLanguageName(
|
||||||
|
name: "Tagalog", nativeName: "Wikang Tagalog, ᜏᜒᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔"),
|
||||||
|
"tn": const ISOLanguageName(name: "Tswana", nativeName: "Setswana"),
|
||||||
|
"to": const ISOLanguageName(
|
||||||
|
name: "Tonga (Tonga Islands)", nativeName: "faka Tonga"),
|
||||||
|
"tr": const ISOLanguageName(name: "Turkish", nativeName: "Türkçe"),
|
||||||
|
"ts": const ISOLanguageName(name: "Tsonga", nativeName: "Xitsonga"),
|
||||||
|
"tt": const ISOLanguageName(
|
||||||
|
name: "Tatar", nativeName: "татарча, tatarça, تاتارچا"),
|
||||||
|
"tw": const ISOLanguageName(name: "Twi", nativeName: "Twi"),
|
||||||
|
"ty": const ISOLanguageName(name: "Tahitian", nativeName: "Reo Tahiti"),
|
||||||
|
"ug": const ISOLanguageName(
|
||||||
|
name: "Uighur, Uyghur", nativeName: "Uyƣurqə, ئۇيغۇرچە"),
|
||||||
|
"uk": const ISOLanguageName(name: "Ukrainian", nativeName: "українська"),
|
||||||
|
"ur": const ISOLanguageName(name: "Urdu", nativeName: "اردو"),
|
||||||
|
"uz": const ISOLanguageName(
|
||||||
|
name: "Uzbek", nativeName: "zbek, Ўзбек, أۇزبېك"),
|
||||||
|
"ve": const ISOLanguageName(name: "Venda", nativeName: "Tshivenḓa"),
|
||||||
|
"vi": const ISOLanguageName(name: "Vietnamese", nativeName: "Tiếng Việt"),
|
||||||
|
"vo": const ISOLanguageName(name: "Volapük", nativeName: "Volapük"),
|
||||||
|
"wa": const ISOLanguageName(name: "Walloon", nativeName: "Walon"),
|
||||||
|
"cy": const ISOLanguageName(name: "Welsh", nativeName: "Cymraeg"),
|
||||||
|
"wo": const ISOLanguageName(name: "Wolof", nativeName: "Wollof"),
|
||||||
|
"fy": const ISOLanguageName(name: "Western Frisian", nativeName: "Frysk"),
|
||||||
|
"xh": const ISOLanguageName(name: "Xhosa", nativeName: "isiXhosa"),
|
||||||
|
"yi": const ISOLanguageName(name: "Yiddish", nativeName: "ייִדיש"),
|
||||||
|
"yo": const ISOLanguageName(name: "Yoruba", nativeName: "Yorùbá"),
|
||||||
|
"za": const ISOLanguageName(
|
||||||
|
name: "Zhuang, Chuang",
|
||||||
|
nativeName: "Saɯ cueŋƅ, Saw cuengh",
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ISOLanguageName getDisplayLanguage(key) {
|
||||||
|
if (isoLangs.containsKey(key)) {
|
||||||
|
return isoLangs[key]!;
|
||||||
|
} else {
|
||||||
|
throw Exception("Language key incorrect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
lib/components/settings/section_card_with_heading.dart
Normal file
37
lib/components/settings/section_card_with_heading.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SectionCardWithHeading extends StatelessWidget {
|
||||||
|
final String heading;
|
||||||
|
final List<Widget> children;
|
||||||
|
const SectionCardWithHeading({
|
||||||
|
super.key,
|
||||||
|
required this.heading,
|
||||||
|
required this.children,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: Text(
|
||||||
|
heading,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Card(
|
||||||
|
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||||
|
child: Column(mainAxisSize: MainAxisSize.min, children: children),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
104
lib/components/shared/adaptive/adaptive_select_tile.dart
Normal file
104
lib/components/shared/adaptive/adaptive_select_tile.dart
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
|
|
||||||
|
class AdaptiveSelectTile<T> extends HookWidget {
|
||||||
|
final Widget title;
|
||||||
|
final Widget? subtitle;
|
||||||
|
final Widget? secondary;
|
||||||
|
final ListTileControlAffinity? controlAffinity;
|
||||||
|
final T value;
|
||||||
|
final ValueChanged<T?>? onChanged;
|
||||||
|
|
||||||
|
final List<DropdownMenuItem<T>> options;
|
||||||
|
|
||||||
|
final Breakpoints breakAfterOr;
|
||||||
|
|
||||||
|
const AdaptiveSelectTile({
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged,
|
||||||
|
required this.options,
|
||||||
|
this.controlAffinity = ListTileControlAffinity.trailing,
|
||||||
|
this.subtitle,
|
||||||
|
this.secondary,
|
||||||
|
this.breakAfterOr = Breakpoints.md,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final breakpoint = useBreakpoints();
|
||||||
|
final rawControl = DropdownButton<T>(
|
||||||
|
items: options,
|
||||||
|
value: value,
|
||||||
|
onChanged: onChanged,
|
||||||
|
);
|
||||||
|
final controlPlaceholder = useMemoized(
|
||||||
|
() => options
|
||||||
|
.firstWhere(
|
||||||
|
(element) => element.value == value,
|
||||||
|
orElse: () => DropdownMenuItem<T>(
|
||||||
|
value: null,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child,
|
||||||
|
[value, options]);
|
||||||
|
|
||||||
|
final control = breakpoint >= breakAfterOr
|
||||||
|
? rawControl
|
||||||
|
: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
child: controlPlaceholder,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: title,
|
||||||
|
subtitle: subtitle,
|
||||||
|
leading: controlAffinity != ListTileControlAffinity.leading
|
||||||
|
? secondary
|
||||||
|
: control,
|
||||||
|
trailing: controlAffinity == ListTileControlAffinity.leading
|
||||||
|
? secondary
|
||||||
|
: control,
|
||||||
|
onTap: breakpoint >= breakAfterOr
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: title,
|
||||||
|
children: [
|
||||||
|
for (final option in options)
|
||||||
|
RadioListTile<T>(
|
||||||
|
title: option.child,
|
||||||
|
value: option.value as T,
|
||||||
|
groupValue: value,
|
||||||
|
onChanged: (v) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
onChanged?.call(v);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -6,13 +6,17 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/collections/env.dart';
|
import 'package:spotube/collections/env.dart';
|
||||||
|
import 'package:spotube/collections/language_codes.dart';
|
||||||
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
||||||
|
import 'package:spotube/components/settings/section_card_with_heading.dart';
|
||||||
import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart';
|
import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart';
|
||||||
|
import 'package:spotube/components/shared/adaptive/adaptive_select_tile.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/collections/spotify_markets.dart';
|
import 'package:spotube/collections/spotify_markets.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
import 'package:spotube/l10n/l10n.dart';
|
import 'package:spotube/l10n/l10n.dart';
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
import 'package:spotube/provider/downloader_provider.dart';
|
import 'package:spotube/provider/downloader_provider.dart';
|
||||||
@ -61,398 +65,382 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
constraints: const BoxConstraints(maxWidth: 1366),
|
constraints: const BoxConstraints(maxWidth: 1366),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
SectionCardWithHeading(
|
||||||
" ${context.l10n.account}",
|
heading: context.l10n.account,
|
||||||
style: theme.textTheme.headlineSmall
|
children: [
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
if (auth == null)
|
||||||
),
|
AdaptiveListTile(
|
||||||
if (auth == null)
|
leading: Icon(
|
||||||
AdaptiveListTile(
|
SpotubeIcons.login,
|
||||||
leading: Icon(
|
|
||||||
SpotubeIcons.login,
|
|
||||||
color: theme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
title: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: AutoSizeText(
|
|
||||||
context.l10n.login_with_spotify,
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
title: Align(
|
||||||
),
|
|
||||||
trailing: (context, update) => FilledButton(
|
|
||||||
onPressed: () {
|
|
||||||
GoRouter.of(context).push("/login");
|
|
||||||
},
|
|
||||||
style: ButtonStyle(
|
|
||||||
shape: MaterialStateProperty.all(
|
|
||||||
RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
context.l10n.connect_with_spotify.toUpperCase(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Builder(builder: (context) {
|
|
||||||
return ListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.logout),
|
|
||||||
title: SizedBox(
|
|
||||||
height: 50,
|
|
||||||
width: 180,
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: AutoSizeText(
|
child: AutoSizeText(
|
||||||
context.l10n.logout_of_this_account,
|
context.l10n.login_with_spotify,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
trailing: (context, update) => FilledButton(
|
||||||
trailing: FilledButton(
|
onPressed: () {
|
||||||
style: ButtonStyle(
|
GoRouter.of(context).push("/login");
|
||||||
backgroundColor:
|
},
|
||||||
MaterialStateProperty.all(Colors.red),
|
style: ButtonStyle(
|
||||||
foregroundColor:
|
shape: MaterialStateProperty.all(
|
||||||
MaterialStateProperty.all(Colors.white),
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(25.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
context.l10n.connect_with_spotify.toUpperCase(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
)
|
||||||
ref
|
else
|
||||||
.read(
|
Builder(builder: (context) {
|
||||||
AuthenticationNotifier.provider.notifier)
|
return ListTile(
|
||||||
.logout();
|
leading: const Icon(SpotubeIcons.logout),
|
||||||
GoRouter.of(context).pop();
|
title: SizedBox(
|
||||||
},
|
height: 50,
|
||||||
child: Text(context.l10n.logout),
|
width: 180,
|
||||||
),
|
child: Align(
|
||||||
);
|
alignment: Alignment.centerLeft,
|
||||||
}),
|
child: AutoSizeText(
|
||||||
Text(
|
context.l10n.logout_of_this_account,
|
||||||
" ${context.l10n.language_region}",
|
maxLines: 1,
|
||||||
style: theme.textTheme.headlineSmall
|
),
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
),
|
||||||
|
),
|
||||||
|
trailing: FilledButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.all(Colors.red),
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.all(Colors.white),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
ref
|
||||||
|
.read(AuthenticationNotifier
|
||||||
|
.provider.notifier)
|
||||||
|
.logout();
|
||||||
|
GoRouter.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(context.l10n.logout),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
ListTile(
|
SectionCardWithHeading(
|
||||||
leading: const Icon(SpotubeIcons.language),
|
heading: context.l10n.language_region,
|
||||||
title: Text(context.l10n.language),
|
children: [
|
||||||
trailing: DropdownButton<Locale>(
|
AdaptiveSelectTile<Locale>(
|
||||||
value: preferences.locale,
|
value: preferences.locale,
|
||||||
items: [
|
onChanged: (locale) {
|
||||||
DropdownMenuItem(
|
if (locale == null) return;
|
||||||
value: const Locale("system", "system"),
|
preferences.setLocale(locale);
|
||||||
child: Text(context.l10n.system_default),
|
},
|
||||||
),
|
title: Text(context.l10n.language),
|
||||||
for (final locale in L10n.all)
|
secondary: const Icon(SpotubeIcons.language),
|
||||||
|
options: [
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: locale,
|
value: const Locale("system", "system"),
|
||||||
child: Text(locale.languageCode),
|
child: Text(context.l10n.system_default),
|
||||||
),
|
),
|
||||||
],
|
for (final locale in L10n.all)
|
||||||
onChanged: (value) {
|
DropdownMenuItem(
|
||||||
if (value != null) {
|
value: locale,
|
||||||
preferences.setLocale(value);
|
child: Builder(builder: (context) {
|
||||||
}
|
final isoCodeName =
|
||||||
},
|
LanguageLocals.getDisplayLanguage(
|
||||||
),
|
locale.languageCode,
|
||||||
),
|
);
|
||||||
AdaptiveListTile(
|
return Text(
|
||||||
leading: const Icon(SpotubeIcons.shoppingBag),
|
"${isoCodeName.name} (${isoCodeName.nativeName})",
|
||||||
title: Text(context.l10n.market_place_region),
|
);
|
||||||
subtitle: Text(context.l10n.recommendation_country),
|
}),
|
||||||
trailing: (context, update) => ConstrainedBox(
|
),
|
||||||
constraints: const BoxConstraints(maxWidth: 350),
|
],
|
||||||
child: DropdownMenu(
|
),
|
||||||
initialSelection: preferences.recommendationMarket,
|
AdaptiveSelectTile<String>(
|
||||||
dropdownMenuEntries: spotifyMarkets
|
breakAfterOr: Breakpoints.lg,
|
||||||
|
secondary: const Icon(SpotubeIcons.shoppingBag),
|
||||||
|
title: Text(context.l10n.market_place_region),
|
||||||
|
subtitle: Text(context.l10n.recommendation_country),
|
||||||
|
value: preferences.recommendationMarket,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == null) return;
|
||||||
|
preferences.setRecommendationMarket(value);
|
||||||
|
},
|
||||||
|
options: spotifyMarkets
|
||||||
.map(
|
.map(
|
||||||
(country) => DropdownMenuEntry(
|
(country) => DropdownMenuItem(
|
||||||
value: country.first,
|
value: country.first,
|
||||||
label: country.last,
|
child: Text(country.last),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
onSelected: (value) {
|
),
|
||||||
if (value == null) return;
|
],
|
||||||
preferences.setRecommendationMarket(
|
),
|
||||||
value as String,
|
SectionCardWithHeading(
|
||||||
);
|
heading: context.l10n.appearance,
|
||||||
update?.call(() {});
|
children: [
|
||||||
|
AdaptiveSelectTile<LayoutMode>(
|
||||||
|
secondary: const Icon(SpotubeIcons.dashboard),
|
||||||
|
title: Text(context.l10n.layout_mode),
|
||||||
|
subtitle: Text(context.l10n.override_layout_settings),
|
||||||
|
value: preferences.layoutMode,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
preferences.setLayoutMode(value);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
options: [
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
" ${context.l10n.appearance}",
|
|
||||||
style: theme.textTheme.headlineSmall
|
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
AdaptiveListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.dashboard),
|
|
||||||
title: Text(context.l10n.layout_mode),
|
|
||||||
subtitle: Text(context.l10n.override_layout_settings),
|
|
||||||
trailing: (context, update) => DropdownButton<LayoutMode>(
|
|
||||||
items: [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: LayoutMode.adaptive,
|
|
||||||
child: Text(context.l10n.adaptive),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: LayoutMode.compact,
|
|
||||||
child: Text(context.l10n.compact),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: LayoutMode.extended,
|
|
||||||
child: Text(context.l10n.extended),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
value: preferences.layoutMode,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
preferences.setLayoutMode(value);
|
|
||||||
update?.call(() {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
AdaptiveListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.darkMode),
|
|
||||||
title: Text(context.l10n.theme),
|
|
||||||
trailing: (context, update) => DropdownButton<ThemeMode>(
|
|
||||||
value: preferences.themeMode,
|
|
||||||
items: [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ThemeMode.dark,
|
|
||||||
child: Text(context.l10n.dark),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ThemeMode.light,
|
|
||||||
child: Text(context.l10n.light),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ThemeMode.system,
|
|
||||||
child: Text(context.l10n.system),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
preferences.setThemeMode(value);
|
|
||||||
update?.call(() {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.palette),
|
|
||||||
title: Text(context.l10n.accent_color),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 15,
|
|
||||||
vertical: 5,
|
|
||||||
),
|
|
||||||
trailing: ColorTile.compact(
|
|
||||||
color: preferences.accentColorScheme,
|
|
||||||
onPressed: pickColorScheme(),
|
|
||||||
isActive: true,
|
|
||||||
),
|
|
||||||
onTap: pickColorScheme(),
|
|
||||||
),
|
|
||||||
SwitchListTile(
|
|
||||||
secondary: const Icon(SpotubeIcons.colorSync),
|
|
||||||
title: Text(context.l10n.sync_album_color),
|
|
||||||
subtitle: Text(context.l10n.sync_album_color_description),
|
|
||||||
value: preferences.albumColorSync,
|
|
||||||
onChanged: preferences.setAlbumColorSync,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
" ${context.l10n.playback}",
|
|
||||||
style: theme.textTheme.headlineSmall
|
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
AdaptiveListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.audioQuality),
|
|
||||||
title: Text(context.l10n.audio_quality),
|
|
||||||
trailing: (context, update) =>
|
|
||||||
DropdownButton<AudioQuality>(
|
|
||||||
value: preferences.audioQuality,
|
|
||||||
items: [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: AudioQuality.high,
|
|
||||||
child: Text(context.l10n.high),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: AudioQuality.low,
|
|
||||||
child: Text(context.l10n.low),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
preferences.setAudioQuality(value);
|
|
||||||
update?.call(() {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SwitchListTile(
|
|
||||||
secondary: const Icon(SpotubeIcons.download),
|
|
||||||
title: Text(context.l10n.pre_download_play),
|
|
||||||
subtitle:
|
|
||||||
Text(context.l10n.pre_download_play_description),
|
|
||||||
value: preferences.predownload,
|
|
||||||
onChanged: (state) {
|
|
||||||
preferences.setPredownload(state);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SwitchListTile(
|
|
||||||
secondary: const Icon(SpotubeIcons.fastForward),
|
|
||||||
title: Text(context.l10n.skip_non_music),
|
|
||||||
value: preferences.skipSponsorSegments,
|
|
||||||
onChanged: (state) {
|
|
||||||
preferences.setSkipSponsorSegments(state);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.playlistRemove),
|
|
||||||
title: Text(context.l10n.blacklist),
|
|
||||||
subtitle: Text(context.l10n.blacklist_description),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).push("/settings/blacklist");
|
|
||||||
},
|
|
||||||
trailing: const Icon(SpotubeIcons.angleRight),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
" ${context.l10n.downloads}",
|
|
||||||
style: theme.textTheme.headlineSmall
|
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: isDownloading
|
|
||||||
? context.l10n.wait_for_download_to_finish
|
|
||||||
: "",
|
|
||||||
child: ListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.download),
|
|
||||||
title: Text(context.l10n.download_location),
|
|
||||||
subtitle: Text(preferences.downloadLocation),
|
|
||||||
trailing: FilledButton(
|
|
||||||
onPressed:
|
|
||||||
isDownloading ? null : pickDownloadLocation,
|
|
||||||
child: const Icon(SpotubeIcons.folder),
|
|
||||||
),
|
|
||||||
onTap: isDownloading ? null : pickDownloadLocation,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SwitchListTile(
|
|
||||||
secondary: const Icon(SpotubeIcons.lyrics),
|
|
||||||
title: Text(context.l10n.download_lyrics),
|
|
||||||
value: preferences.saveTrackLyrics,
|
|
||||||
onChanged: (state) {
|
|
||||||
preferences.setSaveTrackLyrics(state);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (DesktopTools.platform.isDesktop) ...[
|
|
||||||
Text(
|
|
||||||
" ${context.l10n.desktop}",
|
|
||||||
style: theme.textTheme.headlineSmall
|
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
AdaptiveListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.close),
|
|
||||||
title: Text(context.l10n.close_behavior),
|
|
||||||
trailing: (context, update) =>
|
|
||||||
DropdownButton<CloseBehavior>(
|
|
||||||
value: preferences.closeBehavior,
|
|
||||||
items: [
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: CloseBehavior.close,
|
value: LayoutMode.adaptive,
|
||||||
child: Text(context.l10n.close),
|
child: Text(context.l10n.adaptive),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: CloseBehavior.minimizeToTray,
|
value: LayoutMode.compact,
|
||||||
child: Text(context.l10n.minimize_to_tray),
|
child: Text(context.l10n.compact),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: LayoutMode.extended,
|
||||||
|
child: Text(context.l10n.extended),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
AdaptiveSelectTile<ThemeMode>(
|
||||||
|
secondary: const Icon(SpotubeIcons.darkMode),
|
||||||
|
title: Text(context.l10n.theme),
|
||||||
|
value: preferences.themeMode,
|
||||||
|
options: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: ThemeMode.dark,
|
||||||
|
child: Text(context.l10n.dark),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: ThemeMode.light,
|
||||||
|
child: Text(context.l10n.light),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: ThemeMode.system,
|
||||||
|
child: Text(context.l10n.system),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setCloseBehavior(value);
|
preferences.setThemeMode(value);
|
||||||
update?.call(() {});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
ListTile(
|
||||||
SwitchListTile(
|
leading: const Icon(SpotubeIcons.palette),
|
||||||
secondary: const Icon(SpotubeIcons.tray),
|
title: Text(context.l10n.accent_color),
|
||||||
title: Text(context.l10n.show_tray_icon),
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
value: preferences.showSystemTrayIcon,
|
horizontal: 15,
|
||||||
onChanged: preferences.setShowSystemTrayIcon,
|
vertical: 5,
|
||||||
),
|
),
|
||||||
],
|
trailing: ColorTile.compact(
|
||||||
Text(
|
color: preferences.accentColorScheme,
|
||||||
" ${context.l10n.about}",
|
onPressed: pickColorScheme(),
|
||||||
style: theme.textTheme.headlineSmall
|
isActive: true,
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
),
|
||||||
|
onTap: pickColorScheme(),
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(SpotubeIcons.colorSync),
|
||||||
|
title: Text(context.l10n.sync_album_color),
|
||||||
|
subtitle:
|
||||||
|
Text(context.l10n.sync_album_color_description),
|
||||||
|
value: preferences.albumColorSync,
|
||||||
|
onChanged: preferences.setAlbumColorSync,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
AdaptiveListTile(
|
SectionCardWithHeading(
|
||||||
leading: const Icon(
|
heading: context.l10n.playback,
|
||||||
SpotubeIcons.heart,
|
children: [
|
||||||
color: Colors.pink,
|
AdaptiveSelectTile<AudioQuality>(
|
||||||
|
secondary: const Icon(SpotubeIcons.audioQuality),
|
||||||
|
title: Text(context.l10n.audio_quality),
|
||||||
|
value: preferences.audioQuality,
|
||||||
|
options: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: AudioQuality.high,
|
||||||
|
child: Text(context.l10n.high),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: AudioQuality.low,
|
||||||
|
child: Text(context.l10n.low),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
preferences.setAudioQuality(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(SpotubeIcons.download),
|
||||||
|
title: Text(context.l10n.pre_download_play),
|
||||||
|
subtitle:
|
||||||
|
Text(context.l10n.pre_download_play_description),
|
||||||
|
value: preferences.predownload,
|
||||||
|
onChanged: (state) {
|
||||||
|
preferences.setPredownload(state);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(SpotubeIcons.fastForward),
|
||||||
|
title: Text(context.l10n.skip_non_music),
|
||||||
|
value: preferences.skipSponsorSegments,
|
||||||
|
onChanged: (state) {
|
||||||
|
preferences.setSkipSponsorSegments(state);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.playlistRemove),
|
||||||
|
title: Text(context.l10n.blacklist),
|
||||||
|
subtitle: Text(context.l10n.blacklist_description),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).push("/settings/blacklist");
|
||||||
|
},
|
||||||
|
trailing: const Icon(SpotubeIcons.angleRight),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SectionCardWithHeading(
|
||||||
|
heading: context.l10n.downloads,
|
||||||
|
children: [
|
||||||
|
Tooltip(
|
||||||
|
message: isDownloading
|
||||||
|
? context.l10n.wait_for_download_to_finish
|
||||||
|
: "",
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.download),
|
||||||
|
title: Text(context.l10n.download_location),
|
||||||
|
subtitle: Text(preferences.downloadLocation),
|
||||||
|
trailing: FilledButton(
|
||||||
|
onPressed:
|
||||||
|
isDownloading ? null : pickDownloadLocation,
|
||||||
|
child: const Icon(SpotubeIcons.folder),
|
||||||
|
),
|
||||||
|
onTap: isDownloading ? null : pickDownloadLocation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(SpotubeIcons.lyrics),
|
||||||
|
title: Text(context.l10n.download_lyrics),
|
||||||
|
value: preferences.saveTrackLyrics,
|
||||||
|
onChanged: (state) {
|
||||||
|
preferences.setSaveTrackLyrics(state);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (DesktopTools.platform.isDesktop)
|
||||||
|
SectionCardWithHeading(
|
||||||
|
heading: context.l10n.desktop,
|
||||||
|
children: [
|
||||||
|
AdaptiveSelectTile<CloseBehavior>(
|
||||||
|
secondary: const Icon(SpotubeIcons.close),
|
||||||
|
title: Text(context.l10n.close_behavior),
|
||||||
|
value: preferences.closeBehavior,
|
||||||
|
options: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: CloseBehavior.close,
|
||||||
|
child: Text(context.l10n.close),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: CloseBehavior.minimizeToTray,
|
||||||
|
child: Text(context.l10n.minimize_to_tray),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
preferences.setCloseBehavior(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(SpotubeIcons.tray),
|
||||||
|
title: Text(context.l10n.show_tray_icon),
|
||||||
|
value: preferences.showSystemTrayIcon,
|
||||||
|
onChanged: preferences.setShowSystemTrayIcon,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
title: SizedBox(
|
SectionCardWithHeading(
|
||||||
height: 50,
|
heading: context.l10n.about,
|
||||||
width: 200,
|
children: [
|
||||||
child: Align(
|
AdaptiveListTile(
|
||||||
alignment: Alignment.centerLeft,
|
leading: const Icon(
|
||||||
child: AutoSizeText(
|
SpotubeIcons.heart,
|
||||||
context.l10n.u_love_spotube,
|
color: Colors.pink,
|
||||||
maxLines: 1,
|
),
|
||||||
style: const TextStyle(
|
title: SizedBox(
|
||||||
color: Colors.pink,
|
height: 50,
|
||||||
fontWeight: FontWeight.bold,
|
width: 200,
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: AutoSizeText(
|
||||||
|
context.l10n.u_love_spotube,
|
||||||
|
maxLines: 1,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.pink,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: (context, update) => FilledButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStatePropertyAll(Colors.red[100]),
|
||||||
|
foregroundColor: const MaterialStatePropertyAll(
|
||||||
|
Colors.pinkAccent),
|
||||||
|
padding: const MaterialStatePropertyAll(
|
||||||
|
EdgeInsets.all(15)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(
|
||||||
|
"https://opencollective.com/spotube",
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(SpotubeIcons.heart),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Text(context.l10n.please_sponsor),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (Env.enableUpdateChecker)
|
||||||
trailing: (context, update) => FilledButton(
|
SwitchListTile(
|
||||||
style: ButtonStyle(
|
secondary: const Icon(SpotubeIcons.update),
|
||||||
backgroundColor:
|
title: Text(context.l10n.check_for_updates),
|
||||||
MaterialStatePropertyAll(Colors.red[100]),
|
value: preferences.checkUpdate,
|
||||||
foregroundColor:
|
onChanged: (checked) =>
|
||||||
const MaterialStatePropertyAll(Colors.pinkAccent),
|
preferences.setCheckUpdate(checked),
|
||||||
padding: const MaterialStatePropertyAll(
|
),
|
||||||
EdgeInsets.all(15)),
|
ListTile(
|
||||||
),
|
leading: const Icon(SpotubeIcons.info),
|
||||||
onPressed: () {
|
title: Text(context.l10n.about_spotube),
|
||||||
launchUrlString(
|
trailing: const Icon(SpotubeIcons.angleRight),
|
||||||
"https://opencollective.com/spotube",
|
onTap: () {
|
||||||
mode: LaunchMode.externalApplication,
|
GoRouter.of(context).push("/settings/about");
|
||||||
);
|
},
|
||||||
},
|
)
|
||||||
child: Row(
|
],
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(SpotubeIcons.heart),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
Text(context.l10n.please_sponsor),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (Env.enableUpdateChecker)
|
|
||||||
SwitchListTile(
|
|
||||||
secondary: const Icon(SpotubeIcons.update),
|
|
||||||
title: Text(context.l10n.check_for_updates),
|
|
||||||
value: preferences.checkUpdate,
|
|
||||||
onChanged: (checked) =>
|
|
||||||
preferences.setCheckUpdate(checked),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.info),
|
|
||||||
title: Text(context.l10n.about_spotube),
|
|
||||||
trailing: const Icon(SpotubeIcons.angleRight),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).push("/settings/about");
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -95,7 +95,7 @@ class SpotubeAudioPlayer {
|
|||||||
|
|
||||||
Stream<bool> get bufferingStream {
|
Stream<bool> get bufferingStream {
|
||||||
if (apSupportedPlatform) {
|
if (apSupportedPlatform) {
|
||||||
return const Stream.empty();
|
return Stream.value(false);
|
||||||
} else {
|
} else {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@ -215,7 +215,6 @@ class SpotubeAudioPlayer {
|
|||||||
|
|
||||||
Future<void> pause() async {
|
Future<void> pause() async {
|
||||||
await _audioPlayer?.pause();
|
await _audioPlayer?.pause();
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> resume() async {
|
Future<void> resume() async {
|
||||||
|
@ -13,6 +13,7 @@ ThemeData theme(Color seed, Brightness brightness) {
|
|||||||
horizontalTitleGap: 0,
|
horizontalTitleGap: 0,
|
||||||
iconColor: scheme.onSurface,
|
iconColor: scheme.onSurface,
|
||||||
),
|
),
|
||||||
|
appBarTheme: const AppBarTheme(surfaceTintColor: Colors.transparent),
|
||||||
inputDecorationTheme: InputDecorationTheme(
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
Loading…
Reference in New Issue
Block a user