← Назад к вопросам
Реализовать экран настроек с SharedPreferences
1.0 Junior🔥 111 комментариев
#Flutter виджеты#Хранение данных
Условие
Создайте экран настроек приложения с сохранением в SharedPreferences.
Требования
- Переключатель темной темы
- Выбор языка (из списка)
- Переключатель уведомлений
- Слайдер размера шрифта
- Сохранение всех настроек в SharedPreferences
- Применение настроек при запуске приложения
Дополнительные баллы
- Группировка настроек по категориям
- Кнопка сброса к настройкам по умолчанию
- Preview изменений в реальном времени
- Экспорт/импорт настроек
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Экран Настроек с SharedPreferences
Полнофункциональный экран настроек с группировкой, предпросмотром и сохранением.
Модель настроек
class AppSettings {
final bool darkMode;
final String language;
final bool notificationsEnabled;
final double fontSize;
AppSettings({
this.darkMode = false,
this.language = 'ru',
this.notificationsEnabled = true,
this.fontSize = 1.0,
});
Map<String, dynamic> toJson() {
return {
'darkMode': darkMode,
'language': language,
'notificationsEnabled': notificationsEnabled,
'fontSize': fontSize,
};
}
factory AppSettings.fromJson(Map<String, dynamic> json) {
return AppSettings(
darkMode: json['darkMode'] ?? false,
language: json['language'] ?? 'ru',
notificationsEnabled: json['notificationsEnabled'] ?? true,
fontSize: json['fontSize'] ?? 1.0,
);
}
}
Сервис настроек
class SettingsService {
static const String _key = 'app_settings';
final SharedPreferences _prefs;
SettingsService(this._prefs);
Future<AppSettings> loadSettings() async {
final settingsJson = _prefs.getString(_key);
if (settingsJson != null) {
return AppSettings.fromJson(jsonDecode(settingsJson));
}
return AppSettings();
}
Future<void> saveSettings(AppSettings settings) async {
await _prefs.setString(_key, jsonEncode(settings.toJson()));
}
Future<void> resetToDefaults() async {
await _prefs.remove(_key);
}
Future<String> exportSettings() async {
final settings = await loadSettings();
return jsonEncode(settings.toJson());
}
Future<void> importSettings(String jsonString) async {
try {
final json = jsonDecode(jsonString);
final settings = AppSettings.fromJson(json);
await saveSettings(settings);
} catch (e) {
throw Exception('Ошибка импорта настроек');
}
}
}
Контроллер настроек
class SettingsController extends GetxController {
final SettingsService _settingsService;
final settings = AppSettings().obs;
final isLoading = false.obs;
SettingsController({required SettingsService settingsService})
: _settingsService = settingsService;
@override
void onInit() {
super.onInit();
loadSettings();
}
Future<void> loadSettings() async {
isLoading.value = true;
try {
final loadedSettings = await _settingsService.loadSettings();
settings.value = loadedSettings;
} catch (e) {
Get.snackbar('Ошибка', 'Не удалось загрузить настройки');
} finally {
isLoading.value = false;
}
}
Future<void> setDarkMode(bool value) async {
final newSettings = AppSettings(
darkMode: value,
language: settings.value.language,
notificationsEnabled: settings.value.notificationsEnabled,
fontSize: settings.value.fontSize,
);
settings.value = newSettings;
await _settingsService.saveSettings(newSettings);
}
Future<void> setLanguage(String language) async {
final newSettings = AppSettings(
darkMode: settings.value.darkMode,
language: language,
notificationsEnabled: settings.value.notificationsEnabled,
fontSize: settings.value.fontSize,
);
settings.value = newSettings;
await _settingsService.saveSettings(newSettings);
}
Future<void> setNotifications(bool value) async {
final newSettings = AppSettings(
darkMode: settings.value.darkMode,
language: settings.value.language,
notificationsEnabled: value,
fontSize: settings.value.fontSize,
);
settings.value = newSettings;
await _settingsService.saveSettings(newSettings);
}
Future<void> setFontSize(double value) async {
final newSettings = AppSettings(
darkMode: settings.value.darkMode,
language: settings.value.language,
notificationsEnabled: settings.value.notificationsEnabled,
fontSize: value,
);
settings.value = newSettings;
await _settingsService.saveSettings(newSettings);
}
Future<void> resetToDefaults() async {
await _settingsService.resetToDefaults();
settings.value = AppSettings();
Get.snackbar('Успех', 'Настройки сброшены');
}
Future<void> exportSettings() async {
try {
final json = await _settingsService.exportSettings();
// Копируем в буфер обмена
await Clipboard.setData(ClipboardData(text: json));
Get.snackbar('Успех', 'Настройки скопированы');
} catch (e) {
Get.snackbar('Ошибка', 'Не удалось экспортировать');
}
}
Future<void> importSettings(String json) async {
try {
await _settingsService.importSettings(json);
await loadSettings();
Get.snackbar('Успех', 'Настройки импортированы');
} catch (e) {
Get.snackbar('Ошибка', 'Не удалось импортировать');
}
}
}
Главная страница настроек
class SettingsPage extends StatefulWidget {
@override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
final SettingsController _controller = Get.put(
SettingsController(settingsService: Get.find<SettingsService>()),
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Настройки'),
elevation: 0,
backgroundColor: Color(0xFF667EEA),
),
body: Obx(() {
if (_controller.isLoading.value) {
return Center(child: CircularProgressIndicator());
}
return SingleChildScrollView(
child: Column(
children: [
_buildThemeSection(),
_buildLanguageSection(),
_buildNotificationSection(),
_buildFontSizeSection(),
_buildPreviewSection(),
_buildActionSection(),
SizedBox(height: 24),
],
),
);
}),
);
}
Widget _buildThemeSection() {
return _buildSettingSection(
title: 'Внешний вид',
icon: Icons.palette,
children: [
Obx(() {
return SwitchListTile(
title: Text('Тёмная тема'),
subtitle: Text(
_controller.settings.value.darkMode
? 'Включена'
: 'Выключена',
),
value: _controller.settings.value.darkMode,
onChanged: (value) => _controller.setDarkMode(value),
);
}),
],
);
}
Widget _buildLanguageSection() {
return _buildSettingSection(
title: 'Язык и локализация',
icon: Icons.language,
children: [
Obx(() {
return DropdownListTile(
title: 'Язык приложения',
subtitle: _getLanguageName(_controller.settings.value.language),
value: _controller.settings.value.language,
items: [
DropdownMenuItem(value: 'ru', child: Text('Русский')),
DropdownMenuItem(value: 'en', child: Text('English')),
DropdownMenuItem(value: 'de', child: Text('Deutsch')),
],
onChanged: (value) {
if (value != null) {
_controller.setLanguage(value);
}
},
);
}),
],
);
}
Widget _buildNotificationSection() {
return _buildSettingSection(
title: 'Уведомления',
icon: Icons.notifications,
children: [
Obx(() {
return SwitchListTile(
title: Text('Включить уведомления'),
subtitle: Text(
_controller.settings.value.notificationsEnabled
? 'Включены'
: 'Выключены',
),
value: _controller.settings.value.notificationsEnabled,
onChanged: (value) => _controller.setNotifications(value),
);
}),
],
);
}
Widget _buildFontSizeSection() {
return _buildSettingSection(
title: 'Размер шрифта',
icon: Icons.text_fields,
children: [
Obx(() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Размер: ${(_controller.settings.value.fontSize * 100).toStringAsFixed(0)}%',
style: TextStyle(fontWeight: FontWeight.w600),
),
SizedBox(height: 12),
Slider(
value: _controller.settings.value.fontSize,
min: 0.8,
max: 1.5,
divisions: 7,
onChanged: (value) => _controller.setFontSize(value),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('80%', style: TextStyle(fontSize: 12, color: Colors.grey)),
Text('150%', style: TextStyle(fontSize: 12, color: Colors.grey)),
],
),
],
),
);
}),
],
);
}
Widget _buildPreviewSection() {
return _buildSettingSection(
title: 'Предпросмотр',
icon: Icons.preview,
children: [
Obx(() {
final fontSize = 14 * _controller.settings.value.fontSize;
return Padding(
padding: EdgeInsets.all(16),
child: Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: _controller.settings.value.darkMode
? Colors.grey[900]
: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Так выглядит текст в приложении',
style: TextStyle(
fontSize: fontSize,
color: _controller.settings.value.darkMode
? Colors.white
: Colors.black,
),
),
),
);
}),
],
);
}
Widget _buildActionSection() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: Column(
children: [
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _controller.exportSettings,
icon: Icon(Icons.download),
label: Text('Экспортировать настройки'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
),
),
),
SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () => _showImportDialog(),
icon: Icon(Icons.upload),
label: Text('Импортировать настройки'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
),
),
),
SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () => _showResetDialog(),
icon: Icon(Icons.refresh),
label: Text('Сбросить на стандартные'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
),
),
],
),
);
}
Widget _buildSettingSection({
required String title,
required IconData icon,
required List<Widget> children,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(16, 24, 16, 8),
child: Row(
children: [
Icon(icon, color: Color(0xFF667EEA), size: 24),
SizedBox(width: 12),
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFF667EEA),
),
),
],
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[300]!),
),
child: Column(children: children),
),
],
);
}
void _showResetDialog() {
Get.dialog(
AlertDialog(
title: Text('Сбросить настройки?'),
content: Text('Все настройки будут восстановлены по умолчанию'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('Отмена'),
),
TextButton(
onPressed: () {
_controller.resetToDefaults();
Get.back();
},
child: Text('Сбросить', style: TextStyle(color: Colors.red)),
),
],
),
);
}
void _showImportDialog() {
final controller = TextEditingController();
Get.dialog(
AlertDialog(
title: Text('Импортировать настройки'),
content: TextField(
controller: controller,
minLines: 3,
maxLines: 8,
decoration: InputDecoration(
hintText: 'Вставьте JSON настроек',
border: OutlineInputBorder(),
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('Отмена'),
),
TextButton(
onPressed: () {
_controller.importSettings(controller.text);
Get.back();
},
child: Text('Импортировать'),
),
],
),
);
}
String _getLanguageName(String languageCode) {
switch (languageCode) {
case 'ru':
return 'Русский';
case 'en':
return 'English';
case 'de':
return 'Deutsch';
default:
return languageCode;
}
}
}
class DropdownListTile extends StatelessWidget {
final String title;
final String subtitle;
final String value;
final List<DropdownMenuItem<String>> items;
final ValueChanged<String?> onChanged;
const DropdownListTile({
required this.title,
required this.subtitle,
required this.value,
required this.items,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
subtitle: Text(subtitle),
trailing: DropdownButton<String>(
value: value,
items: items,
onChanged: onChanged,
underline: SizedBox(),
),
);
}
}
Dependencies в pubspec.yaml
dependencies:
flutter:
sdk: flutter
get: ^4.6.5
shared_preferences: ^2.2.0
flutter_native_splash: ^2.3.0
Применение в main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
final settingsService = SettingsService(prefs);
Get.put(settingsService);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp();
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: SettingsPage(),
themeMode: ThemeMode.system,
);
}
}
Ключевые особенности
- Группировка по категориям — внешний вид, язык, уведомления
- Переключатели и слайдеры — удобное управление
- Предпросмотр в реальном времени — сразу видите изменения
- Экспорт/Импорт — сохранение и восстановление
- Сброс на стандартные — восстановление умолчаний
- Персистентность — сохранение в SharedPreferences
- Валидация — проверка при импорте