149 lines
4.5 KiB
Dart
149 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import '../models/project.dart';
|
|
|
|
class ProjectDialog extends StatefulWidget {
|
|
final Project? project;
|
|
final Function(Project) onSave;
|
|
|
|
const ProjectDialog({
|
|
super.key,
|
|
this.project,
|
|
required this.onSave,
|
|
});
|
|
|
|
@override
|
|
State<ProjectDialog> createState() => _ProjectDialogState();
|
|
}
|
|
|
|
class _ProjectDialogState extends State<ProjectDialog> {
|
|
late TextEditingController _nameController;
|
|
late TextEditingController _ipController;
|
|
late TextEditingController _portController;
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_nameController = TextEditingController(text: widget.project?.name ?? '');
|
|
_ipController = TextEditingController(
|
|
text: widget.project?.ipAddress ?? '127.0.0.1',
|
|
);
|
|
_portController = TextEditingController(
|
|
text: widget.project?.port.toString() ?? '8888',
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_nameController.dispose();
|
|
_ipController.dispose();
|
|
_portController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _save() {
|
|
if (_formKey.currentState!.validate()) {
|
|
final project = Project(
|
|
id: widget.project?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
|
|
name: _nameController.text.trim(),
|
|
ipAddress: _ipController.text.trim(),
|
|
port: int.parse(_portController.text),
|
|
packages: widget.project?.packages ?? [],
|
|
);
|
|
widget.onSave(project);
|
|
Navigator.pop(context);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text(widget.project == null ? 'New Project' : 'Edit Project'),
|
|
content: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
TextFormField(
|
|
controller: _nameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Project Name',
|
|
prefixIcon: Icon(Icons.folder),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.trim().isEmpty) {
|
|
return 'Please enter a project name';
|
|
}
|
|
return null;
|
|
},
|
|
autofocus: true,
|
|
),
|
|
const SizedBox(height: 16),
|
|
TextFormField(
|
|
controller: _ipController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'IP Address',
|
|
prefixIcon: Icon(Icons.computer),
|
|
hintText: '127.0.0.1',
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.trim().isEmpty) {
|
|
return 'Please enter an IP address';
|
|
}
|
|
// Basic IP address validation
|
|
final ipPattern = RegExp(
|
|
r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$',
|
|
);
|
|
if (!ipPattern.hasMatch(value.trim())) {
|
|
return 'Please enter a valid IP address';
|
|
}
|
|
final parts = value.trim().split('.');
|
|
for (final part in parts) {
|
|
final num = int.tryParse(part);
|
|
if (num == null || num < 0 || num > 255) {
|
|
return 'Each octet must be between 0 and 255';
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
TextFormField(
|
|
controller: _portController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'UDP Port',
|
|
prefixIcon: Icon(Icons.router),
|
|
),
|
|
keyboardType: TextInputType.number,
|
|
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Please enter a port number';
|
|
}
|
|
final port = int.tryParse(value);
|
|
if (port == null || port < 1 || port > 65535) {
|
|
return 'Port must be between 1 and 65535';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: const Text('Cancel'),
|
|
),
|
|
FilledButton(
|
|
onPressed: _save,
|
|
child: const Text('Save'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
|