From 4cad01782cb690f41818981a129592b038ff1346 Mon Sep 17 00:00:00 2001 From: Tommy Chow Date: Sat, 26 Mar 2022 01:52:08 -0400 Subject: [PATCH] Convert settings to expansion tiles (#92) --- .../settings/sections/account_settings.dart | 13 +- .../settings/sections/chat_settings.dart | 303 +++++++++--------- .../settings/sections/general_settings.dart | 55 ++-- .../settings/sections/other_settings.dart | 13 +- .../settings/sections/video_settings.dart | 65 ++-- .../settings/widgets/profile_card.dart | 2 +- 6 files changed, 235 insertions(+), 216 deletions(-) diff --git a/lib/screens/settings/sections/account_settings.dart b/lib/screens/settings/sections/account_settings.dart index fad13994..0c967fff 100644 --- a/lib/screens/settings/sections/account_settings.dart +++ b/lib/screens/settings/sections/account_settings.dart @@ -4,7 +4,6 @@ import 'package:frosty/core/auth/auth_store.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; import 'package:frosty/screens/settings/widgets/blocked_users.dart'; import 'package:frosty/screens/settings/widgets/profile_card.dart'; -import 'package:frosty/widgets/section_header.dart'; import 'package:webview_flutter/webview_flutter.dart'; class AccountSettings extends StatelessWidget { @@ -20,10 +19,16 @@ class AccountSettings extends StatelessWidget { @override Widget build(BuildContext context) { return Observer( - builder: (context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, + builder: (context) => ExpansionTile( + leading: const Icon(Icons.manage_accounts), + title: const Text( + 'Account', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), children: [ - const SectionHeader('ACCOUNT'), ProfileCard(authStore: authStore), if (authStore.isLoggedIn) ...[ ListTile( diff --git a/lib/screens/settings/sections/chat_settings.dart b/lib/screens/settings/sections/chat_settings.dart index c4707854..dfecbf71 100644 --- a/lib/screens/settings/sections/chat_settings.dart +++ b/lib/screens/settings/sections/chat_settings.dart @@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; -import 'package:frosty/widgets/section_header.dart'; class ChatSettings extends StatefulWidget { final SettingsStore settingsStore; @@ -23,162 +22,166 @@ class _ChatSettingsState extends State { final settingsStore = widget.settingsStore; return Observer( - builder: (context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SectionHeader('CHAT'), - SwitchListTile.adaptive( - title: const Text('Show bottom bar'), - value: settingsStore.showBottomBar, - onChanged: (newValue) => settingsStore.showBottomBar = newValue, + builder: (context) => ExpansionTile( + leading: const Icon(Icons.chat), + title: const Text( + 'Chat', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + children: [ + SwitchListTile.adaptive( + title: const Text('Show bottom bar'), + value: settingsStore.showBottomBar, + onChanged: (newValue) => settingsStore.showBottomBar = newValue, + ), + SwitchListTile.adaptive( + title: const Text('Show deleted messages'), + value: settingsStore.showDeletedMessages, + onChanged: (newValue) => settingsStore.showDeletedMessages = newValue, + ), + SwitchListTile.adaptive( + isThreeLine: true, + title: const Text('Show zero-width emotes'), + subtitle: const Text('Makes "stacked" emotes from BetterTTV and 7TV visible in chat messages.'), + value: settingsStore.showZeroWidth, + onChanged: (newValue) => settingsStore.showZeroWidth = newValue, + ), + SwitchListTile.adaptive( + isThreeLine: true, + title: const Text('Use readable colors for chat names'), + subtitle: const Text('Adjusts the lightness value of overly bright/dark names in chat.'), + value: settingsStore.useReadableColors, + onChanged: (newValue) => settingsStore.useReadableColors = newValue, + ), + ListTile( + isThreeLine: true, + title: const Text('Message timestamps'), + subtitle: const Text('Displays timestamps for when a chat message was sent.'), + trailing: DropdownButton( + value: settingsStore.timestampType, + onChanged: (TimestampType? newTimestamp) => settingsStore.timestampType = newTimestamp!, + items: TimestampType.values + .map((TimestampType value) => DropdownMenuItem( + value: value, + child: Text(timestamps[value.index]), + )) + .toList(), ), - SwitchListTile.adaptive( - title: const Text('Show deleted messages'), - value: settingsStore.showDeletedMessages, - onChanged: (newValue) => settingsStore.showDeletedMessages = newValue, - ), - SwitchListTile.adaptive( - isThreeLine: true, - title: const Text('Show zero-width emotes'), - subtitle: const Text('Makes "stacked" emotes from BetterTTV and 7TV visible in chat messages.'), - value: settingsStore.showZeroWidth, - onChanged: (newValue) => settingsStore.showZeroWidth = newValue, - ), - SwitchListTile.adaptive( - isThreeLine: true, - title: const Text('Use readable colors for chat names'), - subtitle: const Text('Adjusts the lightness value of overly bright/dark names in chat.'), - value: settingsStore.useReadableColors, - onChanged: (newValue) => settingsStore.useReadableColors = newValue, - ), - ListTile( - isThreeLine: true, - title: const Text('Message timestamps'), - subtitle: const Text('Displays timestamps for when a chat message was sent.'), - trailing: DropdownButton( - value: settingsStore.timestampType, - onChanged: (TimestampType? newTimestamp) => settingsStore.timestampType = newTimestamp!, - items: TimestampType.values - .map((TimestampType value) => DropdownMenuItem( - value: value, - child: Text(timestamps[value.index]), - )) - .toList(), + ), + const SizedBox(height: 10.0), + ExpansionTile( + title: const Text('Message appearance'), + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.all(20.0), + margin: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.deepPurple), + borderRadius: BorderRadius.circular(15.0), + ), + child: DefaultTextStyle( + style: DefaultTextStyle.of(context).style.copyWith(fontSize: settingsStore.fontSize), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: CachedNetworkImage( + imageUrl: 'https://static-cdn.jtvnw.net/badges/v1/bbbe0db0-a598-423e-86d0-f9fb98ca1933/3', + placeholder: (context, url) => const SizedBox(), + height: settingsStore.badgeHeight, + ), + ), + const TextSpan(text: ' Badge and emote preview '), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: CachedNetworkImage( + imageUrl: 'https://static-cdn.jtvnw.net/emoticons/v2/425618/default/dark/3.0', + placeholder: (context, url) => const SizedBox(), + height: settingsStore.emoteHeight, + ), + ), + ], + ), + textScaleFactor: settingsStore.messageScale, + ), + SizedBox(height: settingsStore.messageSpacing), + Text( + 'Hello! Here\'s a text preview.', + textScaleFactor: settingsStore.messageScale, + ), + SizedBox(height: settingsStore.messageSpacing), + Text( + 'And another for spacing without an emote!', + textScaleFactor: settingsStore.messageScale, + ), + ], + ), + ), ), - ), - const SizedBox(height: 10.0), - ExpansionTile( - title: const Text('Message appearance'), - children: [ - Container( - width: double.infinity, - padding: const EdgeInsets.all(20.0), - margin: const EdgeInsets.all(10.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.deepPurple), - borderRadius: BorderRadius.circular(15.0), - ), - child: DefaultTextStyle( - style: DefaultTextStyle.of(context).style.copyWith(fontSize: settingsStore.fontSize), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text.rich( - TextSpan( - children: [ - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: CachedNetworkImage( - imageUrl: 'https://static-cdn.jtvnw.net/badges/v1/bbbe0db0-a598-423e-86d0-f9fb98ca1933/3', - placeholder: (context, url) => const SizedBox(), - height: settingsStore.badgeHeight, - ), - ), - const TextSpan(text: ' Badge and emote preview '), - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: CachedNetworkImage( - imageUrl: 'https://static-cdn.jtvnw.net/emoticons/v2/425618/default/dark/3.0', - placeholder: (context, url) => const SizedBox(), - height: settingsStore.emoteHeight, - ), - ), - ], - ), - textScaleFactor: settingsStore.messageScale, - ), - SizedBox(height: settingsStore.messageSpacing), - Text( - 'Hello! Here\'s a text preview.', - textScaleFactor: settingsStore.messageScale, - ), - SizedBox(height: settingsStore.messageSpacing), - Text( - 'And another for spacing without an emote!', - textScaleFactor: settingsStore.messageScale, - ), - ], - ), - ), + ListTile( + isThreeLine: true, + title: Text('Message scale: ${settingsStore.messageScale.toStringAsFixed(1)}x'), + subtitle: Slider.adaptive( + value: settingsStore.messageScale, + min: 0.5, + max: 2.0, + divisions: 15, + onChanged: (newValue) => settingsStore.messageScale = newValue, ), - ListTile( - isThreeLine: true, - title: Text('Message scale: ${settingsStore.messageScale.toStringAsFixed(1)}x'), - subtitle: Slider.adaptive( - value: settingsStore.messageScale, - min: 0.5, - max: 2.0, - divisions: 15, - onChanged: (newValue) => settingsStore.messageScale = newValue, - ), + ), + ListTile( + title: Text('Message spacing: ${settingsStore.messageSpacing.toStringAsFixed(0)}'), + subtitle: Slider.adaptive( + value: settingsStore.messageSpacing, + min: 0.0, + max: 30.0, + divisions: 30, + onChanged: (newValue) => settingsStore.messageSpacing = newValue, ), - ListTile( - title: Text('Message spacing: ${settingsStore.messageSpacing.toStringAsFixed(0)}'), - subtitle: Slider.adaptive( - value: settingsStore.messageSpacing, - min: 0.0, - max: 30.0, - divisions: 30, - onChanged: (newValue) => settingsStore.messageSpacing = newValue, - ), + ), + ListTile( + isThreeLine: true, + title: Text('Font size: ${settingsStore.fontSize.toInt()}'), + subtitle: Slider.adaptive( + value: settingsStore.fontSize, + min: 5, + max: 20, + divisions: 15, + onChanged: (newValue) => settingsStore.fontSize = newValue, ), - ListTile( - isThreeLine: true, - title: Text('Font size: ${settingsStore.fontSize.toInt()}'), - subtitle: Slider.adaptive( - value: settingsStore.fontSize, - min: 5, - max: 20, - divisions: 15, - onChanged: (newValue) => settingsStore.fontSize = newValue, - ), + ), + ListTile( + title: Text('Badge height: ${settingsStore.badgeHeight.toStringAsFixed(0)}'), + subtitle: Slider.adaptive( + value: settingsStore.badgeHeight, + min: 10.0, + max: 50.0, + divisions: 8, + onChanged: (newValue) => settingsStore.badgeHeight = newValue, ), - ListTile( - title: Text('Badge height: ${settingsStore.badgeHeight.toStringAsFixed(0)}'), - subtitle: Slider.adaptive( - value: settingsStore.badgeHeight, - min: 10.0, - max: 50.0, - divisions: 8, - onChanged: (newValue) => settingsStore.badgeHeight = newValue, - ), + ), + ListTile( + title: Text('Emote height: ${settingsStore.emoteHeight.toStringAsFixed(0)}'), + subtitle: Slider.adaptive( + value: settingsStore.emoteHeight, + min: 10.0, + max: 50.0, + divisions: 8, + onChanged: (newValue) => settingsStore.emoteHeight = newValue, ), - ListTile( - title: Text('Emote height: ${settingsStore.emoteHeight.toStringAsFixed(0)}'), - subtitle: Slider.adaptive( - value: settingsStore.emoteHeight, - min: 10.0, - max: 50.0, - divisions: 8, - onChanged: (newValue) => settingsStore.emoteHeight = newValue, - ), - ), - ], - ), - ], - ); - }, + ), + ], + ), + ], + ), ); } } diff --git a/lib/screens/settings/sections/general_settings.dart b/lib/screens/settings/sections/general_settings.dart index 942413a6..3ae34300 100644 --- a/lib/screens/settings/sections/general_settings.dart +++ b/lib/screens/settings/sections/general_settings.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; -import 'package:frosty/widgets/section_header.dart'; class GeneralSettings extends StatelessWidget { final SettingsStore settingsStore; @@ -13,32 +12,36 @@ class GeneralSettings extends StatelessWidget { const themes = ['System', 'Light', 'Dark', 'Black']; return Observer( - builder: (context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SectionHeader('GENERAL'), - ListTile( - title: const Text('Theme'), - trailing: DropdownButton( - value: settingsStore.themeType, - onChanged: (ThemeType? newTheme) => settingsStore.themeType = newTheme!, - items: ThemeType.values - .map((ThemeType value) => DropdownMenuItem( - value: value, - child: Text(themes[value.index]), - )) - .toList(), - ), + builder: (context) => ExpansionTile( + leading: const Icon(Icons.app_settings_alt), + title: const Text( + 'General', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + children: [ + ListTile( + title: const Text('Theme'), + trailing: DropdownButton( + value: settingsStore.themeType, + onChanged: (ThemeType? newTheme) => settingsStore.themeType = newTheme!, + items: ThemeType.values + .map((ThemeType value) => DropdownMenuItem( + value: value, + child: Text(themes[value.index]), + )) + .toList(), ), - SwitchListTile.adaptive( - title: const Text('Show stream uptime on thumbnails'), - value: settingsStore.showThumbnailUptime, - onChanged: (newValue) => settingsStore.showThumbnailUptime = newValue, - ), - ], - ); - }, + ), + SwitchListTile.adaptive( + title: const Text('Show stream uptime on thumbnails'), + value: settingsStore.showThumbnailUptime, + onChanged: (newValue) => settingsStore.showThumbnailUptime = newValue, + ), + ], + ), ); } } diff --git a/lib/screens/settings/sections/other_settings.dart b/lib/screens/settings/sections/other_settings.dart index bf746c63..007985bc 100644 --- a/lib/screens/settings/sections/other_settings.dart +++ b/lib/screens/settings/sections/other_settings.dart @@ -3,7 +3,6 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/constants/constants.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; -import 'package:frosty/widgets/section_header.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -16,10 +15,16 @@ class OtherSettings extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return ExpansionTile( + leading: const Icon(Icons.miscellaneous_services), + title: const Text( + 'Other', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), children: [ - const SectionHeader('OTHER'), ListTile( leading: const Icon(Icons.info), title: const Text('About Frosty'), diff --git a/lib/screens/settings/sections/video_settings.dart b/lib/screens/settings/sections/video_settings.dart index 1c5a5e4d..f65ee14a 100644 --- a/lib/screens/settings/sections/video_settings.dart +++ b/lib/screens/settings/sections/video_settings.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; -import 'package:frosty/widgets/section_header.dart'; class VideoSettings extends StatelessWidget { final SettingsStore settingsStore; @@ -13,39 +12,43 @@ class VideoSettings extends StatelessWidget { @override Widget build(BuildContext context) { return Observer( - builder: (context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SectionHeader('VIDEO'), - SwitchListTile.adaptive( - title: const Text('Show video'), - value: settingsStore.showVideo, - onChanged: (newValue) => settingsStore.showVideo = newValue, - ), + builder: (context) => ExpansionTile( + leading: const Icon(Icons.video_settings), + title: const Text( + 'Video', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + children: [ + SwitchListTile.adaptive( + title: const Text('Show video'), + value: settingsStore.showVideo, + onChanged: (newValue) => settingsStore.showVideo = newValue, + ), + SwitchListTile.adaptive( + isThreeLine: true, + title: const Text('Use custom video overlay'), + subtitle: const Text('Replaces the default video overlay with a mobile-friendly version.'), + value: settingsStore.showOverlay, + onChanged: settingsStore.showVideo ? (newValue) => settingsStore.showOverlay = newValue : null, + ), + if (Platform.isIOS) SwitchListTile.adaptive( isThreeLine: true, - title: const Text('Use custom video overlay'), - subtitle: const Text('Replaces the default video overlay with a mobile-friendly version.'), - value: settingsStore.showOverlay, - onChanged: settingsStore.showVideo ? (newValue) => settingsStore.showOverlay = newValue : null, + title: const Text('Picture-in-picture button (experimental)'), + subtitle: const Text('Adds a button to enter PiP mode on the bottom right of the overlay. May cause freezes/crashes.'), + value: settingsStore.pictureInPicture, + onChanged: settingsStore.showVideo && settingsStore.showOverlay ? (newValue) => settingsStore.pictureInPicture = newValue : null, ), - if (Platform.isIOS) - SwitchListTile.adaptive( - isThreeLine: true, - title: const Text('Picture-in-picture button (experimental)'), - subtitle: const Text('Adds a button to enter PiP mode on the bottom right of the overlay. May cause freezes/crashes.'), - value: settingsStore.pictureInPicture, - onChanged: settingsStore.showVideo && settingsStore.showOverlay ? (newValue) => settingsStore.pictureInPicture = newValue : null, - ), - SwitchListTile.adaptive( - title: const Text('Long-press player to toggle overlay'), - value: settingsStore.toggleableOverlay, - onChanged: settingsStore.showVideo ? (newValue) => settingsStore.toggleableOverlay = newValue : null, - ), - ], - ); - }, + SwitchListTile.adaptive( + title: const Text('Long-press player to toggle overlay'), + value: settingsStore.toggleableOverlay, + onChanged: settingsStore.showVideo ? (newValue) => settingsStore.toggleableOverlay = newValue : null, + ), + ], + ), ); } } diff --git a/lib/screens/settings/widgets/profile_card.dart b/lib/screens/settings/widgets/profile_card.dart index 58376278..67aeb571 100644 --- a/lib/screens/settings/widgets/profile_card.dart +++ b/lib/screens/settings/widgets/profile_card.dart @@ -105,7 +105,7 @@ class ProfileCard extends StatelessWidget { return ListTile( isThreeLine: true, leading: const Icon( - Icons.account_circle_outlined, + Icons.no_accounts, size: 40, ), title: const Text('Anonymous User'),