mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +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:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/env.dart';
|
||||
import 'package:spotube/collections/language_codes.dart';
|
||||
|
||||
import 'package:spotube/collections/spotube_icons.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_select_tile.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/collections/spotify_markets.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||
import 'package:spotube/l10n/l10n.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/downloader_provider.dart';
|
||||
@ -61,398 +65,382 @@ class SettingsPage extends HookConsumerWidget {
|
||||
constraints: const BoxConstraints(maxWidth: 1366),
|
||||
child: ListView(
|
||||
children: [
|
||||
Text(
|
||||
" ${context.l10n.account}",
|
||||
style: theme.textTheme.headlineSmall
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
if (auth == null)
|
||||
AdaptiveListTile(
|
||||
leading: Icon(
|
||||
SpotubeIcons.login,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
title: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: AutoSizeText(
|
||||
context.l10n.login_with_spotify,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
SectionCardWithHeading(
|
||||
heading: context.l10n.account,
|
||||
children: [
|
||||
if (auth == null)
|
||||
AdaptiveListTile(
|
||||
leading: Icon(
|
||||
SpotubeIcons.login,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
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(
|
||||
title: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: AutoSizeText(
|
||||
context.l10n.logout_of_this_account,
|
||||
context.l10n.login_with_spotify,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
trailing: FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(Colors.red),
|
||||
foregroundColor:
|
||||
MaterialStateProperty.all(Colors.white),
|
||||
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(),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
ref
|
||||
.read(
|
||||
AuthenticationNotifier.provider.notifier)
|
||||
.logout();
|
||||
GoRouter.of(context).pop();
|
||||
},
|
||||
child: Text(context.l10n.logout),
|
||||
),
|
||||
);
|
||||
}),
|
||||
Text(
|
||||
" ${context.l10n.language_region}",
|
||||
style: theme.textTheme.headlineSmall
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
)
|
||||
else
|
||||
Builder(builder: (context) {
|
||||
return ListTile(
|
||||
leading: const Icon(SpotubeIcons.logout),
|
||||
title: SizedBox(
|
||||
height: 50,
|
||||
width: 180,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: AutoSizeText(
|
||||
context.l10n.logout_of_this_account,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
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(
|
||||
leading: const Icon(SpotubeIcons.language),
|
||||
title: Text(context.l10n.language),
|
||||
trailing: DropdownButton<Locale>(
|
||||
value: preferences.locale,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: const Locale("system", "system"),
|
||||
child: Text(context.l10n.system_default),
|
||||
),
|
||||
for (final locale in L10n.all)
|
||||
SectionCardWithHeading(
|
||||
heading: context.l10n.language_region,
|
||||
children: [
|
||||
AdaptiveSelectTile<Locale>(
|
||||
value: preferences.locale,
|
||||
onChanged: (locale) {
|
||||
if (locale == null) return;
|
||||
preferences.setLocale(locale);
|
||||
},
|
||||
title: Text(context.l10n.language),
|
||||
secondary: const Icon(SpotubeIcons.language),
|
||||
options: [
|
||||
DropdownMenuItem(
|
||||
value: locale,
|
||||
child: Text(locale.languageCode),
|
||||
value: const Locale("system", "system"),
|
||||
child: Text(context.l10n.system_default),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
preferences.setLocale(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(SpotubeIcons.shoppingBag),
|
||||
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,
|
||||
dropdownMenuEntries: spotifyMarkets
|
||||
for (final locale in L10n.all)
|
||||
DropdownMenuItem(
|
||||
value: locale,
|
||||
child: Builder(builder: (context) {
|
||||
final isoCodeName =
|
||||
LanguageLocals.getDisplayLanguage(
|
||||
locale.languageCode,
|
||||
);
|
||||
return Text(
|
||||
"${isoCodeName.name} (${isoCodeName.nativeName})",
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
AdaptiveSelectTile<String>(
|
||||
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(
|
||||
(country) => DropdownMenuEntry(
|
||||
(country) => DropdownMenuItem(
|
||||
value: country.first,
|
||||
label: country.last,
|
||||
child: Text(country.last),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onSelected: (value) {
|
||||
if (value == null) return;
|
||||
preferences.setRecommendationMarket(
|
||||
value as String,
|
||||
);
|
||||
update?.call(() {});
|
||||
),
|
||||
],
|
||||
),
|
||||
SectionCardWithHeading(
|
||||
heading: context.l10n.appearance,
|
||||
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);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
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: [
|
||||
options: [
|
||||
DropdownMenuItem(
|
||||
value: CloseBehavior.close,
|
||||
child: Text(context.l10n.close),
|
||||
value: LayoutMode.adaptive,
|
||||
child: Text(context.l10n.adaptive),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: CloseBehavior.minimizeToTray,
|
||||
child: Text(context.l10n.minimize_to_tray),
|
||||
value: LayoutMode.compact,
|
||||
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) {
|
||||
if (value != null) {
|
||||
preferences.setCloseBehavior(value);
|
||||
update?.call(() {});
|
||||
preferences.setThemeMode(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(SpotubeIcons.tray),
|
||||
title: Text(context.l10n.show_tray_icon),
|
||||
value: preferences.showSystemTrayIcon,
|
||||
onChanged: preferences.setShowSystemTrayIcon,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
" ${context.l10n.about}",
|
||||
style: theme.textTheme.headlineSmall
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(
|
||||
SpotubeIcons.heart,
|
||||
color: Colors.pink,
|
||||
SectionCardWithHeading(
|
||||
heading: context.l10n.playback,
|
||||
children: [
|
||||
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(
|
||||
height: 50,
|
||||
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,
|
||||
SectionCardWithHeading(
|
||||
heading: context.l10n.about,
|
||||
children: [
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(
|
||||
SpotubeIcons.heart,
|
||||
color: Colors.pink,
|
||||
),
|
||||
title: SizedBox(
|
||||
height: 50,
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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)
|
||||
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");
|
||||
},
|
||||
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 {
|
||||
if (apSupportedPlatform) {
|
||||
return const Stream.empty();
|
||||
return Stream.value(false);
|
||||
} else {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
@ -215,7 +215,6 @@ class SpotubeAudioPlayer {
|
||||
|
||||
Future<void> pause() async {
|
||||
await _audioPlayer?.pause();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<void> resume() async {
|
||||
|
@ -13,6 +13,7 @@ ThemeData theme(Color seed, Brightness brightness) {
|
||||
horizontalTitleGap: 0,
|
||||
iconColor: scheme.onSurface,
|
||||
),
|
||||
appBarTheme: const AppBarTheme(surfaceTintColor: Colors.transparent),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
|
Loading…
Reference in New Issue
Block a user