mirror of
https://github.com/johrpan/musicus_mobile.git
synced 2025-10-26 18:57:25 +01:00
Initial commit
This commit is contained in:
commit
17f3040645
37 changed files with 1256 additions and 0 deletions
78
lib/app.dart
Normal file
78
lib/app.dart
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'backend.dart';
|
||||
import 'screens/home.dart';
|
||||
import 'widgets/player_bar.dart';
|
||||
|
||||
class App extends StatefulWidget {
|
||||
@override
|
||||
_AppState createState() => _AppState();
|
||||
}
|
||||
|
||||
class _AppState extends State<App> with SingleTickerProviderStateMixin {
|
||||
final nestedNavigator = GlobalKey<NavigatorState>();
|
||||
|
||||
AnimationController playerBarAnimation;
|
||||
Backend backend;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
playerBarAnimation = AnimationController(
|
||||
vsync: this,
|
||||
duration: Duration(milliseconds: 300),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
backend = Backend.of(context);
|
||||
backend.playerActive.listen((active) =>
|
||||
active ? playerBarAnimation.forward() : playerBarAnimation.reverse());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Musicus',
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
accentColor: Colors.amber,
|
||||
textSelectionColor: Colors.grey[600],
|
||||
cursorColor: Colors.amber,
|
||||
textSelectionHandleColor: Colors.amber,
|
||||
toggleableActiveColor: Colors.amber,
|
||||
fontFamily: 'Libertinus Sans',
|
||||
),
|
||||
// The nested Navigator is for every screen from which the player bar at
|
||||
// the bottom should be accessible. The WillPopScope widget intercepts
|
||||
// taps on the system back button and redirects them to the nested
|
||||
// navigator.
|
||||
home: WillPopScope(
|
||||
onWillPop: () async => !(await nestedNavigator.currentState.maybePop()),
|
||||
child: Scaffold(
|
||||
body: Navigator(
|
||||
key: nestedNavigator,
|
||||
onGenerateRoute: (settings) => settings.name == '/'
|
||||
? MaterialPageRoute(
|
||||
builder: (context) => HomeScreen(),
|
||||
)
|
||||
: null,
|
||||
initialRoute: '/',
|
||||
),
|
||||
bottomNavigationBar: SizeTransition(
|
||||
sizeFactor: CurvedAnimation(
|
||||
curve: Curves.easeOut,
|
||||
parent: playerBarAnimation,
|
||||
),
|
||||
axisAlignment: -1.0,
|
||||
child: PlayerBar(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/backend.dart
Normal file
49
lib/backend.dart
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
class Backend extends InheritedWidget {
|
||||
static Backend of(BuildContext context) =>
|
||||
context.inheritFromWidgetOfExactType(Backend);
|
||||
|
||||
final Widget child;
|
||||
|
||||
final playerActive = BehaviorSubject.seeded(false);
|
||||
final playing = BehaviorSubject.seeded(false);
|
||||
final position = BehaviorSubject.seeded(0.0);
|
||||
|
||||
Backend({
|
||||
@required this.child,
|
||||
}) : super(child: child);
|
||||
|
||||
void startPlayer() {
|
||||
playerActive.add(true);
|
||||
}
|
||||
|
||||
void playPause() {
|
||||
playing.add(!playing.value);
|
||||
if (playing.value) {
|
||||
simulatePlay();
|
||||
}
|
||||
}
|
||||
|
||||
void seekTo(double pos) {
|
||||
position.add(pos);
|
||||
}
|
||||
|
||||
Future<void> simulatePlay() async {
|
||||
while (playing.value) {
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
if (position.value >= 0.99) {
|
||||
position.add(0.0);
|
||||
} else {
|
||||
position.add(position.value + 0.01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(Backend old) =>
|
||||
playerActive != old.playerActive ||
|
||||
playing != old.playing ||
|
||||
position != old.position;
|
||||
}
|
||||
10
lib/main.dart
Normal file
10
lib/main.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'app.dart';
|
||||
import 'backend.dart';
|
||||
|
||||
void main() {
|
||||
runApp(Backend(
|
||||
child: App(),
|
||||
));
|
||||
}
|
||||
29
lib/screens/home.dart
Normal file
29
lib/screens/home.dart
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backend = Backend.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Musicus'),
|
||||
),
|
||||
// For debugging purposes
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text('Start player'),
|
||||
onTap: backend.startPlayer,
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Play/Pause'),
|
||||
onTap: backend.playPause,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
16
lib/screens/program.dart
Normal file
16
lib/screens/program.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ProgramScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.keyboard_arrow_down),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text('Program'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
42
lib/widgets/play_pause_button.dart
Normal file
42
lib/widgets/play_pause_button.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
|
||||
class PlayPauseButton extends StatefulWidget {
|
||||
@override
|
||||
_PlayPauseButtonState createState() => _PlayPauseButtonState();
|
||||
}
|
||||
|
||||
class _PlayPauseButtonState extends State<PlayPauseButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
AnimationController playPauseAnimation;
|
||||
Backend backend;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
playPauseAnimation =
|
||||
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
backend = Backend.of(context);
|
||||
backend.playing.listen((playing) =>
|
||||
playing ? playPauseAnimation.forward() : playPauseAnimation.reverse());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: AnimatedIcon(
|
||||
icon: AnimatedIcons.play_pause,
|
||||
progress: playPauseAnimation,
|
||||
),
|
||||
onPressed: backend.playPause,
|
||||
);
|
||||
}
|
||||
}
|
||||
56
lib/widgets/player_bar.dart
Normal file
56
lib/widgets/player_bar.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../screens/program.dart';
|
||||
|
||||
import 'play_pause_button.dart';
|
||||
|
||||
class PlayerBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backend = Backend.of(context);
|
||||
|
||||
return BottomAppBar(
|
||||
child: InkWell(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
StreamBuilder(
|
||||
stream: backend.position,
|
||||
builder: (context, snapshot) => LinearProgressIndicator(
|
||||
value: snapshot.data,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.keyboard_arrow_up),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Composer',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text('Work: Movement'),
|
||||
],
|
||||
),
|
||||
),
|
||||
PlayPauseButton(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ProgramScreen(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue