feat: better language picker, adaptive select tile and settings section contrast

This commit is contained in:
Kingkor Roy Tirtho 2023-05-01 00:44:35 +06:00
parent 12915f3e5a
commit 6430a25870
6 changed files with 753 additions and 362 deletions

View 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");
}
}
}

View 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),
),
),
],
);
}
}

View 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);
},
),
],
);
},
);
},
);
}
}

View File

@ -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");
},
), ),
], ],
), ),

View File

@ -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 {

View File

@ -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),