import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import '../../../about/internal/application/ServerProfile.dart'; import '../../../about/internal/application/TextType.dart'; import '../../../designs/input/InputFields.dart'; import '../../../designs/text/Text.dart'; import '../../../utils/Colors.dart'; import '../../../utils/Validators.dart'; import 'ConnectServerEditor.dart'; import 'ServerEditor.dart'; import 'ViewServerEditor.dart'; class ServerEditorState extends State implements ConnectServerEditor { ViewServerEditor? _model; final _formKey = GlobalKey(); late final ServerProfile _server; bool _showPassword = false; @override void initState() { super.initState(); _server = widget.server?.copy() ?? ServerProfile(); } bool get _isNew => widget.server == null; @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( viewModelBuilder: () => ViewServerEditor(context, this), onViewModelReady: _onReady, builder: (context, viewModel, child) => Scaffold( appBar: AppBar( title: text( _isNew ? 'Register server' : 'Edit server', 20, TextType.Bold, color: context.jp.text, ), actions: [ TextButton.icon( onPressed: _onTestConnection, icon: const Icon(Icons.wifi_tethering, size: 18), label: text('Test', 13, TextType.Bold, color: colorPrimary), ), Padding( padding: const EdgeInsets.only(right: 12, left: 8), child: FilledButton.icon( onPressed: _onSave, icon: const Icon(Icons.check, size: 18), label: text('Save', 14, TextType.Bold, color: colorWhite), ), ), ], ), body: Form( key: _formKey, child: ListView( padding: const EdgeInsets.fromLTRB(20, 20, 20, 40), children: [ inputSection(context, 'Identity'), inputField( label: 'Server name', hint: 'prod-1', initial: _server.name, onSaved: _onSaveName, validator: requiredField, ), inputSection(context, 'Connection'), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 3, child: inputField( label: 'Host / IP', hint: '10.0.0.5 or example.com', initial: _server.host, onSaved: _onSaveHost, validator: validHost, ), ), const SizedBox(width: 12), Expanded( child: inputField( label: 'Port', initial: _server.port.toString(), keyboardType: TextInputType.number, onSaved: _onSavePort, validator: validPort, ), ), ], ), inputField( label: 'Username', hint: 'root / ubuntu', initial: _server.username, onSaved: _onSaveUsername, validator: requiredField, ), inputField( label: 'Password', initial: _server.password, obscure: !_showPassword, onSaved: _onSavePassword, suffix: IconButton( icon: Icon( _showPassword ? Icons.visibility_off : Icons.visibility, ), onPressed: _onTogglePassword, ), ), inputHint( context, Icons.lock, 'Stored encrypted in your OS keystore ' '(Keychain / Keystore / DPAPI / libsecret).', ), inputSwitch( context: context, title: 'Use sudo for privileged commands', subtitle: 'Sends the password to sudo -S for systemctl / mkdir / rm.', value: _server.useSudo, onChanged: _onToggleSudo, ), ], ), ), ), ); } void _onReady(ViewServerEditor model) { _model = model; // onViewModelReady fires mid-build; a synchronous setState from the // first load would dirty this State while it is still being built. WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) _initiate(); }); } Future _initiate() async {} // ── named handlers ───────────────────────────────────────────────────────── void _onSaveName(String v) => _server.name = v; void _onSaveHost(String v) => _server.host = v; void _onSavePort(String v) => _server.port = int.tryParse(v) ?? 22; void _onSaveUsername(String v) => _server.username = v; void _onSavePassword(String v) => _server.password = v; void _onTogglePassword() => setState(() => _showPassword = !_showPassword); void _onToggleSudo(bool v) => setState(() => _server.useSudo = v); /// Commits the form and hands the profile back to whoever opened the editor. void _onSave() { if (!_formKey.currentState!.validate()) return; _formKey.currentState!.save(); Navigator.of(context).pop(_server); } /// Verifies the details as typed, without leaving the editor. void _onTestConnection() { if (!_formKey.currentState!.validate()) return; _formKey.currentState!.save(); _model?.testConnection(_server); } // ── ConnectServerEditor ──────────────────────────────────────────────────── @override void onConnectionVerified(ServerProfile server) { // The success sheet raised by the ViewModel is the whole outcome; the form // keeps its values so the user can save straight away. } }