initial upload

This commit is contained in:
tom.hempel
2025-10-15 10:53:36 +02:00
commit c15d1d1e49
140 changed files with 6730 additions and 0 deletions

View File

@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
class AppAboutDialog extends StatelessWidget {
const AppAboutDialog({super.key});
@override
Widget build(BuildContext context) {
return AboutDialog(
applicationName: 'UnityUDP',
applicationVersion: '1.0.0',
applicationIcon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.send,
size: 32,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
children: [
const SizedBox(height: 16),
const Text(
'A simple and efficient app for sending UDP packages over custom ports.',
),
const SizedBox(height: 16),
const Text(
'Features:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text('• Create and manage multiple projects'),
const Text('• Configure custom UDP ports'),
const Text('• Store pre-defined packages'),
const Text('• Quick send functionality'),
const Text('• Persistent local storage'),
const SizedBox(height: 16),
const Divider(),
const SizedBox(height: 16),
Row(
children: [
Icon(
Icons.code,
size: 20,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
const Text(
'Developed by Tom Hempel',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
),
],
);
}
}

View File

@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
import '../models/udp_package.dart';
class PackageDialog extends StatefulWidget {
final UdpPackage? package;
final Function(UdpPackage) onSave;
const PackageDialog({
super.key,
this.package,
required this.onSave,
});
@override
State<PackageDialog> createState() => _PackageDialogState();
}
class _PackageDialogState extends State<PackageDialog> {
late TextEditingController _nameController;
late TextEditingController _ipController;
late TextEditingController _dataController;
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
_nameController = TextEditingController(text: widget.package?.name ?? '');
_ipController = TextEditingController(
text: widget.package?.ipAddress ?? '127.0.0.1',
);
_dataController = TextEditingController(text: widget.package?.data ?? '');
}
@override
void dispose() {
_nameController.dispose();
_ipController.dispose();
_dataController.dispose();
super.dispose();
}
bool _isValidIp(String ip) {
final parts = ip.split('.');
if (parts.length != 4) return false;
for (final part in parts) {
final num = int.tryParse(part);
if (num == null || num < 0 || num > 255) return false;
}
return true;
}
void _save() {
if (_formKey.currentState!.validate()) {
final package = UdpPackage(
id: widget.package?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
name: _nameController.text.trim(),
ipAddress: _ipController.text.trim(),
data: _dataController.text,
);
widget.onSave(package);
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(widget.package == null ? 'New Package' : 'Edit Package'),
content: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Package Name',
prefixIcon: Icon(Icons.label),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Please enter a package name';
}
return null;
},
autofocus: true,
),
const SizedBox(height: 16),
TextFormField(
controller: _ipController,
decoration: const InputDecoration(
labelText: 'IP Address',
prefixIcon: Icon(Icons.computer),
hintText: '192.168.1.100',
),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Please enter an IP address';
}
if (!_isValidIp(value.trim())) {
return 'Invalid IP address';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _dataController,
decoration: const InputDecoration(
labelText: 'Data',
prefixIcon: Icon(Icons.data_object),
hintText: 'Enter the data to send',
),
maxLines: 4,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter data to send';
}
return null;
},
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
FilledButton(
onPressed: _save,
child: const Text('Save'),
),
],
);
}
}

View File

@ -0,0 +1,113 @@
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 _portController;
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
_nameController = TextEditingController(text: widget.project?.name ?? '');
_portController = TextEditingController(
text: widget.project?.port.toString() ?? '8888',
);
}
@override
void dispose() {
_nameController.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(),
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: _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'),
),
],
);
}
}