Snippets

Kevin Armstrong Apple Music App - Library Screen

Created by Kevin Armstrong last modified
import 'package:flutter/material.dart';

class AlbumArt extends StatelessWidget {
  final String tag;
  final double size;
  final String image;
  final bool shadow;

  AlbumArt({this.tag, this.size, this.image, this.shadow: false});

  @override
  Widget build(BuildContext context) {
    return Hero(
      tag: tag,
      child: new Container(
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(5.0),
            color: Colors.grey.shade700,
            boxShadow: [
              BoxShadow(color: Colors.grey, blurRadius: shadow ? 5.0 : 0.0, spreadRadius: 0.0, offset: Offset(0.5, 0.5))
            ],
            image: DecorationImage(image: AssetImage(image), fit: BoxFit.contain)
        ),
        height: size,
        width: size,
      ),
    );
  }
}
/*
Assets Helper. Provide convenient access to asset strings
e.g.
class Assets {
  static final String album1 = 'assets/images/albums/a1.jpg';
  static final String album2 = 'assets/images/albums/a2.jpg';
  ...
  static final String album10 = 'assets/images/albums/a10.jpg';
}
*/
import 'package:fluids/utils/assets.dart';

final List<List<Album>> albumsList = [
  [
    Album(title: 'Juicy', artist: 'Willie Bobo', artwork: Assets.album1),
    Album(title: 'Edicion Limitada', artist: 'Eddie Santiago', artwork: Assets.album2),
  ],
  [
    Album(title: 'Infinito', artist: 'Oscar D\'Leon', artwork: Assets.album3),
    Album(title: 'Para Ti', artist: 'Anthony Cruz', artwork: Assets.album4),
  ],
  [
    Album(title: 'Tranquilo', artist: 'Frankie Ruiz', artwork: Assets.album5),
    Album(title: '10 Exitos, Vol 2', artist: 'Tommy Olivencia', artwork: Assets.album6),
  ],
  [
    Album(title: 'Uno Mismo', artist: 'Tony Vega', artwork: Assets.album7),
    Album(title: 'Mi Meta', artist: 'Domingo Quinones', artwork: Assets.album8),
  ],
  [
    Album(title: 'The Best', artist: 'Grupo Niche', artwork: Assets.album9),
    Album(title: 'Un Tipo Comun', artist: 'Tito Nieves', artwork: Assets.album10),
  ],
];

class Album {
  final String title;
  final String artist;
  final String artwork;

  Album({this.title, this.artist, this.artwork});
}
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import './album_list.dart';
import './album_art.dart';

class Detail extends StatefulWidget {
  final Album album;

  Detail(this.album);

  @override
  _DetailState createState() => new _DetailState();
}

class _DetailState extends State<Detail> {
  bool showLyrics = false;

