working media player on android

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-29 03:43:23 +00:00
co-authored by Copilot
parent 34476a56da
commit 38f4769d2d
7 changed files with 79 additions and 10 deletions
+29 -1
View File
@@ -1,4 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application <application
android:label="kryz_go_flutter" android:label="kryz_go_flutter"
android:name="${applicationName}" android:name="${applicationName}"
@@ -30,6 +37,27 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<service
android:name="com.ryanheise.audioservice.AudioService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaPlayback"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<receiver
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
</application> </application>
<!-- Required to query activities that can process text, see: <!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and https://developer.android.com/training/package-visibility and
@@ -1,5 +1,5 @@
package com.example.kryz_go_flutter package com.example.kryz_go_flutter
import io.flutter.embedding.android.FlutterActivity import com.ryanheise.audioservice.AudioServiceActivity
class MainActivity : FlutterActivity() class MainActivity : AudioServiceActivity()
+4
View File
@@ -26,6 +26,10 @@
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>
+28 -6
View File
@@ -1,14 +1,35 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:just_audio_background/just_audio_background.dart';
import 'streaming_audio_business_object.dart'; import 'streaming_audio_business_object.dart';
void main() { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await JustAudioBackground.init(
androidNotificationChannelId: 'com.kryzgoflutter.audio.channel',
androidNotificationChannelName: 'KRYZ Audio Playback',
androidNotificationOngoing: true,
);
runApp(const MyApp()); runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatefulWidget {
const MyApp({super.key}); const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final StreamingAudioBusinessObject _audio = StreamingAudioBusinessObject();
@override
void dispose() {
_audio.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
@@ -16,21 +37,23 @@ class MyApp extends StatelessWidget {
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey), colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey),
), ),
home: const MainPage(), home: MainPage(audio: _audio),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
} }
} }
class MainPage extends StatefulWidget { class MainPage extends StatefulWidget {
const MainPage({super.key}); const MainPage({super.key, required this.audio});
final StreamingAudioBusinessObject audio;
@override @override
State<MainPage> createState() => _MainPageState(); State<MainPage> createState() => _MainPageState();
} }
class _MainPageState extends State<MainPage> { class _MainPageState extends State<MainPage> {
final StreamingAudioBusinessObject _audio = StreamingAudioBusinessObject(); StreamingAudioBusinessObject get _audio => widget.audio;
@override @override
void initState() { void initState() {
@@ -59,7 +82,6 @@ class _MainPageState extends State<MainPage> {
@override @override
void dispose() { void dispose() {
_audio.removeListener(_handleAudioStateChanged); _audio.removeListener(_handleAudioStateChanged);
_audio.dispose();
super.dispose(); super.dispose();
} }
+11 -1
View File
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:just_audio_background/just_audio_background.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
class StreamingAudioBusinessObject extends ChangeNotifier { class StreamingAudioBusinessObject extends ChangeNotifier {
@@ -28,7 +29,16 @@ class StreamingAudioBusinessObject extends ChangeNotifier {
Future<void> play() async { Future<void> play() async {
if (!_isSourceLoaded) { if (!_isSourceLoaded) {
await _player.setUrl(streamUrl); await _player.setAudioSource(
AudioSource.uri(
Uri.parse(streamUrl),
tag: const MediaItem(
id: 'kryz-live-stream',
title: 'KRYZ Live Stream',
artist: 'KRYZ Radio',
),
),
);
_isSourceLoaded = true; _isSourceLoaded = true;
} }
@@ -5,10 +5,14 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import audio_service
import audio_session import audio_session
import just_audio import just_audio
import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
} }
+1
View File
@@ -35,6 +35,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
just_audio: ^0.10.5 just_audio: ^0.10.5
just_audio_background: ^0.0.1-beta.17
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: