refactor: Projektstruktur in app/ und backend/ aufgeteilt
This commit is contained in:
141
app/lib/routing_view.dart
Normal file
141
app/lib/routing_view.dart
Normal file
@@ -0,0 +1,141 @@
|
||||
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<RoutingView> createState() => _RoutingViewState();
|
||||
}
|
||||
|
||||
class _RoutingViewState extends State<RoutingView> {
|
||||
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)])),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user