import 'dart:async'; import 'package:flutter/material.dart'; import 'backend.dart'; import 'screens/home.dart'; import 'widgets/player_bar.dart'; class App extends StatelessWidget { @override Widget build(BuildContext context) { final backend = Backend.of(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, // Added for sliders and FABs. Not everything seems to obey this. colorScheme: ColorScheme.dark( primary: Colors.amber, secondary: Colors.amber, ), snackBarTheme: SnackBarThemeData( backgroundColor: Colors.grey[800], contentTextStyle: TextStyle( color: Colors.white, ), behavior: SnackBarBehavior.floating, ), fontFamily: 'Libertinus Sans', ), home: Builder( builder: (context) { if (backend.status == BackendStatus.loading) { return Material( color: Theme.of(context).scaffoldBackgroundColor, ); } else if (backend.status == BackendStatus.setup) { return Material( color: Theme.of(context).scaffoldBackgroundColor, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Choose the base path for\nyour music library.', textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline6, ), SizedBox( height: 16.0, ), ListTile( leading: const Icon(Icons.folder_open), title: Text('Choose path'), onTap: () { backend.settings.chooseMusicLibraryUri(); }, ), ], ), ); } else { return Content(); } }, ), ); } } class Content extends StatefulWidget { @override _ContentState createState() => _ContentState(); } class _ContentState extends State with SingleTickerProviderStateMixin { final nestedNavigator = GlobalKey(); AnimationController playerBarAnimation; BackendState backend; StreamSubscription playerActiveSubscription; @override void initState() { super.initState(); playerBarAnimation = AnimationController( vsync: this, duration: Duration(milliseconds: 300), ); } @override void didChangeDependencies() { super.didChangeDependencies(); backend = Backend.of(context); playerBarAnimation.value = backend.player.active.value ? 1.0 : 0.0; if (playerActiveSubscription != null) { playerActiveSubscription.cancel(); } playerActiveSubscription = backend.player.active.listen((active) => active ? playerBarAnimation.forward() : playerBarAnimation.reverse()); } @override Widget build(BuildContext context) { // 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. return 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(), ), ), ); } @override void dispose() { super.dispose(); playerActiveSubscription.cancel(); } }