[imp]: Organise Settings page for better visibility

This commit is contained in:
nahnah
2026-02-10 14:31:09 +00:00
parent 57eec8b3d4
commit 3b34302a4a
5 changed files with 601 additions and 311 deletions
+129
View File
@@ -0,0 +1,129 @@
import 'package:florid/providers/settings_provider.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../widgets/m_list.dart';
class AppearanceScreen extends StatelessWidget {
const AppearanceScreen({super.key});
@override
Widget build(BuildContext context) {
return Consumer<SettingsProvider>(
builder: (context, settings, _) {
return Scaffold(
appBar: AppBar(title: const Text('Appearance')),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Column(
spacing: 4,
children: [
MListHeader(title: 'Theme Mode'),
MRadioListView(
items: [
MRadioListItemData<ThemeMode>(
title: 'Follow system theme',
subtitle: '',
value: ThemeMode.system,
suffix: Icon(Symbols.settings_suggest),
),
MRadioListItemData<ThemeMode>(
title: 'Light theme',
subtitle: '',
value: ThemeMode.light,
suffix: Icon(Symbols.light_mode_rounded),
),
MRadioListItemData<ThemeMode>(
title: 'Dark theme',
subtitle: '',
value: ThemeMode.dark,
suffix: Icon(Symbols.dark_mode_rounded),
),
],
groupValue: settings.themeMode,
onChanged: (mode) {
settings.setThemeMode(mode);
},
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'Theme Style'),
MRadioListView(
items: [
MRadioListItemData<ThemeStyle>(
title: 'Material style',
subtitle: '',
value: ThemeStyle.material,
),
MRadioListItemData<ThemeStyle>(
title: 'Florid style',
subtitle: '',
suffix: Container(
margin: const EdgeInsets.only(right: 8.0),
child: Material(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(99.0),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 2.0,
),
child: Text(
'Beta',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSecondary,
),
),
),
),
),
value: ThemeStyle.florid,
),
],
groupValue: settings.themeStyle,
onChanged: (style) {
settings.setThemeStyle(style);
},
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.feedback),
title: 'Fedback on Florid theme',
subtitle:
'Help improve the Florid theme by providing feedback',
onTap: () {
// Keep the same URL used in Settings.
launchUrl(
Uri.parse(
'https://github.com/Nandanrmenon/florid/discussions/5',
),
);
},
suffix: Icon(Symbols.open_in_new),
),
],
),
],
),
],
),
),
),
);
},
);
}
}
+48 -310
View File
@@ -1,14 +1,10 @@
import 'dart:convert';
import 'dart:io';
import 'package:android_intent_plus/android_intent.dart';
import 'package:file_picker/file_picker.dart';
import 'package:florid/l10n/app_localizations.dart';
import 'package:florid/widgets/m_list.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as p;
@@ -18,9 +14,11 @@ import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/app_provider.dart';
import '../providers/download_provider.dart';
import '../providers/settings_provider.dart';
import '../screens/appearance_screen.dart';
import '../screens/repositories_screen.dart';
import '../screens/troubleshooting_screen.dart';
import '../screens/update_settings_screen.dart';
import '../services/fdroid_api_service.dart';
import '../services/update_check_service.dart';
@@ -34,17 +32,12 @@ class SettingsScreen extends StatefulWidget {
enum _FavoritesImportAction { merge, replace }
class _SettingsScreenState extends State<SettingsScreen> {
static const MethodChannel _batteryChannel = MethodChannel(
'florid/battery_optimizations',
);
String _appVersion = '';
bool? _isIgnoringBatteryOptimizations;
@override
void initState() {
super.initState();
_loadVersion();
_loadBatteryOptimizationStatus();
}
Future<void> _loadVersion() async {
@@ -55,70 +48,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
});
}
Future<void> _loadBatteryOptimizationStatus() async {
if (!Platform.isAndroid) return;
try {
final isIgnoring = await _batteryChannel.invokeMethod<bool>(
'isIgnoringBatteryOptimizations',
);
if (!mounted) return;
setState(() {
_isIgnoringBatteryOptimizations = isIgnoring ?? false;
});
} on PlatformException {
if (!mounted) return;
setState(() {
_isIgnoringBatteryOptimizations = null;
});
}
}
Future<void> _clearRepoCache(BuildContext context) async {
final api = context.read<FDroidApiService>();
await api.clearRepositoryCache();
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Repository cache cleared')));
}
Future<void> _clearImageCache(BuildContext context) async {
await DefaultCacheManager().emptyCache();
imageCache.clear();
imageCache.clearLiveImages();
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Image cache cleared')));
}
Future<void> _clearApkDownloads(BuildContext context) async {
final deleted = await context.read<DownloadProvider>().clearAllDownloads();
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
deleted > 0
? 'Deleted $deleted APK file${deleted == 1 ? '' : 's'}'
: 'No APK downloads to delete',
),
),
);
}
Future<void> _requestDisableBatteryOptimizations() async {
if (!Platform.isAndroid) return;
final info = await PackageInfo.fromPlatform();
final intent = AndroidIntent(
action: 'android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS',
data: 'package:${info.packageName}',
);
await intent.launch();
await Future.delayed(const Duration(seconds: 1));
await _loadBatteryOptimizationStatus();
}
Future<void> _exportFavorites(BuildContext context) async {
final appProvider = context.read<AppProvider>();
final favorites = appProvider.favoritePackages.toList()..sort();
@@ -375,96 +304,27 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column(
spacing: 4,
children: [
MListHeader(title: 'Theme Mode'),
MRadioListView(
items: [
MRadioListItemData<ThemeMode>(
title: 'Follow system theme',
subtitle: '',
value: ThemeMode.system,
),
MRadioListItemData<ThemeMode>(
title: 'Light theme',
subtitle: '',
value: ThemeMode.light,
),
MRadioListItemData<ThemeMode>(
title: 'Dark theme',
subtitle: '',
value: ThemeMode.dark,
),
],
groupValue: settings.themeMode,
onChanged: (mode) {
settings.setThemeMode(mode);
},
),
MListHeader(title: 'Theme Style'),
MRadioListView(
items: [
MRadioListItemData<ThemeStyle>(
title: 'Material style',
subtitle: '',
value: ThemeStyle.material,
),
MRadioListItemData<ThemeStyle>(
title: 'Florid style',
subtitle: '',
suffix: Container(
margin: const EdgeInsets.only(right: 8.0),
child: Material(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(99.0),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 2.0,
),
child: Text(
'Beta',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSecondary,
),
),
),
),
),
value: ThemeStyle.florid,
),
],
groupValue: settings.themeStyle,
onChanged: (style) {
settings.setThemeStyle(style);
},
MListHeader(
title: 'General Settings',
icon: Symbols.settings,
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.feedback),
title: 'Fedback on Florid theme',
subtitle:
'Help improve the Florid theme by providing feedback',
leading: Icon(Symbols.palette),
title: 'Appearance',
subtitle: 'Theme mode and style',
onTap: () {
launchUrl(
Uri.parse(
'https://github.com/Nandanrmenon/florid/discussions/5',
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const AppearanceScreen(),
),
);
},
suffix: Icon(Symbols.open_in_new),
suffix: Icon(Symbols.chevron_right),
),
],
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'General Settings'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.language),
title: 'App content language',
@@ -489,187 +349,65 @@ class _SettingsScreenState extends State<SettingsScreen> {
subtitle: 'Add or remove F-Droid repositories',
suffix: Icon(Symbols.chevron_right),
),
MListItemData(
leading: Icon(Symbols.update),
title: 'Update settings',
subtitle: 'Manage background updates',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const UpdateSettingsScreen(),
),
);
},
suffix: Icon(Symbols.chevron_right),
),
],
),
],
),
Column(
spacing: 4,
spacing: 4.0,
children: [
MListHeader(title: 'Favourites'),
MListHeader(
title: 'Miscellaneous',
icon: Symbols.more_horiz,
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.upload),
leading: Icon(Symbols.file_upload),
title: 'Export favourites',
subtitle: 'Save a JSON file to Downloads',
onTap: () => _exportFavorites(context),
),
MListItemData(
leading: Icon(Symbols.download),
leading: Icon(Symbols.file_download),
title: 'Import favourites',
subtitle: 'Import favourites from a JSON file',
onTap: () => _importFavorites(context),
),
],
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'Background updates'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.notifications),
title: 'Check for updates in background',
subtitle: 'Notify when updates are available',
onTap: () async {
await settings.setBackgroundUpdatesEnabled(
!settings.backgroundUpdatesEnabled,
leading: Icon(Symbols.build),
title: 'Troubleshooting',
subtitle: 'Storage, cache, and downloads',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const TroubleshootingScreen(),
),
);
await UpdateCheckService.scheduleFromPrefs();
},
suffix: Switch(
value: settings.backgroundUpdatesEnabled,
onChanged: (value) async {
await settings.setBackgroundUpdatesEnabled(
value,
);
await UpdateCheckService.scheduleFromPrefs();
},
),
),
MListItemData(
leading: Icon(Symbols.network_check),
title: 'Update network',
subtitle: _updateNetworkPolicyLabel(
settings.updateNetworkPolicy,
),
onTap: () => _showUpdateNetworkPolicyDialog(
context,
settings,
),
suffix: Icon(Symbols.chevron_right),
),
MListItemData(
leading: Icon(Symbols.schedule),
title: 'Update interval',
subtitle: _updateIntervalLabel(
settings.updateIntervalHours,
),
onTap: () =>
_showUpdateIntervalDialog(context, settings),
suffix: Icon(Symbols.chevron_right),
),
if (_isIgnoringBatteryOptimizations == false)
MListItemData(
selected: true,
leading: Icon(
Symbols.battery_saver,
fill: 1,
color: Theme.of(context).colorScheme.error,
),
title: 'Disable battery optimization',
subtitle:
'Allow background checks to run reliably',
onTap: _requestDisableBatteryOptimizations,
),
if (kDebugMode)
MListItemData(
leading: Icon(Symbols.bolt),
title: 'Run debug check in 10s',
subtitle:
'Shows a test notification and runs after 10s',
onTap: () async {
await UpdateCheckService.showDebugNotificationNow(
'Debug check scheduled',
);
await UpdateCheckService.runDebugInApp();
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Debug update check will run in 10 seconds',
),
),
);
},
),
],
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'Downloads & Storage'),
MListView(
items: [
MListItemData(
title: 'Auto-install after download',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Install APKs automatically once download finishes',
suffix: Switch(
value: settings.autoInstallApk,
onChanged: (value) {
settings.setAutoInstallApk(value);
},
),
),
MListItemData(
title: 'Delete APK after install',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Remove installer files after successful installation',
suffix: Switch(
value: settings.autoDeleteApk,
onChanged: (value) {
settings.setAutoDeleteApk(value);
},
),
),
],
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.cleaning_services),
title: 'Clear repository cache',
onTap: () {
_clearRepoCache(context);
},
subtitle:
'Refresh app list and metadata on next load',
),
MListItemData(
leading: Icon(Symbols.delete_sweep),
title: 'Clear APK downloads',
onTap: () {
_clearApkDownloads(context);
},
subtitle:
'Remove downloaded installer files from storage',
),
MListItemData(
leading: Icon(Symbols.image_not_supported),
title: 'Clear image cache',
onTap: () {
_clearImageCache(context);
},
subtitle: 'Remove cached icons and screenshots',
),
],
),
],
@@ -678,7 +416,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column(
spacing: 4,
children: [
MListHeader(title: 'About'),
MListHeader(title: 'About', icon: Symbols.android),
MListView(
items: [
MListItemData(
+133
View File
@@ -0,0 +1,133 @@
import 'package:florid/providers/download_provider.dart';
import 'package:florid/providers/settings_provider.dart';
import 'package:florid/services/fdroid_api_service.dart';
import 'package:florid/widgets/m_list.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
class TroubleshootingScreen extends StatelessWidget {
const TroubleshootingScreen({super.key});
Future<void> _clearRepoCache(BuildContext context) async {
final api = context.read<FDroidApiService>();
await api.clearRepositoryCache();
if (!context.mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Repository cache cleared')));
}
Future<void> _clearImageCache(BuildContext context) async {
await DefaultCacheManager().emptyCache();
imageCache.clear();
imageCache.clearLiveImages();
if (!context.mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Image cache cleared')));
}
Future<void> _clearApkDownloads(BuildContext context) async {
final deleted = await context.read<DownloadProvider>().clearAllDownloads();
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
deleted > 0
? 'Deleted $deleted APK file${deleted == 1 ? '' : 's'}'
: 'No APK downloads to delete',
),
),
);
}
@override
Widget build(BuildContext context) {
return Consumer<SettingsProvider>(
builder: (context, settings, _) {
return Scaffold(
appBar: AppBar(title: const Text('Troubleshooting')),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Column(
spacing: 4,
children: [
MListHeader(title: 'Downloads & Storage'),
MListView(
items: [
MListItemData(
title: 'Auto-install after download',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Install APKs automatically once download finishes',
suffix: Switch(
value: settings.autoInstallApk,
onChanged: (value) {
settings.setAutoInstallApk(value);
},
),
),
MListItemData(
title: 'Delete APK after install',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Remove installer files after successful installation',
suffix: Switch(
value: settings.autoDeleteApk,
onChanged: (value) {
settings.setAutoDeleteApk(value);
},
),
),
],
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.cleaning_services),
title: 'Clear repository cache',
onTap: () => _clearRepoCache(context),
subtitle:
'Refresh app list and metadata on next load',
),
MListItemData(
leading: Icon(Symbols.delete_sweep),
title: 'Clear APK downloads',
onTap: () => _clearApkDownloads(context),
subtitle:
'Remove downloaded installer files from storage',
),
MListItemData(
leading: Icon(Symbols.image_not_supported),
title: 'Clear image cache',
onTap: () => _clearImageCache(context),
subtitle: 'Remove cached icons and screenshots',
),
],
),
],
),
],
),
),
),
);
},
);
}
}
+290
View File
@@ -0,0 +1,290 @@
import 'dart:io';
import 'package:android_intent_plus/android_intent.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import '../providers/settings_provider.dart';
import '../services/update_check_service.dart';
import '../widgets/m_list.dart';
class UpdateSettingsScreen extends StatefulWidget {
const UpdateSettingsScreen({super.key});
@override
State<UpdateSettingsScreen> createState() => _UpdateSettingsScreenState();
}
class _UpdateSettingsScreenState extends State<UpdateSettingsScreen> {
static const MethodChannel _batteryChannel = MethodChannel(
'florid/battery_optimizations',
);
bool? _isIgnoringBatteryOptimizations;
@override
void initState() {
super.initState();
_loadBatteryOptimizationStatus();
}
Future<void> _loadBatteryOptimizationStatus() async {
if (!Platform.isAndroid) return;
try {
final isIgnoring = await _batteryChannel.invokeMethod<bool>(
'isIgnoringBatteryOptimizations',
);
if (!mounted) return;
setState(() {
_isIgnoringBatteryOptimizations = isIgnoring ?? false;
});
} on PlatformException {
if (!mounted) return;
setState(() {
_isIgnoringBatteryOptimizations = null;
});
}
}
Future<void> _requestDisableBatteryOptimizations() async {
if (!Platform.isAndroid) return;
final info = await PackageInfo.fromPlatform();
final intent = AndroidIntent(
action: 'android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS',
data: 'package:${info.packageName}',
);
await intent.launch();
await Future.delayed(const Duration(seconds: 1));
await _loadBatteryOptimizationStatus();
}
String _updateNetworkPolicyLabel(UpdateNetworkPolicy policy) {
switch (policy) {
case UpdateNetworkPolicy.wifiOnly:
return 'Wi-Fi only';
case UpdateNetworkPolicy.wifiAndCharging:
return 'Wi-Fi + charging';
case UpdateNetworkPolicy.any:
return 'Mobile data or Wi-Fi';
}
}
String _updateIntervalLabel(int hours) {
switch (hours) {
case 1:
return 'Every 1 hour';
case 2:
return 'Every 2 hours';
case 3:
return 'Every 3 hours';
case 6:
return 'Every 6 hours';
case 12:
return 'Every 12 hours';
case 24:
return 'Daily';
default:
return 'Every $hours hours';
}
}
Future<void> _showUpdateNetworkPolicyDialog(
BuildContext context,
SettingsProvider settings,
) async {
await showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Update network'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: UpdateNetworkPolicy.values
.map(
(policy) => RadioListTile<UpdateNetworkPolicy>(
value: policy,
groupValue: settings.updateNetworkPolicy,
title: Text(_updateNetworkPolicyLabel(policy)),
onChanged: (value) async {
if (value == null) return;
await settings.setUpdateNetworkPolicy(value);
await UpdateCheckService.scheduleFromPrefs();
if (!context.mounted) return;
Navigator.of(context).pop();
},
),
)
.toList(),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
],
),
);
}
Future<void> _showUpdateIntervalDialog(
BuildContext context,
SettingsProvider settings,
) async {
const intervals = [1, 2, 3, 6, 12, 24];
await showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Update interval'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: intervals
.map(
(hours) => RadioListTile<int>(
value: hours,
groupValue: settings.updateIntervalHours,
title: Text(_updateIntervalLabel(hours)),
onChanged: (value) async {
if (value == null) return;
await settings.setUpdateIntervalHours(value);
await UpdateCheckService.scheduleFromPrefs();
if (!context.mounted) return;
Navigator.of(context).pop();
},
),
)
.toList(),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Consumer<SettingsProvider>(
builder: (context, settings, _) {
return Scaffold(
appBar: AppBar(title: const Text('Update settings')),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Column(
spacing: 4,
children: [
MListHeader(title: 'Background updates'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.notifications),
title: 'Check for updates in background',
subtitle: 'Notify when updates are available',
onTap: () async {
await settings.setBackgroundUpdatesEnabled(
!settings.backgroundUpdatesEnabled,
);
await UpdateCheckService.scheduleFromPrefs();
},
suffix: Switch(
value: settings.backgroundUpdatesEnabled,
onChanged: (value) async {
await settings.setBackgroundUpdatesEnabled(
value,
);
await UpdateCheckService.scheduleFromPrefs();
},
),
),
MListItemData(
leading: Icon(Symbols.network_check),
title: 'Update network',
subtitle: _updateNetworkPolicyLabel(
settings.updateNetworkPolicy,
),
onTap: () => _showUpdateNetworkPolicyDialog(
context,
settings,
),
suffix: Icon(Symbols.chevron_right),
),
MListItemData(
leading: Icon(Symbols.schedule),
title: 'Update interval',
subtitle: _updateIntervalLabel(
settings.updateIntervalHours,
),
onTap: () =>
_showUpdateIntervalDialog(context, settings),
suffix: Icon(Symbols.chevron_right),
),
],
),
],
),
Column(
spacing: 4,
children: [
if (settings.backgroundUpdatesEnabled &&
_isIgnoringBatteryOptimizations == false)
MListHeader(title: 'Reliability'),
MListView(
items: [
if (settings.backgroundUpdatesEnabled &&
_isIgnoringBatteryOptimizations == false)
MListItemData(
leading: Icon(
Symbols.battery_saver,
fill: 1,
color: Theme.of(context).colorScheme.error,
),
title: 'Disable battery optimization',
subtitle:
'Allow background checks to run reliably',
onTap: _requestDisableBatteryOptimizations,
),
if (kDebugMode)
MListItemData(
leading: Icon(Symbols.bolt),
title: 'Run debug check in 10s',
subtitle:
'Shows a test notification and runs after 10s',
onTap: () async {
await UpdateCheckService.showDebugNotificationNow(
'Debug check scheduled',
);
await UpdateCheckService.runDebugInApp();
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Debug update check will run in 10 seconds',
),
),
);
},
),
],
),
],
),
],
),
),
),
);
},
);
}
}
+1 -1
View File
@@ -248,7 +248,7 @@ class MRadioListView<T> extends StatelessWidget {
color: Theme.of(context).colorScheme.surfaceContainer,
clipBehavior: Clip.antiAlias,
child: RadioListTile<T>(
contentPadding: EdgeInsets.only(left: 16.0, right: 4.0),
contentPadding: EdgeInsets.only(left: 16.0, right: 18.0),
title: Text(items[index].title),
subtitle: items[index].subtitle.isNotEmpty
? Text(items[index].subtitle)