import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:webview_flutter/webview_flutter.dart'; /// Login screen that loads wp-login.php in a WebView. /// /// When the user successfully logs in, WordPress redirects to /twp-phone/. /// We detect that URL change and report login success to the parent. class LoginScreen extends StatefulWidget { final void Function(String serverUrl) onLoginSuccess; const LoginScreen({super.key, required this.onLoginSuccess}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { static const _storage = FlutterSecureStorage(); final _formKey = GlobalKey(); final _serverController = TextEditingController(); bool _showWebView = false; bool _webViewLoading = true; String? _error; late WebViewController _webViewController; @override void initState() { super.initState(); _loadSavedServer(); } Future _loadSavedServer() async { final saved = await _storage.read(key: 'server_url'); if (saved != null && mounted) { _serverController.text = saved; } } void _startLogin() { if (!_formKey.currentState!.validate()) return; var serverUrl = _serverController.text.trim(); if (!serverUrl.startsWith('http')) { serverUrl = 'https://$serverUrl'; } // Remove trailing slash serverUrl = serverUrl.replaceAll(RegExp(r'/+$'), ''); setState(() { _showWebView = true; _webViewLoading = true; _error = null; }); final loginUrl = '$serverUrl/wp-login.php?redirect_to=${Uri.encodeComponent('$serverUrl/twp-phone/')}'; _webViewController = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setNavigationDelegate( NavigationDelegate( onPageStarted: (url) { // Check if we've been redirected to the phone page (login success) if (url.contains('/twp-phone/') || url.endsWith('/twp-phone')) { _onLoginComplete(serverUrl); } }, onPageFinished: (url) { if (mounted) { setState(() => _webViewLoading = false); } // Also check on page finish in case redirect was instant if (url.contains('/twp-phone/') || url.endsWith('/twp-phone')) { _onLoginComplete(serverUrl); } }, onWebResourceError: (error) { if (mounted) { setState(() { _showWebView = false; _error = 'Could not connect to server: ${error.description}'; }); } }, ), ) ..setUserAgent('TWPMobile/2.0 (Android; WebView)') ..loadRequest(Uri.parse(loginUrl)); } Future _onLoginComplete(String serverUrl) async { // Save server URL for next launch await _storage.write(key: 'server_url', value: serverUrl); if (mounted) { widget.onLoginSuccess(serverUrl); } } void _cancelLogin() { setState(() { _showWebView = false; _error = null; }); } @override Widget build(BuildContext context) { if (_showWebView) { return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: _cancelLogin, ), title: const Text('Sign In'), ), body: Stack( children: [ WebViewWidget(controller: _webViewController), if (_webViewLoading) const Center(child: CircularProgressIndicator()), ], ), ); } return Scaffold( body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.phone_in_talk, size: 64, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 16), Text( 'TWP Softphone', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 32), TextFormField( controller: _serverController, decoration: const InputDecoration( labelText: 'Server URL', hintText: 'https://your-site.com', prefixIcon: Icon(Icons.dns), border: OutlineInputBorder(), ), keyboardType: TextInputType.url, autofillHints: const [AutofillHints.url], validator: (v) => v == null || v.trim().isEmpty ? 'Required' : null, ), if (_error != null) ...[ const SizedBox(height: 16), Text( _error!, style: TextStyle( color: Theme.of(context).colorScheme.error), ), ], const SizedBox(height: 24), SizedBox( width: double.infinity, height: 48, child: FilledButton( onPressed: _startLogin, child: const Text('Connect'), ), ), ], ), ), ), ), ), ); } @override void dispose() { _serverController.dispose(); super.dispose(); } }