  @override
  Widget build(BuildContext context) {
    Album album = widget.album;
    return Stack(
      alignment: Alignment.bottomCenter,
      children: <Widget>[
        new Positioned(
          top: 40.0,
          left: 15.0,
          right: 15.0,
          bottom: 0.0,
          child: Container(
            width: double.infinity,
            height: double.infinity,
            alignment: Alignment.topCenter,
            padding: EdgeInsets.all(15.0),
            decoration: BoxDecoration(
              color: Colors.white30,
              borderRadius: BorderRadius.circular(10.0),
            ),
          ),
        ),
        new Positioned(
          top: 50.0,
          left: 0.0,
          right: 0.0,
          bottom: 0.0,
          child: Container(
            width: double.infinity,
            height: double.infinity,
            alignment: Alignment.topCenter,
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(10.0),
            ),
            child: Material(
              color: Colors.white,
              borderRadius: BorderRadius.circular(10.0),
              child: SingleChildScrollView(
                physics: AlwaysScrollableScrollPhysics(),
                child: Column(
                  children: <Widget>[
                    GestureDetector(
                        onTap: (){
                          Navigator.pop(context);
                        },
                        child: Icon(Icons.keyboard_arrow_down, color: Colors.grey, size: 40.0,)
                    ),
                    Container(
                      padding: EdgeInsets.symmetric(vertical: 20.0),
                      alignment: Alignment.center,
                      child: AlbumArt(tag: album.title, image: album.artwork, shadow: false, size: MediaQuery.of(context).size.width - 100,),
                    ),
                    _makeDetailProgress(),
                    _makeDetailTrackInfo(album.title, album.artist),
                    _makeDetailControls(),
                    _makeVolumeControl(),
                    _makeExtraControls(),
                    Divider(height: 2.0,),
                    _makeDetailButtons(),
                    Divider(height: 2.0,),
                    _makeDetailLyrics(),
                    Divider(height: 2.0,),
                    _makeUpNext(),
                    SizedBox(height: 60.0,),
                  ],
                ),
              ),
            ),
          ),
        )
      ],
    );
  }

  _makeDetailProgress(){
    return Column(
      children: <Widget>[
        Slider(
          value: 39.0,
          activeColor: Colors.grey.shade700,
          inactiveColor: Colors.grey.shade200,
          min: 0.0,
          max: 157.0,
          onChanged: (double v){},
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 15.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Text('0:39', style: Theme.of(context).textTheme.caption,),
              Text('-1:58', style: Theme.of(context).textTheme.caption,),
            ],
          ),
        )
      ],
    );
  }

  _makeDetailTrackInfo(String title, String artist){
    return Padding(
      padding: EdgeInsets.all(20.0),
      child: Column(
        children: <Widget>[
          Text(title,
            style: Theme.of(context).textTheme.headline.copyWith(fontWeight: FontWeight.w600),),
          Text('$artist - $title',
            style: Theme.of(context).textTheme.headline.copyWith(color: Colors.red),
            maxLines: 1,
          ),
        ],
      ),
    );
  }

  _makeDetailControls(){
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        CupertinoButton(child: Icon(Icons.fast_rewind, size: 50.0,), pressedOpacity: 0.8, onPressed: (){},),
        CupertinoButton(child: Icon(Icons.play_arrow, size: 75.0,), pressedOpacity: 0.8, onPressed: (){}),
        CupertinoButton(child: Icon(Icons.fast_forward, size: 50.0,), pressedOpacity: 0.8, onPressed: (){}),
      ],
    );
  }

  _makeVolumeControl() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 10.0),
      child: Row(
        children: <Widget>[
          Icon(Icons.volume_down, size: 18.0, color: Colors.grey,),
          Expanded(
            child: CupertinoSlider(
              value: 65.0,
              min: 0.0,
              max: 100.0,
              onChanged: (double v){},
              activeColor: Colors.grey.shade700,
            ),
          ),
          Icon(Icons.volume_up, size: 18.0, color: Colors.grey,),
        ],
      ),
    );
  }

  _makeExtraControls() {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 15.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          CupertinoButton(child: Icon(Icons.add, color: Colors.red,), pressedOpacity: 0.8, onPressed: (){}),
          CupertinoButton(child: Icon(Icons.airplay, color: Colors.red,), pressedOpacity: 0.8, onPressed: (){}),
          CupertinoButton(child: Icon(Icons.more_horiz, color: Colors.red,), pressedOpacity: 0.8, onPressed: (){}),
        ],
      ),
    );
  }

  _makeDetailButtons() {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 15.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          SizedBox(width: 15.0,),
          Expanded(
            child: CupertinoButton(
              padding: EdgeInsets.all(0.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Icon(Icons.shuffle, color: Colors.red,),
                  Text('  Shuffle', style: TextStyle(color: Colors.red),)
                ],
              ),
              onPressed: (){},
              color: Colors.grey.shade100,
              pressedOpacity: 0.4,
            ),
          ),
          SizedBox(width: 15.0,),
          Expanded(
            child: CupertinoButton(
              padding: EdgeInsets.all(0.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Icon(Icons.repeat, color: Colors.white,),
                  Text('  Repeat', style: TextStyle(color: Colors.white,),)
                ],
              ),
              onPressed: (){},
              color: Colors.red,
              pressedOpacity: 0.4,
            ),
          ),
          SizedBox(width: 15.0,),
        ],
      ),
    );
  }

  _makeDetailLyrics() {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
      child: Column(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Text('Lyrics', style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 26.0,
              ),),
              CupertinoButton(
                child: Text(showLyrics ? 'Hide' : 'Show',
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 18.0
                  ),
                ),
                onPressed: (){
                  setState(() {
                    showLyrics = !showLyrics;
                    print(showLyrics);
                  });
                },
              ),
            ],
          ),
          showLyrics ? Divider() : Container(),
          showLyrics ? Container(
            alignment: Alignment.topLeft,
            child: Text('Tu eres la rueda, yo soy el camino\n'+
                'pasas encima de mi dando vueltas\n'+
                'tu rodaras porque ese es tu destino\n'+
                'sin encontrar nadie que te detenga.\n\n'+
                'Quise pararte pero ibas sin frenos\n'+
                'y tus rodadas me hicieron pedazos\n'+
                'porque no quieres los caminos buenos\n'+
                'y agarras todo lo que hay en tus pasos.\n\n'+
                'Yo que soñaba, con ser en tu vida\n'+
                'el terminar de tus vueltas al mundo\n'+
                'te vi pasar como nave perdida\n'+
                'de aquí pa\'lla sin agarrar tu mundo.\n\n'+
                'Ninguna rueda me había lastimado\n'+
                'y me pasaron de las más pesadas\n'+
                'pero contigo quede destrozado\n'+
                'porque no hiciste ninguna parada.',
              style: TextStyle(fontSize: 18.0),
            ),
          ) : Container(),
        ],
      ),
    );
  }

  _makeUpNext(){
    return Container(
      padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
      color: Colors.grey.shade100,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Text('Up Next', style: TextStyle(
            fontWeight: FontWeight.bold,
            fontSize: 26.0,
          ),),
          Container(),
        ],
      ),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import './album_list.dart';

class LibraryScreen extends StatelessWidget {
  final Function onTap;

  LibraryScreen({this.onTap});

  @override
  Widget build(BuildContext context) {
    return new Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        new Padding(
          padding: EdgeInsets.only(left: 20.0),
          child: Column(
            children: <Widget>[
              Divider(),
              _makeListItem(context, 'Playlists'),
              Divider(),
              _makeListItem(context, 'Artists'),
              Divider(),
              _makeListItem(context, 'Albums'),
              Divider(),
              _makeListItem(context, 'Songs'),
              Divider(),
              _makeListItem(context, 'Downloaded Music'),
              Divider(),
            ],
          ),
        ),
        new Padding(
          padding: EdgeInsets.only(top: 20.0, bottom: 10.0, left: 20.0),
          child: Text(
            'Recently Added',
            style: Theme.of(context).textTheme.headline.copyWith(
              fontWeight: FontWeight.w700
            ),
          ),
        ),
        new Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0),
          child: new ListView.builder(
            shrinkWrap: true,
            primary: false,
            itemCount: albumsList.length,
            itemBuilder: (context, index){
              List<Album> albumRowData = albumsList[index];
              return _makeAlbumRow(context, [
                albumRowData.first,
                albumRowData.last,  //TODO: test to see if this exists
              ]);
            },
          ),
        ),
        SizedBox(height: 100.0,),
      ],
    );
  }

  _makeAlbum(BuildContext context, Album album) {
    double size = MediaQuery.of(context).size.width/2 - 40;
    return GestureDetector(
      onTap: () => onTap(album),
      child: Card(
        child: new Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Container(
                height: size,
                width: size,
                decoration: BoxDecoration(
                  image: DecorationImage(image: AssetImage(album.artwork), fit: BoxFit.cover),
                ),
              ),
              new Padding(
                padding: const EdgeInsets.symmetric(vertical: 4.0),
                child: Text(album.title, style: TextStyle(fontSize: 16.0),),
              ),
              Text(album.artist, style: TextStyle(color: Colors.grey),),
            ],
          ),
        ),
        elevation: 0.0,
      ),
    );
  }

  _makeListItem(BuildContext context, String s) {
    return new CupertinoButton(
      pressedOpacity: 0.6,
      onPressed: (){},
      padding: EdgeInsets.all(0.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Text(s,
            style: Theme.of(context).textTheme.headline.copyWith(
              color: Colors.red
            ),
          ),
          new Padding(
            padding: const EdgeInsets.all(5.0),
            child: Icon(Icons.chevron_right, color: Colors.grey.shade300, size: 30.0,),
          )
        ],
      ),
    );
  }

  _makeAlbumRow(BuildContext context, List<Album> albums){
    if(albums.length > 0) {
      Album album1 = albums.first;
      Album album2 = albums.length > 1 ? albums.last : null;
      return Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          _makeAlbum(context, album1),
          album2 != null ? _makeAlbum(context, album2) : Container(),
        ],
      );
    } else {
      return Container();
    }
  }
}
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:fluids/utils/md_icons.dart'; //custom icons
import './library.dart';
import './album_art.dart';
import './detail.dart';
import './album_list.dart';

class AppleMusicMain extends StatefulWidget {
  AppleMusicMain({Key key}) : super(key: key);

  @override
  _AppleMusicMainState createState() => new _AppleMusicMainState();
}

class _AppleMusicMainState extends State<AppleMusicMain> {
  int selectedTab = 0;
  String title = 'Library';
  bool showTitle = false;
  Album _selectedAlbum;

