Skip to content
Snippets Groups Projects
Commit f5e5e00c authored by Erik Hinkelmanns's avatar Erik Hinkelmanns
Browse files

- implemented spotify streaming for android (Disclaimer: spotify app must be...

- implemented spotify streaming for android (Disclaimer:  spotify app must be installed and user has to be logged in with premium account)
parent 0e5e367b
No related branches found
No related tags found
No related merge requests found
.env 0 → 100644
CLIENT_ID=c66416c22e044a128ba80121bcfc7000
REDIRECT_URL=http://localhost:8888/callback
\ No newline at end of file
import 'dart:async';
import 'dart:io';
import 'package:ambient/widgets/musicPlayer.dart';
import 'package:ambient/widgets/MusicPlayerState.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'dart:math' as math;
import 'package:file_picker/file_picker.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:spotify_sdk/models/connection_status.dart';
import 'package:spotify_sdk/models/image_uri.dart';
import 'package:spotify_sdk/models/player_context.dart';
import 'package:spotify_sdk/spotify_sdk.dart';
import 'package:spotify_sdk/models/player_state.dart';
import 'dart:developer' as developer;
@override
State<StatefulWidget> createState() => _HomePageState();
}
final player = AudioPlayer();
import 'main.dart';
class _HomePageState extends State<HomePage> {
void printErr(String msg) {
developer.log(msg);
}
late List<File> files;
static Color color = Colors.black;
bool isPlaying = false;
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
color =
Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
color: color,
height: 200,
width: 200,
child: GestureDetector(
onTap: () async {
try {
FilePickerResult? result = await FilePicker.platform
.pickFiles(allowMultiple: true);
return StreamBuilder<ConnectionStatus>(
stream: SpotifySdk.subscribeConnectionStatus(),
builder: (context, snapshot) {
//_connected = false
var data = snapshot.data;
if (data != null) {
MusicPlayerState.of(context).connected = data.connected;
}
return StreamBuilder<void>(
stream: MusicPlayerState.of(context).rebuildStream.stream,
builder: (context, _) => _sampleFlowWidget(context),
);
},
);
}
if (result != null) {
player.setFilePath(result.files.single.path!);
files = result.paths.map((path) => File(path!)).toList();
} else {
// User canceled the picker
Widget utilityBar(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: const Icon(Icons.queue_music),
iconSize: 50,
onPressed: queue,
),
IconButton(
icon: const Icon(Icons.playlist_play),
iconSize: 50,
onPressed: play,
),
IconButton(
icon: const Icon(Icons.info),
iconSize: 50,
color: primaryColor,
onPressed: () => checkIfAppIsActive(context),
),
MusicPlayerState.of(context).connected
? IconButton(
onPressed: MusicPlayerState.of(context).disconnect,
icon: const Icon(Icons.exit_to_app),
)
: Container()
],
);
}
} catch (e) {
print("Error loading file: $e");
Widget _sampleFlowWidget(BuildContext context) {
return Stack(
children: [
ListView(
padding: const EdgeInsets.all(8),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
TextButton(
onPressed:
MusicPlayerState.of(context).connectToSpotifyRemote,
child: const Icon(Icons.settings_remote),
),
IconButton(
icon: const Icon(Icons.info),
iconSize: 50,
color: primaryColor,
onPressed: () => checkIfAppIsActive(context),
),
],
),
const Divider(),
MusicPlayerState.of(context).connected
? _buildPlayerStateWidget()
: const Center(
child: Text('Not connected'),
),
const Divider(),
/*const Text(
'Player Context',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
_connected
? _buildPlayerContextWidget()
: const Center(
child: Text('Not connected'),
),
_connected ? utilityBar(context) : Text("noUtilBar"),*/
],
),
MusicPlayerState.of(context).loading
? Container(
color: Colors.black12,
child: const Center(child: CircularProgressIndicator()))
: const SizedBox(),
],
);
}
},
Widget _buildPlayerStateWidget() {
return StreamBuilder<PlayerState>(
stream: SpotifySdk.subscribePlayerState(),
builder: (BuildContext context, AsyncSnapshot<PlayerState> snapshot) {
var track = snapshot.data?.track;
MusicPlayerState.of(context).currentTrackImageUri = track?.imageUri;
var playerState = snapshot.data;
if (playerState == null || track == null) {
return Center(
child: Text('Not connected'),
);
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('''
${track.name}
${track.artist.name}
${track.album.name}
''',
maxLines: 20,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.black)),
MusicPlayerState.of(context).connected
? spotifyImageWidget(track.imageUri)
: const Text('Connect to see an image...'),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
iconSize: 50.0,
icon: const Padding(
padding: EdgeInsets.zero,
child: Icon(Icons.skip_previous_outlined)),
onPressed: skipPrevious,
),
playerState.isPaused
? IconButton(
icon: const Icon(Icons.play_arrow_outlined),
iconSize: 50,
onPressed: resume,
)
: IconButton(
icon: const Icon(Icons.pause_outlined),
iconSize: 50,
color: primaryColor,
onPressed: pause,
),
IconButton(
iconSize: 50.0,
icon: const Padding(
padding: EdgeInsets.zero,
child: Icon(Icons.skip_next_outlined)),
onPressed: skipNext,
),
ControlButtons(player),
SongDescription(player),
],
),
],
);
},
);
}
Widget _buildPlayerContextWidget() {
return StreamBuilder<PlayerContext>(
stream: SpotifySdk.subscribePlayerContext(),
initialData: PlayerContext('', '', '', ''),
builder: (BuildContext context, AsyncSnapshot<PlayerContext> snapshot) {
var playerContext = snapshot.data;
if (playerContext == null) {
return const Center(
child: Text('Not connected'),
);
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Title: ${playerContext.title}'),
Text('Subtitle: ${playerContext.subtitle}'),
Text('Type: ${playerContext.type}'),
Text('Uri: ${playerContext.uri}'),
],
);
},
);
}
Widget spotifyImageWidget(ImageUri image) {
return FutureBuilder(
future: SpotifySdk.getImage(
imageUri: image,
dimension: ImageDimension.large,
),
builder: (BuildContext context, AsyncSnapshot<Uint8List?> snapshot) {
if (snapshot.hasData) {
return Image.memory(snapshot.data!);
} else if (snapshot.hasError) {
setStatus(snapshot.error.toString());
return SizedBox(
width: ImageDimension.large.value.toDouble(),
height: ImageDimension.large.value.toDouble(),
child: const Center(child: Text('Error getting image')),
);
} else {
return SizedBox(
width: ImageDimension.large.value.toDouble(),
height: ImageDimension.large.value.toDouble(),
child: const Center(child: Text('Getting image...')),
);
}
});
}
Future getPlayerState() async {
try {
return await SpotifySdk.getPlayerState();
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> queue() async {
try {
await SpotifySdk.queue(
spotifyUri: 'spotify:track:58kNJana4w5BIjlZE2wq5m');
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> play() async {
try {
await SpotifySdk.play(spotifyUri: 'spotify:track:58kNJana4w5BIjlZE2wq5m');
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> pause() async {
try {
await SpotifySdk.pause();
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> resume() async {
try {
await SpotifySdk.resume();
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> skipNext() async {
try {
await SpotifySdk.skipNext();
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> skipPrevious() async {
try {
await SpotifySdk.skipPrevious();
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
Future<void> checkIfAppIsActive(BuildContext context) async {
try {
var isActive = await SpotifySdk.isSpotifyAppActive;
final snackBar = SnackBar(
content: Text(isActive
? 'Spotify app connection is active (currently playing)'
: 'Spotify app connection is not active (currently not playing)'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} on PlatformException catch (e) {
setStatus(e.code, message: e.message);
} on MissingPluginException {
setStatus('not implemented');
}
}
void setStatus(String code, {String? message}) {
var text = message ?? '';
printErr(text);
}
}
Color darken(Color color, [double amount = .1]) {
assert(amount >= 0 && amount <= 1);
......@@ -82,12 +348,7 @@ Color ligten(Color color, [double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(color);
final hslLight =
hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
final hslLight = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
return hslLight.toColor();
}
import 'package:ambient/widgets/MusicPlayerState.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:ambient/searchpage.dart';
import 'package:ambient/widgets/navbars.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'homepage.dart';
import 'moodpage.dart';
import 'searchpage.dart';
void main() async {
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp;
await dotenv.load(fileName: '.env');
runApp(const MyApp());
}
......@@ -21,7 +22,9 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HUD(),
home: MusicPlayerState(
child: HUD(),
),
);
}
}
......
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import '../homepage.dart';
import '../main.dart';
class ControlButtons extends StatelessWidget {
......@@ -60,6 +59,9 @@ class ControlButtons extends StatelessWidget {
}
}
class SongDescription extends StatelessWidget {
final AudioPlayer player;
......
import 'dart:async';
import 'package:ambient/widgets/MusicPlayerState.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:spotify_sdk/models/image_uri.dart';
import 'package:logger/logger.dart';
import 'package:spotify_sdk/spotify_sdk.dart';
class MusicPlayerState extends InheritedWidget {
MusicPlayerState({required super.child});
bool loading = false;
bool connected = false;
StreamController<void> rebuildStream = new StreamController.broadcast();
late ImageUri? currentTrackImageUri;
static MusicPlayerState? maybeOf(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<MusicPlayerState>();
static MusicPlayerState of(BuildContext context) {
final MusicPlayerState? result = maybeOf(context);
assert(result != null, 'No PlayerState found in context');
return result!;
}
void setLoading(bool loading){
this.loading = loading;
rebuildStream.sink.add(Null);
}
Future<void> disconnect() async {
try {
setLoading(true);
var result = await SpotifySdk.disconnect();
setStatus(result ? 'disconnect successful' : 'disconnect failed');
setLoading(false);
} on PlatformException catch (e) {
setLoading(false);
setStatus(e.code, message: e.message);
} on MissingPluginException {
setLoading(false);
setStatus('not implemented');
}
}
Future<void> connectToSpotifyRemote() async {
try {
setLoading(true);
var result = await SpotifySdk.connectToSpotifyRemote(
clientId: dotenv.env['CLIENT_ID'].toString(),
redirectUrl: dotenv.env['REDIRECT_URL'].toString());
setStatus(result
? 'connect to spotify successful'
: 'connect to spotify failed');
setLoading(false);
} on PlatformException catch (e) {
setLoading(false);
setStatus(e.code, message: e.message);
} on MissingPluginException {
setLoading(false);
setStatus('not implemented');
}
}
void setStatus(String code, {String? message}) {
var text = message ?? '';
print(text);
}
@override
bool updateShouldNotify(covariant MusicPlayerState oldWidget) {
return loading != oldWidget.loading && connected != oldWidget.connected;
}
}
......@@ -181,6 +181,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
flutter_dotenv:
dependency: "direct main"
description:
name: flutter_dotenv
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.2"
flutter_lints:
dependency: "direct dev"
description:
......
......@@ -48,6 +48,7 @@ dependencies:
flutter_custom_clippers: ^2.1.0
cloud_firestore: ^4.3.0
spotify_sdk: ^2.3.0
flutter_dotenv: ^5.0.2
dev_dependencies:
flutter_test:
......@@ -72,7 +73,8 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
assets:
- .env
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment