Functioning music player features
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -7,11 +7,21 @@ RUN apt-get update && apt-get install -y \
|
|||||||
wget curl unzip git xz-utils zip libglu1-mesa openjdk-17-jdk\
|
wget curl unzip git xz-utils zip libglu1-mesa openjdk-17-jdk\
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Become non-root
|
||||||
|
ARG USERNAME=vscode
|
||||||
|
ARG USER_UID=1000
|
||||||
|
ARG USER_GID=1000
|
||||||
|
|
||||||
|
ENV FLUTTER_VERSION=3.41.8
|
||||||
|
ENV FLUTTER_HOME=/opt/flutter
|
||||||
|
RUN groupadd --gid $USER_GID $USERNAME \
|
||||||
|
&& useradd --uid $USER_UID --gid $USER_GID -m -s /bin/bash $USERNAME
|
||||||
|
|
||||||
|
|
||||||
# Install Flutter
|
# Install Flutter
|
||||||
RUN git clone https://github.com/flutter/flutter.git /opt/flutter \
|
RUN git clone https://github.com/flutter/flutter.git ${FLUTTER_HOME} \
|
||||||
&& cd /opt/flutter \
|
&& cd ${FLUTTER_HOME} \
|
||||||
&& git checkout $FLUTTER_VERSION \
|
&& git checkout $FLUTTER_VERSION
|
||||||
&& flutter precache --android
|
|
||||||
|
|
||||||
# Set up Android SDK
|
# Set up Android SDK
|
||||||
ENV ANDROID_HOME=/opt/android-sdk
|
ENV ANDROID_HOME=/opt/android-sdk
|
||||||
@@ -23,4 +33,7 @@ RUN mkdir -p ${ANDROID_HOME}/cmdline-tools \
|
|||||||
&& mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \
|
&& mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \
|
||||||
&& rm commandlinetools.zip && yes | sdkmanager --licenses \
|
&& rm commandlinetools.zip && yes | sdkmanager --licenses \
|
||||||
&& sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.0.0"
|
&& sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.0.0"
|
||||||
# && chown -R node:node ${ANDROID_HOME}
|
RUN chown -R 1000:1000 ${ANDROID_HOME} && chown -R 1000:1000 ${FLUTTER_HOME}
|
||||||
|
|
||||||
|
USER $USERNAME
|
||||||
|
ENV HOME=/home/$USERNAME
|
||||||
@@ -6,15 +6,10 @@
|
|||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"ms-vscode.vscode-typescript-next"
|
"ms-vscode.vscode-typescript-next",
|
||||||
],
|
"dart-code.flutter"
|
||||||
"settings": {
|
|
||||||
"terminal.integrated.defaultProfile.linux": "bash"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"postCreateCommand": "flutter doctor -v",
|
|
||||||
"mounts": [
|
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"postCreateCommand": "flutter doctor -v"
|
||||||
|
}
|
||||||
@@ -1,2 +1,6 @@
|
|||||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
# This builtInKotlin flag was added automatically by Flutter migrator
|
||||||
|
android.builtInKotlin=false
|
||||||
|
# This newDsl flag was added automatically by Flutter migrator
|
||||||
|
android.newDsl=false
|
||||||
|
|||||||
+77
-81
@@ -1,5 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'streaming_audio_business_object.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
@@ -7,115 +9,109 @@ void main() {
|
|||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Flutter Demo',
|
title: 'Split Screen Player',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
// This is the theme of your application.
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey),
|
||||||
//
|
|
||||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
|
||||||
// the application has a purple toolbar. Then, without quitting the app,
|
|
||||||
// try changing the seedColor in the colorScheme below to Colors.green
|
|
||||||
// and then invoke "hot reload" (save your changes or press the "hot
|
|
||||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
|
||||||
// the command line to start the app).
|
|
||||||
//
|
|
||||||
// Notice that the counter didn't reset back to zero; the application
|
|
||||||
// state is not lost during the reload. To reset the state, use hot
|
|
||||||
// restart instead.
|
|
||||||
//
|
|
||||||
// This works for code too, not just values: Most code changes can be
|
|
||||||
// tested with just a hot reload.
|
|
||||||
colorScheme: .fromSeed(seedColor: Colors.deepPurple),
|
|
||||||
),
|
),
|
||||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
home: const MainPage(),
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MainPage extends StatefulWidget {
|
||||||
const MyHomePage({super.key, required this.title});
|
const MainPage({super.key});
|
||||||
|
|
||||||
// This widget is the home page of your application. It is stateful, meaning
|
|
||||||
// that it has a State object (defined below) that contains fields that affect
|
|
||||||
// how it looks.
|
|
||||||
|
|
||||||
// This class is the configuration for the state. It holds the values (in this
|
|
||||||
// case the title) provided by the parent (in this case the App widget) and
|
|
||||||
// used by the build method of the State. Fields in a Widget subclass are
|
|
||||||
// always marked "final".
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
State<MainPage> createState() => _MainPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MainPageState extends State<MainPage> {
|
||||||
int _counter = 0;
|
final StreamingAudioBusinessObject _audio = StreamingAudioBusinessObject();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_audio.addListener(_handleAudioStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleAudioStateChanged() {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void _incrementCounter() {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
// This call to setState tells the Flutter framework that something has
|
// Rebuild when playback state changes from the player.
|
||||||
// changed in this State, which causes it to rerun the build method below
|
|
||||||
// so that the display can reflect the updated values. If we changed
|
|
||||||
// _counter without calling setState(), then the build method would not be
|
|
||||||
// called again, and so nothing would appear to happen.
|
|
||||||
_counter++;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _play() async {
|
||||||
|
await _audio.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _stop() async {
|
||||||
|
await _audio.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_audio.removeListener(_handleAudioStateChanged);
|
||||||
|
_audio.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// This method is rerun every time setState is called, for instance as done
|
|
||||||
// by the _incrementCounter method above.
|
|
||||||
//
|
|
||||||
// The Flutter framework has been optimized to make rerunning build methods
|
|
||||||
// fast, so that you can just rebuild anything that needs updating rather
|
|
||||||
// than having to individually change instances of widgets.
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
body: SafeArea(
|
||||||
// TRY THIS: Try changing the color here to a specific color (to
|
|
||||||
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
|
|
||||||
// change color while the other colors stay the same.
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
||||||
// Here we take the value from the MyHomePage object that was created by
|
|
||||||
// the App.build method, and use it to set our appbar title.
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
// Center is a layout widget. It takes a single child and positions it
|
|
||||||
// in the middle of the parent.
|
|
||||||
child: Column(
|
child: Column(
|
||||||
// Column is also a layout widget. It takes a list of children and
|
|
||||||
// arranges them vertically. By default, it sizes itself to fit its
|
|
||||||
// children horizontally, and tries to be as tall as its parent.
|
|
||||||
//
|
|
||||||
// Column has various properties to control how it sizes itself and
|
|
||||||
// how it positions its children. Here we use mainAxisAlignment to
|
|
||||||
// center the children vertically; the main axis here is the vertical
|
|
||||||
// axis because Columns are vertical (the cross axis would be
|
|
||||||
// horizontal).
|
|
||||||
//
|
|
||||||
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
|
|
||||||
// action in the IDE, or press "p" in the console), to see the
|
|
||||||
// wireframe for each widget.
|
|
||||||
mainAxisAlignment: .center,
|
|
||||||
children: [
|
children: [
|
||||||
const Text('You have pushed the button this many times:'),
|
Expanded(
|
||||||
|
flex: 9,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.black26, width: 2),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
color: Colors.black87,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _audio.isPlaying ? null : _play,
|
||||||
|
child: const Text('Play'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _audio.isPlaying ? _stop : null,
|
||||||
|
child: const Text('Stop'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
Text(
|
Text(
|
||||||
'$_counter',
|
_audio.isPlaying ? 'Playing' : 'Stopped',
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
),
|
||||||
onPressed: _incrementCounter,
|
],
|
||||||
tooltip: 'Increment',
|
),
|
||||||
child: const Icon(Icons.add),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
||||||
|
class StreamingAudioBusinessObject extends ChangeNotifier {
|
||||||
|
StreamingAudioBusinessObject({
|
||||||
|
this.streamUrl = 'https://kryz.out.airtime.pro/kryz_a',
|
||||||
|
}) {
|
||||||
|
_playerStateSubscription = _player.playerStateStream.listen((state) {
|
||||||
|
final isCurrentlyPlaying =
|
||||||
|
state.playing && state.processingState != ProcessingState.completed;
|
||||||
|
|
||||||
|
if (_isPlaying != isCurrentlyPlaying) {
|
||||||
|
_isPlaying = isCurrentlyPlaying;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final String streamUrl;
|
||||||
|
final AudioPlayer _player = AudioPlayer();
|
||||||
|
late final StreamSubscription<PlayerState> _playerStateSubscription;
|
||||||
|
bool _isSourceLoaded = false;
|
||||||
|
bool _isPlaying = false;
|
||||||
|
|
||||||
|
bool get isPlaying => _isPlaying;
|
||||||
|
|
||||||
|
Future<void> play() async {
|
||||||
|
if (!_isSourceLoaded) {
|
||||||
|
await _player.setUrl(streamUrl);
|
||||||
|
_isSourceLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _player.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pause() async {
|
||||||
|
await _player.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_playerStateSubscription.cancel();
|
||||||
|
_player.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import audio_session
|
||||||
|
import just_audio
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
|
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# 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
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
Reference in New Issue
Block a user