import 'dart:io'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'slide_controller.dart'; void main() => runApp(const PptRemoteApp()); class PptRemoteApp extends StatelessWidget { const PptRemoteApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'PPT Remote', theme: ThemeData.dark(useMaterial3: true).copyWith( colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepOrange, brightness: Brightness.dark, ), ), home: const ConnectScreen(), ); } } // ─── Connect Screen ─────────────────────────────────────────────────────────── class ConnectScreen extends StatefulWidget { const ConnectScreen({super.key}); @override State createState() => _ConnectScreenState(); } class _ConnectScreenState extends State { final _hostController = TextEditingController(text: '192.168.1.100'); final _portController = TextEditingController(text: '8765'); @override void initState() { super.initState(); _loadPrefs(); } Future _loadPrefs() async { final prefs = await SharedPreferences.getInstance(); setState(() { _hostController.text = prefs.getString('host') ?? '192.168.1.100'; _portController.text = prefs.getString('port') ?? '8765'; }); } bool _checking = false; String? _checkError; Future _connect() async { final host = _hostController.text.trim(); final port = _portController.text.trim(); final portNum = int.tryParse(port) ?? 8765; setState(() { _checking = true; _checkError = null; }); // Probe the HTTP endpoint before opening a WebSocket final reachable = await _checkServer(host, portNum); if (!mounted) return; if (!reachable) { setState(() { _checking = false; _checkError = 'Cannot reach $host:$portNum\n' 'Make sure the server is running and both devices are on the same Wi-Fi.'; }); return; } final prefs = await SharedPreferences.getInstance(); await prefs.setString('host', host); await prefs.setString('port', port); setState(() => _checking = false); if (!mounted) return; Navigator.of(context).push(MaterialPageRoute( builder: (_) => RemoteScreen(host: host, port: portNum), )); } /// Tries a plain TCP connection to host:port with a 3-second timeout. Future _checkServer(String host, int port) async { try { final socket = await Socket.connect(host, port, timeout: const Duration(seconds: 3)); socket.destroy(); return true; } catch (_) { return false; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('PPT Remote')), body: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.slideshow, size: 72, color: Colors.deepOrange), const SizedBox(height: 32), TextField( controller: _hostController, decoration: const InputDecoration( labelText: 'Server IP', border: OutlineInputBorder(), prefixIcon: Icon(Icons.computer), ), keyboardType: TextInputType.url, ), const SizedBox(height: 16), TextField( controller: _portController, decoration: const InputDecoration( labelText: 'Port', border: OutlineInputBorder(), prefixIcon: Icon(Icons.settings_ethernet), ), keyboardType: TextInputType.number, ), const SizedBox(height: 32), if (_checkError != null) Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( children: [ const Icon(Icons.error_outline, color: Colors.redAccent), const SizedBox(width: 8), Expanded( child: Text( _checkError!, style: const TextStyle(color: Colors.redAccent), ), ), ], ), ), FilledButton.icon( onPressed: _checking ? null : _connect, icon: _checking ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.wifi), label: Text(_checking ? 'Checking...' : 'Connect'), style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(52), ), ), ], ), ), ); } }