diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 6fbcb34..a3464db 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 2 /// Strings: 350 (175 per locale) /// -/// Built on 2024-02-28 at 18:58 UTC +/// Built on 2024-02-28 at 19:46 UTC // coverage:ignore-file // ignore_for_file: type=lint diff --git a/lib/screens/settings/model/customization.model.dart b/lib/screens/settings/model/customization.model.dart index ff3a593..70b942a 100644 --- a/lib/screens/settings/model/customization.model.dart +++ b/lib/screens/settings/model/customization.model.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; class CustomizationModel { ScrollController? scrollController; + int? selectedScreen; CustomizationModel({ this.scrollController, + this.selectedScreen, }); } diff --git a/lib/screens/settings/model/settings.model.dart b/lib/screens/settings/model/settings.model.dart new file mode 100644 index 0000000..e806cf6 --- /dev/null +++ b/lib/screens/settings/model/settings.model.dart @@ -0,0 +1,7 @@ +class SettingsModel { + int? selectedScreen; + + SettingsModel({ + this.selectedScreen, + }); +} diff --git a/lib/screens/settings/provider/settings.provider.dart b/lib/screens/settings/provider/settings.provider.dart new file mode 100644 index 0000000..9842f32 --- /dev/null +++ b/lib/screens/settings/provider/settings.provider.dart @@ -0,0 +1,18 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'package:linkdy/screens/settings/model/settings.model.dart'; + +part 'settings.provider.g.dart'; + +@riverpod +class Settings extends _$Settings { + @override + SettingsModel build() { + return SettingsModel(); + } + + void setSelectedScreen(int screen) { + state.selectedScreen = screen; + ref.notifyListeners(); + } +} diff --git a/lib/screens/settings/provider/settings.provider.g.dart b/lib/screens/settings/provider/settings.provider.g.dart new file mode 100644 index 0000000..4a2fc62 --- /dev/null +++ b/lib/screens/settings/provider/settings.provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'settings.provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$settingsHash() => r'709696bfa70c1fa702132beb428561e073c7a620'; + +/// See also [Settings]. +@ProviderFor(Settings) +final settingsProvider = + AutoDisposeNotifierProvider.internal( + Settings.new, + name: r'settingsProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$settingsHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$Settings = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/screens/settings/ui/settings.dart b/lib/screens/settings/ui/settings.dart index e5554ba..1df3fb4 100644 --- a/lib/screens/settings/ui/settings.dart +++ b/lib/screens/settings/ui/settings.dart @@ -2,21 +2,50 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_split_view/flutter_split_view.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:linkdy/providers/router.provider.dart'; -import 'package:linkdy/router/paths.dart'; +import 'package:linkdy/screens/settings/ui/customization/customization.dart'; +import 'package:linkdy/screens/settings/provider/settings.provider.dart'; +import 'package:linkdy/screens/settings/ui/general_settings/general_settings.dart'; import 'package:linkdy/widgets/section_label.dart'; import 'package:linkdy/widgets/custom_list_tile.dart'; +import 'package:linkdy/widgets/custom_settings_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/config/sizes.dart'; import 'package:linkdy/constants/urls.dart'; import 'package:linkdy/i18n/strings.g.dart'; class SettingsScreen extends ConsumerWidget { - const SettingsScreen({Key? key}) : super(key: key); + const SettingsScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > Sizes.tabletBreakpoint) { + return const SplitView.material( + hideDivider: true, + flexWidth: FlexWidth(mainViewFlexWidth: 1, secondaryViewFlexWidth: 2), + child: _List(tabletView: true), + ); + } else { + return const _List(tabletView: false); + } + }, + ); + } +} + +class _List extends ConsumerWidget { + final bool tabletView; + + const _List({ + required this.tabletView, + }); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,17 +56,21 @@ class SettingsScreen extends ConsumerWidget { body: ListView( children: [ SectionLabel(label: t.settings.appSettings), - CustomListTile( + _SettingsTile( icon: Icons.palette_rounded, title: t.settings.customization.customization, subtitle: t.settings.customization.customizationDescription, - onTap: () => ref.read(routerProvider).push(RoutesPaths.customization), + thisItem: 0, + twoColumns: tabletView, + screenToNavigate: const Customization(), ), - CustomListTile( + _SettingsTile( icon: Icons.settings_rounded, title: t.settings.generalSettings.generalSettings, subtitle: t.settings.generalSettings.generalSettingsDescription, - onTap: () => ref.read(routerProvider).push(RoutesPaths.generalSettings), + thisItem: 1, + screenToNavigate: const GeneralSettings(), + twoColumns: tabletView, ), SectionLabel(label: t.settings.aboutApp), CustomListTile( @@ -82,3 +115,51 @@ class SettingsScreen extends ConsumerWidget { ); } } + +class _SettingsTile extends ConsumerWidget { + final String title; + final String subtitle; + final IconData icon; + final Widget? trailing; + final Widget screenToNavigate; + final int thisItem; + final bool twoColumns; + + const _SettingsTile({ + required this.title, + required this.subtitle, + required this.icon, + this.trailing, + required this.screenToNavigate, + required this.thisItem, + required this.twoColumns, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (twoColumns) { + return CustomSettingsTile( + title: title, + subtitle: subtitle, + icon: icon, + trailing: trailing, + thisItem: thisItem, + selectedItem: ref.watch(settingsProvider).selectedScreen, + onTap: () { + ref.read(settingsProvider.notifier).setSelectedScreen(thisItem); + SplitView.of(context).setSecondary(screenToNavigate); + }, + ); + } else { + return CustomListTile( + title: title, + subtitle: subtitle, + icon: icon, + trailing: trailing, + onTap: () { + Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) => screenToNavigate)); + }, + ); + } + } +} diff --git a/lib/widgets/custom_settings_tile.dart b/lib/widgets/custom_settings_tile.dart new file mode 100644 index 0000000..76efb21 --- /dev/null +++ b/lib/widgets/custom_settings_tile.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; + +class CustomSettingsTile extends StatelessWidget { + final String title; + final String? subtitle; + final Widget? subtitleWidget; + final void Function()? onTap; + final IconData? icon; + final Widget? trailing; + final EdgeInsets? padding; + final int thisItem; + final int? selectedItem; + + const CustomSettingsTile({ + super.key, + required this.title, + this.subtitle, + this.subtitleWidget, + this.onTap, + this.icon, + this.trailing, + this.padding, + required this.thisItem, + required this.selectedItem, + }); + + @override + Widget build(BuildContext context) { + Widget tileBody = Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + Icon( + icon, + size: 24, + color: Theme.of(context).listTileTheme.iconColor, + ), + const SizedBox(width: 16), + ], + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + if (subtitle != null || subtitleWidget != null) ...[ + const SizedBox(height: 5), + if (subtitle == null && subtitleWidget != null) subtitleWidget!, + if (subtitle != null && subtitleWidget == null) + Text( + subtitle!, + style: TextStyle( + color: Theme.of(context).listTileTheme.textColor, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ] + ], + ), + ) + ], + ), + ), + if (trailing != null) ...[const SizedBox(width: 10), trailing!] + ], + ); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(28), + child: InkWell( + borderRadius: BorderRadius.circular(28), + onTap: onTap, + child: Container( + width: double.maxFinite, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(28), + color: thisItem == selectedItem ? Theme.of(context).colorScheme.primaryContainer : null), + child: tileBody), + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index b449253..39c8dab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -443,6 +443,15 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + flutter_split_view: + dependency: "direct main" + description: + path: "." + ref: master-alt + resolved-ref: a6aa2419243cc0abccf9474ce340790d6c0ded6f + url: "https://github.com/JGeek00/flutter_split_view" + source: git + version: "0.1.2" flutter_svg: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c9dce13..522d2d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,10 @@ dependencies: webview_flutter: ^4.7.0 flutter_hooks: ^0.20.5 hooks_riverpod: ^2.4.10 + flutter_split_view: + git: + url: https://github.com/JGeek00/flutter_split_view + ref: master-alt dev_dependencies: build_runner: ^2.4.8