2024-11-10 13:48:42 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
2024-11-10 14:14:27 +00:00
|
|
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
2024-11-10 13:48:42 +00:00
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:gap/gap.dart';
|
2024-12-08 09:14:31 +00:00
|
|
|
import 'package:go_router/go_router.dart';
|
2024-11-10 13:48:42 +00:00
|
|
|
import 'package:image_picker/image_picker.dart';
|
|
|
|
import 'package:material_symbols_icons/symbols.dart';
|
|
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import 'package:styled_widget/styled_widget.dart';
|
2024-11-10 14:14:27 +00:00
|
|
|
import 'package:surface/providers/sn_network.dart';
|
2024-11-10 13:48:42 +00:00
|
|
|
import 'package:surface/providers/theme.dart';
|
|
|
|
import 'package:surface/theme.dart';
|
2024-11-10 14:14:27 +00:00
|
|
|
import 'package:surface/widgets/dialog.dart';
|
2024-11-10 13:48:42 +00:00
|
|
|
|
|
|
|
class SettingsScreen extends StatefulWidget {
|
|
|
|
const SettingsScreen({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<SettingsScreen> createState() => _SettingsScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SettingsScreenState extends State<SettingsScreen> {
|
|
|
|
SharedPreferences? _prefs;
|
|
|
|
String _docBasepath = '/';
|
|
|
|
|
2024-11-10 14:14:27 +00:00
|
|
|
final TextEditingController _serverUrlController = TextEditingController();
|
|
|
|
|
2024-11-10 13:48:42 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
getApplicationDocumentsDirectory().then((dir) {
|
|
|
|
_docBasepath = dir.path;
|
|
|
|
if (mounted) {
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
SharedPreferences.getInstance().then((prefs) {
|
|
|
|
setState(() {
|
|
|
|
_prefs = prefs;
|
2024-12-08 09:14:31 +00:00
|
|
|
_serverUrlController.text = prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
2024-11-10 13:48:42 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-11-10 14:14:27 +00:00
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_serverUrlController.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
2024-11-10 13:48:42 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-11-10 14:14:27 +00:00
|
|
|
final sn = context.read<SnNetworkProvider>();
|
|
|
|
|
2024-11-14 14:21:13 +00:00
|
|
|
return Scaffold(
|
2024-11-10 13:48:42 +00:00
|
|
|
body: SingleChildScrollView(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
2024-12-08 09:14:31 +00:00
|
|
|
Text('settingsAppearance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4),
|
2024-11-10 13:48:42 +00:00
|
|
|
if (!kIsWeb)
|
|
|
|
ListTile(
|
|
|
|
title: Text('settingsBackgroundImage').tr(),
|
|
|
|
subtitle: Text('settingsBackgroundImageDescription').tr(),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
|
|
leading: const Icon(Symbols.image),
|
|
|
|
trailing: const Icon(Symbols.chevron_right),
|
|
|
|
onTap: () async {
|
2024-12-08 09:14:31 +00:00
|
|
|
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
|
2024-11-10 13:48:42 +00:00
|
|
|
if (image == null) return;
|
|
|
|
|
2024-12-08 09:14:31 +00:00
|
|
|
await File(image.path).copy('$_docBasepath/app_background_image');
|
2024-12-09 14:57:20 +00:00
|
|
|
_prefs?.setBool('has_background_image', true);
|
2024-11-10 13:48:42 +00:00
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
if (!kIsWeb)
|
|
|
|
FutureBuilder<bool>(
|
2024-12-08 09:14:31 +00:00
|
|
|
future: File('$_docBasepath/app_background_image').exists(),
|
2024-11-10 13:48:42 +00:00
|
|
|
builder: (context, snapshot) {
|
|
|
|
if (!snapshot.hasData || !snapshot.data!) {
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ListTile(
|
|
|
|
title: Text('settingsBackgroundImageClear').tr(),
|
2024-12-08 09:14:31 +00:00
|
|
|
subtitle: Text('settingsBackgroundImageClearDescription').tr(),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
2024-11-10 13:48:42 +00:00
|
|
|
leading: const Icon(Symbols.texture),
|
|
|
|
trailing: const Icon(Symbols.chevron_right),
|
|
|
|
onTap: () {
|
2024-12-08 09:14:31 +00:00
|
|
|
File('$_docBasepath/app_background_image').deleteSync();
|
2024-12-09 14:57:20 +00:00
|
|
|
_prefs?.remove('has_background_image');
|
2024-11-10 13:48:42 +00:00
|
|
|
setState(() {});
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
if (_prefs != null)
|
|
|
|
CheckboxListTile(
|
|
|
|
title: Text('settingsThemeMaterial3').tr(),
|
|
|
|
subtitle: Text('settingsThemeMaterial3Description').tr(),
|
|
|
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
|
|
|
secondary: const Icon(Symbols.new_releases),
|
|
|
|
value: _prefs!.getBool(kMaterialYouToggleStoreKey) ?? false,
|
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
_prefs!.setBool(
|
|
|
|
kMaterialYouToggleStoreKey,
|
|
|
|
value ?? false,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
final th = context.watch<ThemeProvider>();
|
|
|
|
th.reloadTheme(useMaterial3: value ?? false);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
2024-12-08 09:14:31 +00:00
|
|
|
Text('settingsNetwork').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4),
|
2024-11-10 14:14:27 +00:00
|
|
|
TextField(
|
|
|
|
controller: _serverUrlController,
|
|
|
|
decoration: InputDecoration(
|
|
|
|
border: const OutlineInputBorder(),
|
|
|
|
labelText: 'settingsNetworkServer'.tr(),
|
|
|
|
helperText: 'settingsNetworkServerDescription'.tr(),
|
|
|
|
prefixIcon: const Icon(Symbols.dns),
|
|
|
|
suffixIcon: IconButton(
|
|
|
|
icon: const Icon(Symbols.save),
|
|
|
|
onPressed: () {
|
|
|
|
sn.setBaseUrl(_serverUrlController.text);
|
|
|
|
_prefs?.setString(
|
|
|
|
kNetworkServerStoreKey,
|
|
|
|
_serverUrlController.text,
|
|
|
|
);
|
|
|
|
context.showSnackbar('settingsNetworkServerSaved'.tr());
|
|
|
|
setState(() {});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
2024-12-08 09:14:31 +00:00
|
|
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
2024-11-10 14:14:27 +00:00
|
|
|
).padding(horizontal: 16, top: 8, bottom: 4),
|
|
|
|
ListTile(
|
|
|
|
title: Text('settingsNetworkServerPreset').tr(),
|
|
|
|
subtitle: Text('settingsNetworkServerPresetDescription').tr(),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
|
|
leading: const Icon(Symbols.lists),
|
|
|
|
trailing: DropdownButtonHideUnderline(
|
|
|
|
child: DropdownButton2<String>(
|
|
|
|
isExpanded: true,
|
|
|
|
items: [
|
|
|
|
...kNetworkServerDirectory,
|
2024-12-08 09:14:31 +00:00
|
|
|
if (!kNetworkServerDirectory.map((ele) => ele.$2).contains(_serverUrlController.text))
|
2024-11-10 14:14:27 +00:00
|
|
|
('Custom', _serverUrlController.text),
|
|
|
|
]
|
|
|
|
.map(
|
|
|
|
(item) => DropdownMenuItem<String>(
|
|
|
|
value: item.$2,
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.max,
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(item.$1).fontSize(14),
|
2024-12-08 09:14:31 +00:00
|
|
|
Text(item.$2, overflow: TextOverflow.ellipsis).fontSize(11)
|
2024-11-10 14:14:27 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.toList(),
|
|
|
|
value: _serverUrlController.text,
|
|
|
|
onChanged: (String? value) {
|
|
|
|
if (value == null) return;
|
|
|
|
_serverUrlController.text = value;
|
|
|
|
_prefs?.setString(kNetworkServerStoreKey, value);
|
|
|
|
context.showSnackbar('settingsNetworkServerSaved'.tr());
|
|
|
|
setState(() {});
|
|
|
|
},
|
|
|
|
buttonStyleData: const ButtonStyleData(
|
|
|
|
padding: EdgeInsets.symmetric(
|
|
|
|
horizontal: 16,
|
|
|
|
vertical: 5,
|
|
|
|
),
|
|
|
|
height: 40,
|
2024-12-08 08:21:56 +00:00
|
|
|
width: 160,
|
2024-11-10 14:14:27 +00:00
|
|
|
),
|
|
|
|
menuItemStyleData: const MenuItemStyleData(
|
|
|
|
height: 60,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
ListTile(
|
|
|
|
title: Text('settingsNetworkServerReset').tr(),
|
|
|
|
subtitle: Text('settingsNetworkServerResetDescription').tr(),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
|
|
leading: const Icon(Symbols.reset_wrench),
|
|
|
|
trailing: const Icon(Symbols.chevron_right),
|
|
|
|
onTap: () {
|
|
|
|
_serverUrlController.text = kNetworkServerDefault;
|
|
|
|
_prefs?.remove(kNetworkServerStoreKey);
|
|
|
|
context.showSnackbar('settingsNetworkServerSaved'.tr());
|
|
|
|
setState(() {});
|
|
|
|
},
|
|
|
|
),
|
2024-11-10 13:48:42 +00:00
|
|
|
],
|
|
|
|
),
|
2024-12-08 09:14:31 +00:00
|
|
|
Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text('settingsMisc').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4),
|
|
|
|
ListTile(
|
|
|
|
title: Text('settingsMiscAbout').tr(),
|
|
|
|
subtitle: Text('settingsMiscAboutDescription').tr(),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
|
|
leading: const Icon(Symbols.info),
|
|
|
|
trailing: const Icon(Symbols.chevron_right),
|
|
|
|
onTap: () async {
|
|
|
|
GoRouter.of(context).pushNamed('about');
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
2024-11-10 13:48:42 +00:00
|
|
|
].expand((ele) => [ele, const Gap(16)]).toList(),
|
|
|
|
).padding(vertical: 20),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|