Flutter-App mit WebView, STOMP-Client und Konfigurationssystem
- Flutter-App (app/) für iOS, Android, macOS, Windows und Linux erstellt - WebView-Startseite mit flutter_inappwebview (iOS/Android/macOS/Windows), Linux-Fallback mit url_launcher - STOMP-over-WebSocket: Topic-basierte Echtzeit-Kommunikation zwischen Flutter-App und Spring Boot Core - Core: STOMP-Broker (/ws/stomp), CallEventBroadcaster auf /topic/calls, StompMessageController für /app/ping und /app/broadcast - SecurityConfig: /ws/** permitAll + CSRF-Ausnahme - Asset-basierte Konfigurationsdatei (app_config.json) für Server-URL, STOMP-Reconnect, Topics und WebView-URL - launch.json um Flutter-Debug/Profile/Release-Konfigurationen erweitert - macOS: FLTEnableMergedPlatformUIThread deaktiviert (WKWebView-Kompatibilität), network.client Entitlement gesetzt - iOS: NSAllowsLocalNetworking für lokale Entwicklung - Android: INTERNET-Permission und usesCleartextTraffic Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
131
app/lib/pages/webview_home_page.dart
Normal file
131
app/lib/pages/webview_home_page.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../config/app_config.dart';
|
||||
import '../main.dart';
|
||||
|
||||
class WebViewHomePage extends StatefulWidget {
|
||||
const WebViewHomePage({super.key, required this.config});
|
||||
|
||||
final AppConfig config;
|
||||
|
||||
@override
|
||||
State<WebViewHomePage> createState() => _WebViewHomePageState();
|
||||
}
|
||||
|
||||
class _WebViewHomePageState extends State<WebViewHomePage> {
|
||||
InAppWebViewController? _controller;
|
||||
int _progress = 0;
|
||||
|
||||
bool get _platformSupportsWebView =>
|
||||
!kIsWeb &&
|
||||
(Platform.isAndroid ||
|
||||
Platform.isIOS ||
|
||||
Platform.isMacOS ||
|
||||
Platform.isWindows);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Swyx'),
|
||||
actions: [
|
||||
if (_platformSupportsWebView)
|
||||
IconButton(
|
||||
tooltip: 'Neu laden',
|
||||
onPressed: () => _controller?.reload(),
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Im Browser öffnen',
|
||||
onPressed: _openExternally,
|
||||
icon: const Icon(Icons.open_in_browser),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'STOMP',
|
||||
onPressed: _openStomp,
|
||||
icon: const Icon(Icons.bolt),
|
||||
),
|
||||
],
|
||||
),
|
||||
body:
|
||||
_platformSupportsWebView ? _buildWebView() : _buildLinuxFallback(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWebView() {
|
||||
return Column(
|
||||
children: [
|
||||
if (_progress < 100) LinearProgressIndicator(value: _progress / 100),
|
||||
Expanded(
|
||||
child: InAppWebView(
|
||||
initialUrlRequest:
|
||||
URLRequest(url: WebUri(widget.config.webviewUrl)),
|
||||
initialSettings: InAppWebViewSettings(
|
||||
javaScriptEnabled: true,
|
||||
transparentBackground: true,
|
||||
),
|
||||
onWebViewCreated: (controller) => _controller = controller,
|
||||
onProgressChanged: (_, progress) =>
|
||||
setState(() => _progress = progress),
|
||||
onLoadStart: (_, _) => setState(() => _progress = 0),
|
||||
onLoadStop: (_, _) => setState(() => _progress = 100),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLinuxFallback() {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.web_asset_off, size: 48),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Eingebettete Webansicht ist unter Linux nicht verfügbar.',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SelectableText(
|
||||
widget.config.webviewUrl,
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
FilledButton.icon(
|
||||
onPressed: _openExternally,
|
||||
icon: const Icon(Icons.open_in_browser),
|
||||
label: const Text('Im Browser öffnen'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openExternally() async {
|
||||
final uri = Uri.parse(widget.config.webviewUrl);
|
||||
if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text('Konnte ${widget.config.webviewUrl} nicht öffnen')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _openStomp() {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => StompDemoPage(config: widget.config),
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user