import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'song_detail_tab.dart'; import 'utils.dart'; import 'widgets.dart'; class SongsTab extends StatefulWidget { static const title = 'Songs'; static const androidIcon = Icon(Icons.music_note); static const iosIcon = Icon(CupertinoIcons.music_note); const SongsTab({Key key, this.androidDrawer}) : super(key: key); final Widget androidDrawer; @override _SongsTabState createState() => _SongsTabState(); } class _SongsTabState extends State { static const _itemsLength = 50; final _androidRefreshKey = GlobalKey(); List colors; List songNames; @override void initState() { _setData(); super.initState(); } void _setData() { colors = getRandomColors(_itemsLength); songNames = getRandomNames(_itemsLength); } Future _refreshData() { return Future.delayed( // This is just an arbitrary delay that simulates some network activity. const Duration(seconds: 2), () => setState(() => _setData()), ); } Widget _listBuilder(BuildContext context, int index) { if (index >= _itemsLength) return null; // Show a slightly different color palette. Show poppy-ier colors on iOS // due to lighter contrasting bars and tone it down on Android. final color = defaultTargetPlatform == TargetPlatform.iOS ? colors[index] : colors[index].shade400; return SafeArea( top: false, bottom: false, child: Hero( tag: index, child: HeroAnimatingSongCard( song: songNames[index], color: color, heroAnimation: AlwaysStoppedAnimation(0), onPressed: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => SongDetailTab( id: index, song: songNames[index], color: color, ), ), ), ), ), ); } void _togglePlatform() { TargetPlatform _getOppositePlatform() { if (defaultTargetPlatform == TargetPlatform.iOS) { return TargetPlatform.android; } else { return TargetPlatform.iOS; } } debugDefaultTargetPlatformOverride = _getOppositePlatform(); // This rebuilds the application. This should obviously never be // done in a real app but it's done here since this app // unrealistically toggles the current platform for demonstration // purposes. WidgetsBinding.instance.reassembleApplication(); } // =========================================================================== // Non-shared code below because: // - Android and iOS have different scaffolds // - There are differenc items in the app bar / nav bar // - Android has a hamburger drawer, iOS has bottom tabs // - The iOS nav bar is scrollable, Android is not // - Pull-to-refresh works differently, and Android has a button to trigger it too // // And these are all design time choices that doesn't have a single 'right' // answer. // =========================================================================== Widget _buildAndroid(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(SongsTab.title), actions: [ IconButton( icon: Icon(Icons.refresh), onPressed: () async => await _androidRefreshKey.currentState.show(), ), IconButton( icon: Icon(Icons.shuffle), onPressed: _togglePlatform, ), ], ), drawer: widget.androidDrawer, body: RefreshIndicator( key: _androidRefreshKey, onRefresh: _refreshData, child: ListView.builder( padding: EdgeInsets.symmetric(vertical: 12), itemBuilder: _listBuilder, ), ), ); } Widget _buildIos(BuildContext context) { return CustomScrollView( slivers: [ CupertinoSliverNavigationBar( trailing: CupertinoButton( padding: EdgeInsets.zero, // TODO(redbrogdon): Change this back to shuffle once the new icon // makes it into Flutter's stable channel. child: Icon(CupertinoIcons.shuffle_thick), onPressed: _togglePlatform, ), ), CupertinoSliverRefreshControl( onRefresh: _refreshData, ), SliverSafeArea( top: false, sliver: SliverPadding( padding: EdgeInsets.symmetric(vertical: 12), sliver: SliverList( delegate: SliverChildBuilderDelegate(_listBuilder), ), ), ), ], ); } @override Widget build(context) { return PlatformWidget( androidBuilder: _buildAndroid, iosBuilder: _buildIos, ); } }