Theme configuration

This commit is contained in:
Juan Gilsanz Polo
2024-02-23 01:26:22 +01:00
parent 57af5e926e
commit e21546693a
32 changed files with 1053 additions and 45 deletions

View File

@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
class ColorItem extends StatelessWidget {
final int index;
final int total;
final Color color;
final int numericValue;
final int? selectedValue;
final void Function(int) onChanged;
const ColorItem({
super.key,
required this.index,
required this.total,
required this.color,
required this.numericValue,
required this.selectedValue,
required this.onChanged
});
@override
Widget build(BuildContext context) {
return Padding(
padding: index == 0
? const EdgeInsets.only(top: 10, right: 10, bottom: 10)
: index == total-1
? const EdgeInsets.only(top: 10, bottom: 10, left: 10)
: const EdgeInsets.all(10),
child: Material(
borderRadius: BorderRadius.circular(50),
child: InkWell(
onTap: () => onChanged(numericValue),
borderRadius: BorderRadius.circular(50),
overlayColor: const MaterialStatePropertyAll(Colors.grey),
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(50)
),
child: AnimatedOpacity(
opacity: numericValue == selectedValue ? 1 : 0,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
child: Icon(
Icons.check,
size: 30,
color: color.computeLuminance() > 0.5 ? Colors.black : Colors.white,
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,156 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:linkdy/screens/settings/provider/customization.provider.dart';
import 'package:linkdy/screens/settings/ui/customization/color_item.dart';
import 'package:linkdy/screens/settings/ui/customization/theme_mode_button.dart';
import 'package:linkdy/utils/color_translation.dart';
import 'package:linkdy/widgets/custom_switch_list_tile.dart';
import 'package:linkdy/widgets/section_label.dart';
import 'package:linkdy/constants/colors.dart';
import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/i18n/strings.g.dart';
import 'package:linkdy/providers/app_status_provider.dart';
class Customization extends ConsumerWidget {
const Customization({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text(t.settings.customization.customization),
centerTitle: false,
),
body: SafeArea(
child: ListView(
children: [
SectionLabel(
label: t.settings.customization.theme,
padding: const EdgeInsets.only(top: 10, left: 16, right: 16, bottom: 5),
),
Column(
children: [
CustomSwitchListTile(
value: ref.watch(appStatusProvider).selectedTheme == SelectedTheme.system ? true : false,
onChanged: (value) => ref
.read(appStatusProvider.notifier)
.setTheme(value == true ? SelectedTheme.system : SelectedTheme.light),
title: t.settings.customization.systemDefined,
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ThemeModeButton(
icon: Icons.light_mode,
value: 1,
selected: ref.watch(appStatusProvider).selectedTheme.index,
label: t.settings.customization.light,
onChanged: (value) => ref.read(appStatusProvider.notifier).setTheme(SelectedTheme.values[value]),
disabled: ref.watch(appStatusProvider).selectedTheme == SelectedTheme.system ? true : false,
),
ThemeModeButton(
icon: Icons.dark_mode,
value: 2,
selected: ref.watch(appStatusProvider).selectedTheme.index,
label: t.settings.customization.dark,
onChanged: (value) => ref.read(appStatusProvider.notifier).setTheme(SelectedTheme.values[value]),
disabled: ref.watch(appStatusProvider).selectedTheme == SelectedTheme.system ? true : false,
),
],
),
],
),
SectionLabel(
label: t.settings.customization.color,
padding: const EdgeInsets.only(top: 45, left: 16, right: 16, bottom: 5),
),
if (ref.watch(appStatusProvider).supportsDynamicTheme)
CustomSwitchListTile(
value: ref.watch(appStatusProvider).useDynamicTheme,
onChanged: ref.read(appStatusProvider.notifier).setUseDynamicTheme,
title: t.settings.customization.useDynamicTheme,
),
if (ref.watch(appStatusProvider).supportsDynamicTheme == false ||
(ref.watch(appStatusProvider).supportsDynamicTheme == true &&
ref.watch(appStatusProvider).useDynamicTheme == false))
Padding(
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
child: Scrollbar(
controller: ref.watch(customizationProvider).scrollController,
thumbVisibility: Platform.isMacOS || Platform.isLinux || Platform.isWindows,
interactive: Platform.isMacOS || Platform.isLinux || Platform.isWindows,
thickness: Platform.isMacOS || Platform.isLinux || Platform.isWindows ? 8 : 0,
child: Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
height: 70,
child: ListView.builder(
controller: ref.watch(customizationProvider).scrollController,
scrollDirection: Axis.horizontal,
itemCount: colors.length,
padding: const EdgeInsets.all(0),
itemBuilder: (context, index) {
if (index == 0) {
return Row(
children: [
ColorItem(
index: index,
total: colors.length,
color: colors[index],
numericValue: index,
selectedValue: ref.watch(appStatusProvider).selectedColor,
onChanged: ref.read(appStatusProvider.notifier).setSelectedColor,
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
width: 1,
height: 60,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(1),
),
),
],
);
} else {
return ColorItem(
index: index,
total: colors.length,
color: colors[index],
numericValue: index,
selectedValue: ref.watch(appStatusProvider).selectedColor,
onChanged: ref.read(appStatusProvider.notifier).setSelectedColor,
);
}
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
colorTranslation(ref.watch(appStatusProvider).selectedColor),
style: TextStyle(color: Theme.of(context).listTileTheme.iconColor, fontSize: 16),
),
),
],
),
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
class ThemeModeButton extends StatelessWidget {
final IconData icon;
final int value;
final int selected;
final String label;
final void Function(int) onChanged;
final bool? disabled;
const ThemeModeButton({
Key? key,
required this.icon,
required this.value,
required this.selected,
required this.label,
required this.onChanged,
this.disabled
}) : super(key: key);
@override
Widget build(BuildContext context) {
final Color greyBackgroundColor = Theme.of(context).brightness == Brightness.light
?const Color.fromRGBO(200, 200, 200, 1)
:const Color.fromRGBO(50, 50, 50, 1);
final Color greyIconColor = Theme.of(context).brightness == Brightness.light
? const Color.fromRGBO(130, 130, 130, 1)
: const Color.fromRGBO(100, 100, 100, 1);
return ElevatedButton(
onPressed: disabled == null || disabled == false
? () => onChanged(value)
: null,
style: ButtonStyle(
elevation: MaterialStateProperty.all(0),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
)
),
backgroundColor: MaterialStateProperty.all(
value == selected
? disabled == null || disabled == false
? Theme.of(context).colorScheme.primary
: greyBackgroundColor
: disabled == null || disabled == false
? Theme.of(context).colorScheme.surfaceVariant
: greyBackgroundColor,
)
),
child: AnimatedContainer(
width: 118,
height: 150,
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 200),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
icon,
color: value == selected
? disabled == null || disabled == false
? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white
: greyIconColor
: disabled == null || disabled == false
? Theme.of(context).colorScheme.primary
: greyIconColor,
size: 30,
),
Text(
label,
style: TextStyle(
color: value == selected
? disabled == null || disabled == false
? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white
: greyIconColor
: disabled == null || disabled == false
? Theme.of(context).colorScheme.primary
: greyIconColor,
fontSize: 18
),
)
],
),
),
);
}
}

View File

@@ -1,6 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:linkdy/providers/router_provider.dart';
import 'package:linkdy/router/paths.dart';
import 'package:linkdy/widgets/section_label.dart';
import 'package:linkdy/widgets/custom_list_tile.dart';
import 'package:linkdy/providers/app_info_provider.dart';
import 'package:linkdy/utils/open_url.dart';
import 'package:linkdy/constants/strings.dart';
import 'package:linkdy/constants/urls.dart';
import 'package:linkdy/i18n/strings.g.dart';
import 'package:linkdy/providers/api_client_provider.dart';
@@ -15,6 +25,18 @@ class Settings extends ConsumerWidget {
),
body: ListView(
children: [
SectionLabel(label: t.settings.appSettings),
CustomListTile(
icon: Icons.palette_rounded,
title: t.settings.customization.customization,
subtitle: t.settings.customizationDescription,
onTap: () => ref.read(routerProvider).push(RoutesPaths.customization),
),
CustomListTile(
icon: Icons.settings_rounded,
title: t.settings.generalSettings,
subtitle: t.settings.generalSettingsDescription,
),
FilledButton.icon(
onPressed: () => ref.read(apiClientProviderProvider.notifier).disconnectApiClient(),
icon: const Icon(Icons.clear_rounded),
@@ -22,6 +44,44 @@ class Settings extends ConsumerWidget {
t.settings.disconnectFromServer,
),
),
SectionLabel(label: t.settings.aboutApp),
CustomListTile(
title: t.settings.appVersion,
subtitle: ref.watch(appInfoProvider).version,
),
CustomListTile(
title: t.settings.createdBy,
subtitle: Strings.createdBy,
),
Padding(
padding: const EdgeInsets.all(15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// if (Platform.isAndroid)
// IconButton(
// onPressed: () => openUrl(Urls.playStore),
// icon: SvgPicture.asset(
// 'assets/resources/google-play.svg',
// color: Theme.of(context).colorScheme.onSurfaceVariant,
// width: 30,
// height: 30,
// ),
// tooltip: t.settings.visitGooglePlay,
// ),
IconButton(
onPressed: () => openUrl(Urls.gitHubRepo),
icon: SvgPicture.asset(
'assets/resources/github.svg',
color: Theme.of(context).colorScheme.onSurfaceVariant,
width: 30,
height: 30,
),
tooltip: t.settings.visitGitHubRepo,
),
],
),
),
],
),
);