You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HMG_QLine/lib/views/common_widgets/ip_config_dialog.dart

246 lines
8.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:universal_html/html.dart' as html;
import 'package:hmg_qline/constants/app_constants.dart';
/// Dialog for configuring device IP address on web platform
class IpConfigDialog extends StatefulWidget {
final Function(String) onIpSubmitted;
const IpConfigDialog({
super.key,
required this.onIpSubmitted,
});
@override
State<IpConfigDialog> createState() => _IpConfigDialogState();
}
class _IpConfigDialogState extends State<IpConfigDialog> {
final TextEditingController _ipController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
@override
void initState() {
super.initState();
// Try to get IP from localStorage
final storedIp = html.window.localStorage['device_ip'];
if (storedIp != null && storedIp.isNotEmpty && storedIp != 'WEB_DEFAULT') {
_ipController.text = storedIp;
}
}
@override
void dispose() {
_ipController.dispose();
super.dispose();
}
void _submitIp() {
if (_formKey.currentState!.validate()) {
setState(() => _isLoading = true);
final ip = _ipController.text.trim();
// Save to localStorage
try {
html.window.localStorage['device_ip'] = ip;
// Verify it was saved
final saved = html.window.localStorage['device_ip'];
print('💾 Saved IP to localStorage: $ip');
print('✅ Verified in localStorage: $saved');
print(' Match: ${saved == ip}');
if (saved != ip) {
print('⚠️ WARNING: localStorage save verification failed!');
print(' Expected: $ip');
print(' Got: $saved');
}
} catch (e) {
print('❌ Error saving to localStorage: $e');
}
// Notify parent
widget.onIpSubmitted(ip);
}
}
String? _validateIp(String? value) {
if (value == null || value.trim().isEmpty) {
return 'Please enter an IP address';
}
// Basic IP format validation (supports xxx.xxx.xxx.xxx format)
final ipPattern = RegExp(r'^(\d{1,3}\.){3}\d{1,3}$');
if (!ipPattern.hasMatch(value.trim())) {
return 'Invalid IP format (e.g., 12.4.5.1)';
}
return null;
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
constraints: const BoxConstraints(maxWidth: 500),
padding: const EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Row(
children: [
Icon(
Icons.settings_ethernet,
color: AppColors.blueColor,
size: 32,
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Device IP Configuration',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: AppColors.greyColor,
),
),
),
],
),
const SizedBox(height: 24),
// Instructions
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.blueColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: AppColors.blueColor.withValues(alpha: 0.3),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Enter the device IP address to configure this kiosk.',
style: TextStyle(
fontSize: 14,
color: AppColors.greyColor,
),
),
const SizedBox(height: 8),
Text(
'Alternatively, you can add ?ip=YOUR_IP to the URL:',
style: TextStyle(
fontSize: 12,
color: AppColors.greyColor.withValues(alpha: 0.7),
),
),
const SizedBox(height: 4),
SelectableText(
'Example: ${html.window.location.origin}/?ip=12.4.5.1',
style: TextStyle(
fontSize: 12,
fontFamily: 'monospace',
color: AppColors.blueColor,
),
),
],
),
),
const SizedBox(height: 24),
// IP Input Field
TextFormField(
controller: _ipController,
validator: _validateIp,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
],
decoration: InputDecoration(
labelText: 'IP Address',
hintText: '12.4.5.1',
prefixIcon: Icon(Icons.router, color: AppColors.blueColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: AppColors.blueColor, width: 2),
),
),
onFieldSubmitted: (_) => _submitIp(),
),
const SizedBox(height: 24),
// Buttons
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: _isLoading ? null : () {
// Use test IP
_ipController.text = AppConstants.testIP;
_submitIp();
},
child: Text(
'Use Test IP',
style: TextStyle(color: AppColors.greyColor),
),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: _isLoading ? null : _submitIp,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.blueColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text(
'Connect',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
],
),
],
),
),
),
);
}
}