import 'package:flutter/material.dart'; import 'package:just_audio_background/just_audio_background.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'app_theme_business_object.dart'; import 'streaming_audio_business_object.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await JustAudioBackground.init( androidNotificationChannelId: 'com.kryzgoflutter.audio.channel', androidNotificationChannelName: 'KRYZ Audio Playback', androidNotificationOngoing: true, ); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { final StreamingAudioBusinessObject _audio = StreamingAudioBusinessObject(); @override void dispose() { _audio.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Split Screen Player', theme: AppThemeBusinessObject.lightTheme(), darkTheme: AppThemeBusinessObject.darkTheme(), themeMode: ThemeMode.system, home: MainPage(audio: _audio), debugShowCheckedModeBanner: false, ); } } class MainPage extends StatefulWidget { const MainPage({super.key, required this.audio}); final StreamingAudioBusinessObject audio; @override State createState() => _MainPageState(); } class _MainPageState extends State { StreamingAudioBusinessObject get _audio => widget.audio; late final WebViewController _webViewController; String _playbackStatusLabel() { switch (_audio.playbackStatus) { case StreamingPlaybackStatus.stopped: return 'Stopped'; case StreamingPlaybackStatus.buffering: return 'Buffering...'; case StreamingPlaybackStatus.playing: return 'Playing'; } } @override void initState() { super.initState(); _webViewController = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..loadRequest(Uri.parse('https://kryzradio.org')); _audio.addListener(_handleAudioStateChanged); } void _handleAudioStateChanged() { if (!mounted) { return; } setState(() { // Rebuild when playback state changes from the player. }); } Future _play() async { await _audio.play(); } Future _stop() async { await _audio.stop(); } @override void dispose() { _audio.removeListener(_handleAudioStateChanged); super.dispose(); } @override Widget build(BuildContext context) { final themeColors = AppThemeBusinessObject.colorsFor(context); return Scaffold( body: SafeArea( child: Column( children: [ Expanded( flex: 9, child: Padding( padding: const EdgeInsets.all(12), child: Container( width: double.infinity, decoration: BoxDecoration( border: Border.all( color: themeColors.webViewBorder, width: 2, ), borderRadius: BorderRadius.circular(8), color: themeColors.webViewBackground, ), child: ClipRRect( borderRadius: BorderRadius.circular(6), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), decoration: BoxDecoration( border: Border( bottom: BorderSide( color: themeColors.webViewBorder, ), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Station Information', style: Theme.of(context).textTheme.labelMedium ?.copyWith( color: themeColors.statusText, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 2), Text( 'https://kryzradio.org', style: Theme.of(context).textTheme.labelMedium ?.copyWith( color: Colors.blue, decoration: TextDecoration.underline, decorationColor: Colors.blue, ), ), ], ), ), Expanded( child: WebViewWidget(controller: _webViewController), ), ], ), ), ), ), ), Expanded( flex: 1, child: Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 12), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ themeColors.controlsGradientStart, themeColors.controlsGradientEnd, ], ), boxShadow: [ BoxShadow( color: themeColors.controlsShadow, blurRadius: 14, offset: Offset(0, 6), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: _audio.isStopped ? _play : null, child: const Text('▶️'), ), const SizedBox(width: 12), ElevatedButton( onPressed: _audio.isStopped ? null : _stop, child: const Text('⏹️'), ), const SizedBox(width: 16), Text( _playbackStatusLabel(), style: TextStyle(color: themeColors.statusText), ), ], ), ), ), ), ], ), ), ); } }