import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'l10n/app_localizations.dart'; import 'package:url_launcher/url_launcher.dart'; import 'widgets/offline_banner.dart'; // Routing view that immediately opens a Google Maps navigation inside a WebView // It receives an address string and optionally a title. class RoutingView extends StatefulWidget { final String address; final String? title; final bool isDelivery; // to distinguish pickup/delivery if needed later const RoutingView({super.key, required this.address, this.title, this.isDelivery = true}); @override State createState() => _RoutingViewState(); } class _RoutingViewState extends State { bool _initialized = false; late final WebViewController _controller; double _progress = 0.0; String _buildDirectionsUrl(String rawAddress) { const apiKey = 'AIzaSyDnbitL06iLp3elmj-WtPudCykX9xvXcVE'; final query = Uri.encodeComponent(rawAddress); // Google Maps Directions URL with API key appended. return 'https://www.google.com/maps/dir/?api=1&destination=$query&travelmode=driving&key=$apiKey'; } String? _extractBrowserFallbackUrl(String intentUrl) { const key = 'S.browser_fallback_url='; final idx = intentUrl.indexOf(key); if (idx == -1) return null; final start = idx + key.length; final end = intentUrl.indexOf(';', start); final encoded = end == -1 ? intentUrl.substring(start) : intentUrl.substring(start, end); try { return Uri.decodeComponent(encoded); } catch (_) { return encoded; } } String? _convertIntentToHttps(String intentUrl) { const prefix = 'intent://'; if (!intentUrl.startsWith(prefix)) return null; final after = intentUrl.substring(prefix.length); final hashIndex = after.indexOf('#'); final pathPart = hashIndex == -1 ? after : after.substring(0, hashIndex); // default scheme is https unless specified var scheme = 'https'; final schemeKey = '#Intent;scheme='; final schemeIdx = intentUrl.indexOf(schemeKey); if (schemeIdx != -1) { final start = schemeIdx + schemeKey.length; final end = intentUrl.indexOf(';', start); if (end > start) { scheme = intentUrl.substring(start, end); } } return '$scheme://$pathPart'; } @override void initState() { assert(!_initialized); super.initState(); final url = _buildDirectionsUrl(widget.address); _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setNavigationDelegate( NavigationDelegate( onNavigationRequest: (NavigationRequest request) async { final uri = Uri.tryParse(request.url); if (uri == null) { return NavigationDecision.prevent; } // Handle intent:// and other non-http(s) schemes by launching externally or using fallback. if (uri.scheme == 'intent') { final fallback = _extractBrowserFallbackUrl(request.url); if (fallback != null) { await _controller.loadRequest(Uri.parse(fallback)); } else { // Try converting to https as a naive fallback final httpsCandidate = _convertIntentToHttps(request.url); if (httpsCandidate != null && await canLaunchUrl(Uri.parse(httpsCandidate))) { await launchUrl(Uri.parse(httpsCandidate), mode: LaunchMode.externalApplication); } else { // As a last resort, prevent navigation and show a hint if (mounted) { ScaffoldMessenger.maybeOf(context)?.showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).connectionError))); } } } return NavigationDecision.prevent; } if (uri.scheme != 'http' && uri.scheme != 'https' && uri.scheme != 'about' && uri.scheme != 'data') { if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } return NavigationDecision.prevent; } return NavigationDecision.navigate; }, onProgress: (int progress) { setState(() { _progress = progress / 100.0; }); }, onWebResourceError: (WebResourceError error) { // Optionally show a snackbar on error if (mounted) { ScaffoldMessenger.maybeOf(context)?.showSnackBar(SnackBar(content: Text('${AppLocalizations.of(context).error}: ${error.errorCode}'))); } }, ), ) ..loadRequest(Uri.parse(url)); _initialized = true; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title?.isNotEmpty == true ? widget.title! : (widget.isDelivery ? 'Route zur Zustelladresse' : 'Route zur Abholadresse'))), body: Column( children: [ OfflineBanner(), Expanded(child: Stack(children: [WebViewWidget(key: const ValueKey('routing-webview'), controller: _controller), if (_progress < 1.0) LinearProgressIndicator(value: _progress)])), ], ), ); } }