import 'package:flutter/material.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:pweb/models/edit_state.dart'; import 'package:pweb/utils/error/snackbar.dart'; import 'package:pweb/generated/i18n/app_localizations.dart'; abstract class BaseEditTile extends AbstractSettingsTile { const BaseEditTile({ super.key, required this.icon, required this.title, required this.valueGetter, required this.valueSetter, required this.errorSituation, }); final IconData icon; final String title; final ValueGetter valueGetter; final Future Function(T) valueSetter; final String errorSituation; Widget buildView(BuildContext context, T? value); Widget buildEditor( BuildContext context, T? initial, void Function(T) onSave, VoidCallback onCancel, bool isSaving, ); bool get useDialogEditor => false; @override Widget build(BuildContext context) => _BaseEditTileBody(delegate: this); } class _BaseEditTileBody extends StatefulWidget { const _BaseEditTileBody({required this.delegate}); final BaseEditTile delegate; @override State<_BaseEditTileBody> createState() => _BaseEditTileBodyState(); } class _BaseEditTileBodyState extends State<_BaseEditTileBody> { EditState _state = EditState.view; bool get _isSaving => _state == EditState.saving; Future _performSave(T newValue) async { final current = widget.delegate.valueGetter(); if (newValue == current) { setState(() => _state = EditState.view); return; } setState(() => _state = EditState.saving); final sms = ScaffoldMessenger.of(context); final locs = AppLocalizations.of(context)!; try { await widget.delegate.valueSetter(newValue); sms.showSnackBar(SnackBar( content: Text(locs.settingsSuccessfullyUpdated), duration: const Duration(milliseconds: 1200), )); } catch (e) { notifyUserOfErrorX( scaffoldMessenger: sms, errorSituation: widget.delegate.errorSituation, appLocalizations: locs, exception: e, ); } finally { if (mounted) setState(() => _state = EditState.view); } } Future _openDialogEditor() async { final initial = widget.delegate.valueGetter(); final T? result = await showDialog( context: context, barrierDismissible: true, builder: (ctx) { return Dialog( insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)), child: Padding( padding: const EdgeInsets.all(16), child: widget.delegate.buildEditor( ctx, initial, (v) => Navigator.of(ctx).pop(v), () => Navigator.of(ctx).pop(), _isSaving, ), ), ); }, ); if (result != null) await _performSave(result); } @override Widget build(BuildContext context) { final delegate = widget.delegate; final current = delegate.valueGetter(); if (delegate.useDialogEditor) { return SettingsTile.navigation( leading: Icon(delegate.icon), title: Text(delegate.title), value: delegate.buildView(context, current), onPressed: (_) => _openDialogEditor(), ); } return SettingsTile.navigation( leading: Icon(delegate.icon), title: Text(delegate.title), value: _state == EditState.view ? delegate.buildView(context, current) : delegate.buildEditor( context, current, _performSave, () => setState(() => _state = EditState.view), _isSaving, ), onPressed: (_) { if (_state == EditState.view) setState(() => _state = EditState.edit); }, ); } }