import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart' hide NestedScrollView; import 'package:provider/provider.dart'; import 'package:tsacdop/class/download_state.dart'; import 'package:tsacdop/home/playlist.dart'; import 'package:tuple/tuple.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/episodebrief.dart'; import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; import 'package:tsacdop/util/episodegrid.dart'; import 'package:tsacdop/util/mypopupmenu.dart'; import 'package:tsacdop/home/appbar/importompl.dart'; import 'package:tsacdop/home/audioplayer.dart'; import 'home_groups.dart'; import 'download_list.dart'; class Home extends StatefulWidget { @override _HomeState createState() => _HomeState(); } class _HomeState extends State with SingleTickerProviderStateMixin { TabController _controller; Decoration _getIndicator(BuildContext context) { return UnderlineTabIndicator( borderSide: BorderSide(color: Theme.of(context).accentColor, width: 3), insets: EdgeInsets.only( left: 10.0, right: 10.0, top: 10.0, )); } @override void initState() { super.initState(); _controller = TabController(length: 3, vsync: this); } @override void dispose() { _controller.dispose(); super.dispose(); } double top = 0; @override Widget build(BuildContext context) { double width = MediaQuery.of(context).size.width; double height = (width - 20) / 3 + 140; return SafeArea( child: Stack( children: [ Column( children: [ Import(), Expanded( child: NestedScrollView( innerScrollPositionKeyBuilder: () { return Key('tab' + _controller.index.toString()); }, pinnedHeaderSliverHeightBuilder: () => 50, headerSliverBuilder: (BuildContext context, bool innerBoxScrolled) { return [ SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return SizedBox( height: height, width: width, child: ScrollPodcasts(), ); }, childCount: 1, ), ), SliverPersistentHeader( delegate: _SliverAppBarDelegate( TabBar( indicator: _getIndicator(context), isScrollable: true, indicatorSize: TabBarIndicatorSize.tab, controller: _controller, tabs: [ Tab( child: Text('Recent Update'), ), Tab( child: Text('Favorite'), ), Tab( child: Text('Download'), ) ], ), ), pinned: true, ), ]; }, body: TabBarView( controller: _controller, children: [ NestedScrollViewInnerScrollPositionKeyWidget( Key('tab0'), _RecentUpdate()), NestedScrollViewInnerScrollPositionKeyWidget( Key('tab1'), _MyFavorite()), NestedScrollViewInnerScrollPositionKeyWidget( Key('tab2'), _MyDownload()), ], ), ), ), Selector( selector: (_, audio) => audio.playerRunning, builder: (_, data, __) { return Padding( padding: EdgeInsets.only(bottom: data ? 60.0 : 0), ); }), ], ), Container(child: PlayerWidget()), ], ), ); } } class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { _SliverAppBarDelegate(this._tabBar); final TabBar _tabBar; @override double get minExtent => _tabBar.preferredSize.height + 2; @override double get maxExtent => _tabBar.preferredSize.height + 2; @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return Container( color: Theme.of(context).scaffoldBackgroundColor, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ _tabBar, Spacer(), PlaylistButton(), ], ), Container(height: 2, color: Theme.of(context).primaryColorDark), ], ), ); } @override bool shouldRebuild(_SliverAppBarDelegate oldDelegate) { return true; } } class PlaylistButton extends StatefulWidget { PlaylistButton({Key key}) : super(key: key); @override PlaylistButtonState createState() => PlaylistButtonState(); } class PlaylistButtonState extends State { bool _loadPlay; static String _stringForSeconds(int seconds) { if (seconds == null) return null; return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}'; } _getPlaylist() async { await Provider.of(context, listen: false) .loadPlaylist(); setState(() { _loadPlay = true; }); } @override void initState() { super.initState(); _loadPlay = false; _getPlaylist(); } @override Widget build(BuildContext context) { var audio = Provider.of(context, listen: false); return MyPopupMenuButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10))), elevation: 1, icon: Icon(Icons.playlist_play), tooltip: "Menu", itemBuilder: (context) => [ MyPopupMenuItem( height: 50, value: 1, child: Container( decoration: BoxDecoration( // color: Theme.of(context).accentColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)), ), child: Selector>( selector: (_, audio) => Tuple3(audio.playerRunning, audio.queue, audio.lastPositin), builder: (_, data, __) => !_loadPlay ? Container( height: 8.0, ) : data.item1 || data.item2.playlist.length == 0 ? Container( height: 8.0, ) : InkWell( borderRadius: BorderRadius.only( topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)), onTap: () { audio.playlistLoad(); Navigator.pop(context); }, child: Column( children: [ Padding( padding: EdgeInsets.symmetric(vertical: 5), ), Stack( alignment: Alignment.center, children: [ CircleAvatar( radius: 20, backgroundImage: FileImage(File( "${data.item2.playlist.first.imagePath}")), ), Container( height: 40.0, width: 40.0, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.black12), child: Icon( Icons.play_arrow, color: Colors.white, ), ), ], ), Padding( padding: EdgeInsets.symmetric(vertical: 2), ), Container( height: 70, width: 140, child: Column( children: [ Text( _stringForSeconds(data.item3 ~/ 1000), // style: // TextStyle(color: Colors.white) ), Text( data.item2.playlist.first.title, maxLines: 2, textAlign: TextAlign.center, overflow: TextOverflow.fade, // style: TextStyle(color: Colors.white), ), ], ), ), Divider( height: 2, ), ], ), ), ), ), ), PopupMenuItem( value: 0, child: Container( padding: EdgeInsets.only(left: 10), child: Row( children: [ Icon(Icons.playlist_play), Padding( padding: EdgeInsets.symmetric(horizontal: 5.0), ), Text('Playlist'), ], ), ), ), ], onSelected: (value) { if (value == 0) { Navigator.push( context, MaterialPageRoute(builder: (context) => PlaylistPage())); } else if (value == 1) {} }, ); } } class _RecentUpdate extends StatefulWidget { @override _RecentUpdateState createState() => _RecentUpdateState(); } class _RecentUpdateState extends State<_RecentUpdate> with AutomaticKeepAliveClientMixin { Future> _getRssItem(int top) async { var dbHelper = DBHelper(); List episodes = await dbHelper.getRecentRssItem(top); return episodes; } _loadMoreEpisode() async { if (mounted) setState(() => _loadMore = true); await Future.delayed(Duration(seconds: 3)); if (mounted) setState(() { _top = _top + 33; _loadMore = false; }); } int _top = 99; bool _loadMore; @override void initState() { super.initState(); _loadMore = false; } @override Widget build(BuildContext context) { super.build(context); return FutureBuilder>( future: _getRssItem(_top), builder: (context, snapshot) { if (snapshot.hasError) print(snapshot.error); return (snapshot.hasData) ? NotificationListener( onNotification: (ScrollNotification scrollInfo) { if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && snapshot.data.length == _top) _loadMoreEpisode(); return true; }, child: CustomScrollView( key: PageStorageKey('update'), physics: const AlwaysScrollableScrollPhysics(), slivers: [ EpisodeGrid( episodes: snapshot.data, ), SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return _loadMore ? Container( height: 2, child: LinearProgressIndicator()) : Center(); }, childCount: 1, ), ), ]), ) : Center(child: CircularProgressIndicator()); }, ); } @override bool get wantKeepAlive => true; } class _MyFavorite extends StatefulWidget { @override _MyFavoriteState createState() => _MyFavoriteState(); } class _MyFavoriteState extends State<_MyFavorite> with AutomaticKeepAliveClientMixin { Future> _getLikedRssItem(_top) async { var dbHelper = DBHelper(); List episodes = await dbHelper.getLikedRssItem(_top); return episodes; } _loadMoreEpisode() async { if (mounted) setState(() => _loadMore = true); await Future.delayed(Duration(seconds: 3)); if (mounted) setState(() { _top = _top + 33; _loadMore = false; }); } int _top = 99; bool _loadMore; @override void initState() { super.initState(); _loadMore = false; } @override Widget build(BuildContext context) { super.build(context); return FutureBuilder>( future: _getLikedRssItem(_top), builder: (context, snapshot) { if (snapshot.hasError) print(snapshot.error); return (snapshot.hasData) ? NotificationListener( onNotification: (ScrollNotification scrollInfo) { if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && snapshot.data.length == _top) _loadMoreEpisode(); return true; }, child: CustomScrollView( key: PageStorageKey('favorite'), slivers: [ EpisodeGrid( episodes: snapshot.data, ), SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return _loadMore ? Container( height: 2, child: LinearProgressIndicator()) : Center(); }, childCount: 1, ), ), ], ), ) : Center(child: CircularProgressIndicator()); }, ); } @override bool get wantKeepAlive => true; } class _MyDownload extends StatefulWidget { @override _MyDownloadState createState() => _MyDownloadState(); } class _MyDownloadState extends State<_MyDownload> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); return CustomScrollView( key: PageStorageKey('downloas_list'), slivers: [ DownloadList(), SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Container( alignment: Alignment.centerLeft, padding: EdgeInsets.all(15.0), child: Text('Downloaded'), ); }, childCount: 1, ), ), Consumer( builder: (_, downloader, __) { var episodes = downloader.episodeTasks .where((task) => task.status.value == 3) .toList() .map((e) => e.episode) .toList() .reversed .toList(); return EpisodeGrid( episodes: episodes, ); }, ), ], ); } @override bool get wantKeepAlive => true; }