  @override
  initState() {
    _selectedAlbum = albumsList[0][0];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Theme(
      data: ThemeData(
        primaryColor: Colors.white,
      ),
      child: new Scaffold(
        appBar: new AppBar(
          backgroundColor: Colors.white,
          elevation: 0.0,
          title: AnimatedOpacity(
            opacity: showTitle ? 1.0 : 0.0,
            duration: Duration(milliseconds: showTitle ? 200 : 500),
            child: Text('Library', style: TextStyle(color: Colors.black),),
          ),
          centerTitle: true,
          iconTheme: IconThemeData(
            color: Colors.red,
          ),
          actions: <Widget>[
            CupertinoButton(
              child: Text('Edit',
                style: TextStyle(
                  color: Colors.red,
                  fontSize: 18.0
                ),
              ),
              onPressed: (){},
            )
          ],
        ),
        body: new Stack(
          children: <Widget>[
            new Positioned.fill(
              child: new NotificationListener(
                child: new SingleChildScrollView(
                  child: new Container(
                    color: Colors.white,
                    child: new Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        _makeHeader('Library', hide: showTitle,),
                        LibraryScreen(onTap: (Album album){
                          setState(() {
                            _selectedAlbum = album;
                          });
                        },),
                      ],
                    ),
                  ),
                ),
                onNotification: (ScrollUpdateNotification notification){
                  setState(() {
                    if(notification.metrics.pixels > 52 && !showTitle){
                      showTitle = true;
                    } else if(notification.metrics.pixels < 52 && showTitle){
                      showTitle = false;
                    }
                  });
                },
              ),
            ),
            new Positioned(
              bottom: 0.0,
              left: 0.0,
              right: 0.0,
              child: _makeNowPlaying(context),
            )
          ],
        ),
        bottomNavigationBar: new BottomNavigationBar(
          items: [
            _makeNavButton(title: 'Library', icon: MDIcons.library_music),
            _makeNavButton(title: 'For You', icon: MDIcons.heart),
            _makeNavButton(title: 'Browse', icon: MDIcons.music_note),
            _makeNavButton(title: 'Radio', icon: MDIcons.radio),
            _makeNavButton(title: 'Search', icon: MDIcons.magnify),
          ],
          type: BottomNavigationBarType.fixed,
          currentIndex: selectedTab,
          onTap: (int value){
            setState(() {
              selectedTab = value;
            });
          },
          fixedColor: Colors.red,
        ),
      ),
    );
  }

  _makeHeader(String title, {bool hide: false}) {
    return new Padding(
      padding: const EdgeInsets.only(left: 20.0),
      child: new Text(
        title,
        style: Theme.of(context).textTheme.display2.copyWith(
            color: hide ? Colors.transparent : Colors.black,
            fontWeight: FontWeight.bold,
            letterSpacing: 1.0
        ),
      ),
    );
  }

  _makeNavButton({String title, IconData icon}) {
    return new BottomNavigationBarItem(
      icon: Icon(icon),
      title: Text(title),
      backgroundColor: Colors.white,
    );
  }

  _makeNowPlaying(BuildContext context) {
    return CupertinoButton(
      onPressed: (){
        Navigator.of(context).push(MaterialPageRoute(
          fullscreenDialog: true,
          builder: (BuildContext context){
            return Detail(_selectedAlbum);
          }
        ));
      },
      pressedOpacity: 0.9,
      color: Colors.grey.shade200.withAlpha(280).withOpacity(0.9),
      padding: EdgeInsets.all(0.0),
      child: Container(
        decoration: BoxDecoration(
          border: Border(
            top: BorderSide(color: Colors.grey.shade300, width: 1.0),
            bottom: BorderSide(color: Colors.grey.shade300, width: 1.0),
          ),
        ),
        padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 20.0),
        child: Row(
          children: <Widget>[
            AlbumArt(tag: _selectedAlbum.title, size: 50.0, image: _selectedAlbum.artwork, shadow: true),
            Expanded(
              child: new Padding(
                padding: const EdgeInsets.symmetric(horizontal: 20.0),
                child: Text(
                  _selectedAlbum.title,
                  style: Theme.of(context).textTheme.subhead.copyWith(
                    fontWeight: FontWeight.normal
                  ),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ),
            new Container(
              width: 40.0,
              child: IconButton(
                icon: Icon(Icons.pause),
                onPressed: (){},
                iconSize: 35.0,
              ),
            ),
            new Container(
              width: 40.0,
              child: IconButton(
                icon: Icon(Icons.fast_forward),
                onPressed: (){},
                iconSize: 35.0,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.