mirror of
https://github.com/stonega/tsacdop
synced 2025-02-03 08:57:33 +01:00
Change player widget UI
Change homepage to nested scroll
This commit is contained in:
parent
be5de73ddc
commit
97ec6a59e4
@ -9,7 +9,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: flutter upgrade
|
- run: cd /home/cirrus/sdks/flutter && git remote add origin https://github.com/flutter/flutter
|
||||||
|
- run: flutter upgrade && cd /home/cirrus/project/tsacdop
|
||||||
- run:
|
- run:
|
||||||
name: Run Flutter doctor
|
name: Run Flutter doctor
|
||||||
command: flutter doctor
|
command: flutter doctor
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-10/"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||||
<classpathentry kind="output" path="bin/default"/>
|
<classpathentry kind="output" path="bin/default"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
@ -155,11 +155,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// set setStopOnComplete(bool boo) {
|
set autoPlaySwitch(bool boo) {
|
||||||
// _stopOnComplete = boo;
|
|
||||||
//}
|
|
||||||
|
|
||||||
set autoPlaySwitch(bool boo) {
|
|
||||||
_autoPlay = boo;
|
_autoPlay = boo;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -168,6 +164,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
void addListener(VoidCallback listener) async {
|
void addListener(VoidCallback listener) async {
|
||||||
super.addListener(listener);
|
super.addListener(listener);
|
||||||
await AudioService.connect();
|
await AudioService.connect();
|
||||||
|
if(await AudioService.running){
|
||||||
|
AudioService.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPlaylist() async {
|
loadPlaylist() async {
|
||||||
@ -515,7 +514,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||||||
_skipState = BasicPlaybackState.skippingToNext;
|
_skipState = BasicPlaybackState.skippingToNext;
|
||||||
await _audioPlayer.setUrl(mediaItem.id);
|
await _audioPlayer.setUrl(mediaItem.id);
|
||||||
print(mediaItem.id);
|
print(mediaItem.id);
|
||||||
Duration duration = await _audioPlayer.durationFuture;
|
Duration duration = await _audioPlayer.durationFuture ?? 0;
|
||||||
AudioServiceBackground.setMediaItem(
|
AudioServiceBackground.setMediaItem(
|
||||||
mediaItem.copyWith(duration: duration.inMilliseconds));
|
mediaItem.copyWith(duration: duration.inMilliseconds));
|
||||||
_skipState = null;
|
_skipState = null;
|
||||||
|
@ -12,6 +12,7 @@ class PodcastLocal {
|
|||||||
|
|
||||||
final String description;
|
final String description;
|
||||||
final int upateCount;
|
final int upateCount;
|
||||||
|
final int episodeCount;
|
||||||
PodcastLocal(
|
PodcastLocal(
|
||||||
this.title,
|
this.title,
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
@ -25,6 +26,7 @@ class PodcastLocal {
|
|||||||
{
|
{
|
||||||
this.description ='',
|
this.description ='',
|
||||||
this.upateCount = 0,
|
this.upateCount = 0,
|
||||||
|
this.episodeCount = 0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -197,13 +197,13 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
padding: EdgeInsets.only(top: 5.0),
|
padding: EdgeInsets.only(top: 5.0),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
//physics: AlwaysScrollableScrollPhysics(),
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
child: _loaddes
|
child: _loaddes
|
||||||
? (_description.contains('<'))
|
? (_description.contains('<'))
|
||||||
? Html(
|
? Html(
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.symmetric(horizontal: 20.0),
|
EdgeInsets.only(left: 20.0, right: 20, bottom: 10),
|
||||||
defaultTextStyle: TextStyle(height: 1.8),
|
defaultTextStyle: TextStyle(height: 1.8),
|
||||||
data: _description,
|
data: _description,
|
||||||
linkStyle: TextStyle(
|
linkStyle: TextStyle(
|
||||||
@ -217,7 +217,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
)
|
)
|
||||||
: Container(
|
: Container(
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.symmetric(horizontal: 20.0),
|
EdgeInsets.only(left: 20.0, right: 20.0, bottom: 10.0),
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: SelectableLinkify(
|
child: SelectableLinkify(
|
||||||
onOpen: (link) {
|
onOpen: (link) {
|
||||||
|
@ -72,7 +72,7 @@ class AboutApp extends StatelessWidget {
|
|||||||
image: AssetImage('assets/logo.png'),
|
image: AssetImage('assets/logo.png'),
|
||||||
height: 80,
|
height: 80,
|
||||||
),
|
),
|
||||||
Text('Version: 0.1.4'),
|
Text('Version: 0.1.5'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -18,10 +18,10 @@ import 'package:tsacdop/class/podcast_group.dart';
|
|||||||
import 'package:tsacdop/class/searchpodcast.dart';
|
import 'package:tsacdop/class/searchpodcast.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
import 'package:tsacdop/home/home.dart';
|
|
||||||
import 'package:tsacdop/home/appbar/popupmenu.dart';
|
import 'package:tsacdop/home/appbar/popupmenu.dart';
|
||||||
import 'package:tsacdop/webfeed/webfeed.dart';
|
import 'package:tsacdop/webfeed/webfeed.dart';
|
||||||
import 'package:tsacdop/.env.dart';
|
import 'package:tsacdop/.env.dart';
|
||||||
|
import '../nested_home.dart';
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -5,9 +5,9 @@ import 'dart:math' as math;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:marquee/marquee.dart';
|
import 'package:marquee/marquee.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
import 'package:flutter_neumorphic/flutter_neumorphic.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/class/audiostate.dart';
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
@ -17,7 +17,6 @@ import 'package:tsacdop/util/pageroute.dart';
|
|||||||
import 'package:tsacdop/util/colorize.dart';
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
import 'package:tsacdop/util/day_night_switch.dart';
|
import 'package:tsacdop/util/day_night_switch.dart';
|
||||||
|
|
||||||
//Custom slider
|
|
||||||
class MyRectangularTrackShape extends RectangularSliderTrackShape {
|
class MyRectangularTrackShape extends RectangularSliderTrackShape {
|
||||||
Rect getPreferredRect({
|
Rect getPreferredRect({
|
||||||
@required RenderBox parentBox,
|
@required RenderBox parentBox,
|
||||||
@ -83,14 +82,28 @@ class MyRoundSliderThumpShape extends SliderComponentShape {
|
|||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
..strokeWidth = 2,
|
..strokeWidth = 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Path _path = Path();
|
||||||
|
|
||||||
|
// _path.moveTo(center.dx - 12, center.dy + 10);
|
||||||
|
// _path.lineTo(center.dx - 12, center.dy - 12);
|
||||||
|
// _path.lineTo(center.dx -12, center.dy - 12);
|
||||||
|
// canvas.drawShadow(_path, Colors.black, 4, false);
|
||||||
|
|
||||||
|
// Path _pathLight = Path();
|
||||||
|
// _pathLight.moveTo(center.dx + 12, center.dy - 12);
|
||||||
|
// _pathLight.lineTo(center.dx + 12, center.dy + 10);
|
||||||
|
//// _pathLight.lineTo(center.dx - 12, center.dy + 10);
|
||||||
|
// canvas.drawShadow(_pathLight, Colors.black, 4, true);
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Rect.fromLTRB(
|
Rect.fromLTRB(
|
||||||
center.dx - 10, center.dy + 10, center.dx + 10, center.dy - 10),
|
center.dx - 10, center.dy + 10, center.dx + 10, center.dy - 10),
|
||||||
Paint()
|
Paint()
|
||||||
..color = colorTween.evaluate(enableAnimation)
|
..color = Colors.white
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
..strokeWidth = 10,
|
..strokeWidth = 10,
|
||||||
);
|
);
|
||||||
|
|
||||||
canvas.drawLine(
|
canvas.drawLine(
|
||||||
Offset(center.dx - 5, center.dy - 2),
|
Offset(center.dx - 5, center.dy - 2),
|
||||||
Offset(center.dx + 5, center.dy + 2),
|
Offset(center.dx + 5, center.dy + 2),
|
||||||
@ -113,6 +126,22 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<BoxShadow> _customShadow = [
|
||||||
|
BoxShadow(blurRadius: 26, offset: Offset(-6, -6), color: Colors.white),
|
||||||
|
BoxShadow(
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: Offset(2, 2),
|
||||||
|
color: Colors.grey[600].withOpacity(0.4))
|
||||||
|
];
|
||||||
|
|
||||||
|
List<BoxShadow> _customShadowNight = [
|
||||||
|
BoxShadow(
|
||||||
|
blurRadius: 6,
|
||||||
|
offset: Offset(-1, -1),
|
||||||
|
color: Colors.grey[100].withOpacity(0.3)),
|
||||||
|
BoxShadow(blurRadius: 8, offset: Offset(2, 2), color: Colors.black)
|
||||||
|
];
|
||||||
|
|
||||||
List minsToSelect = [1, 5, 10, 15, 20, 25, 30, 45, 60, 70, 80, 90, 99];
|
List minsToSelect = [1, 5, 10, 15, 20, 25, 30, 45, 60, 70, 80, 90, 99];
|
||||||
int _minSelected;
|
int _minSelected;
|
||||||
|
|
||||||
@ -137,7 +166,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
height: 300,
|
height: 300,
|
||||||
color: data.item3 > 0
|
color: data.item3 > 0
|
||||||
? Colors.black.withOpacity(data.item3)
|
? Colors.black.withOpacity(data.item3)
|
||||||
: Theme.of(context).scaffoldBackgroundColor,
|
: Theme.of(context).primaryColor,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Column(
|
Column(
|
||||||
@ -149,54 +178,60 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
),
|
),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
padding: EdgeInsets.symmetric(vertical: 20),
|
||||||
children: minsToSelect
|
child: Row(
|
||||||
.map((e) => InkWell(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
onTap: () => setState(() => _minSelected = e),
|
children: minsToSelect
|
||||||
child: Stack(
|
.map((e) => InkWell(
|
||||||
alignment: Alignment.center,
|
onTap: () => setState(() => _minSelected = e),
|
||||||
children: <Widget>[
|
child: Stack(
|
||||||
AnimatedContainer(
|
alignment: Alignment.center,
|
||||||
duration: Duration(milliseconds: 300),
|
children: <Widget>[
|
||||||
curve: Curves.elasticOut,
|
Container(
|
||||||
margin: EdgeInsets.symmetric(
|
margin: EdgeInsets.symmetric(
|
||||||
horizontal: 10.0),
|
horizontal: 10.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
boxShadow: [
|
boxShadow: !(e == _minSelected ||
|
||||||
BoxShadow(
|
data.item3 > 0)
|
||||||
color: Colors.grey[800],
|
? (Theme.of(context).brightness ==
|
||||||
blurRadius: 0,
|
Brightness.dark)
|
||||||
offset: Offset(0, 0)),
|
? _customShadowNight
|
||||||
],
|
: _customShadow
|
||||||
color: (e == _minSelected)
|
: [
|
||||||
? Theme.of(context).accentColor
|
BoxShadow(
|
||||||
: Theme.of(context).primaryColorDark,
|
color: Colors.black,
|
||||||
shape: BoxShape.circle,
|
blurRadius: 0,
|
||||||
|
offset: Offset(0, 0)),
|
||||||
|
],
|
||||||
|
color: (e == _minSelected)
|
||||||
|
? Theme.of(context).accentColor
|
||||||
|
: Theme.of(context).primaryColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 30,
|
||||||
|
width: 30,
|
||||||
|
child: Text(e.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: (e == _minSelected)
|
||||||
|
? Colors.white
|
||||||
|
: null)),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
Container(
|
||||||
height: (e == _minSelected) ? 40 : 30,
|
height: 30 * data.item3,
|
||||||
width: (e == _minSelected) ? 40 : 30,
|
width: 30 * data.item3,
|
||||||
child: Text(e.toString(),
|
decoration: BoxDecoration(
|
||||||
style: TextStyle(
|
color: Colors.black
|
||||||
fontWeight: FontWeight.bold,
|
.withOpacity(data.item3),
|
||||||
color: Colors.white)),
|
shape: BoxShape.circle),
|
||||||
),
|
),
|
||||||
Container(
|
],
|
||||||
height: (e == _minSelected)
|
),
|
||||||
? 40 * data.item3
|
))
|
||||||
: 30 * data.item3,
|
.toList(),
|
||||||
width: (e == _minSelected)
|
),
|
||||||
? 40 * data.item3
|
|
||||||
: 30 * data.item3,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.black,
|
|
||||||
shape: BoxShape.circle),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
@ -209,7 +244,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
value: data.item1,
|
value: data.item1,
|
||||||
sunColor: Colors.yellow[700],
|
sunColor: Colors.yellow[700],
|
||||||
moonColor: Colors.grey[600],
|
moonColor: Colors.grey[600],
|
||||||
dayColor: Colors.grey[300],
|
dayColor: Theme.of(context).primaryColorDark,
|
||||||
nightColor: Colors.black,
|
nightColor: Colors.black,
|
||||||
onDrag: (value) => audio.setSwitchValue = value,
|
onDrag: (value) => audio.setSwitchValue = value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -231,15 +266,13 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
height: 25,
|
height: 25,
|
||||||
width: 100,
|
width: 100,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(),
|
||||||
// color: Theme.of(context).accentColor,
|
|
||||||
),
|
|
||||||
child: Text(_stringForSeconds(data.item2.toDouble()),
|
child: Text(_stringForSeconds(data.item2.toDouble()),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Colors.white,
|
color:
|
||||||
)),
|
(data.item3 > 0) ? Colors.white : null)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -247,7 +280,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 60 + 20 * data.item3,
|
bottom: 50 + 20 * data.item3,
|
||||||
left: MediaQuery.of(context).size.width / 2 - 100,
|
left: MediaQuery.of(context).size.width / 2 - 100,
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -302,7 +335,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
padding: EdgeInsets.only(top: 10, left: 10, right: 10),
|
padding: EdgeInsets.only(top: 10, left: 10, right: 10),
|
||||||
child: SliderTheme(
|
child: SliderTheme(
|
||||||
data: SliderTheme.of(context).copyWith(
|
data: SliderTheme.of(context).copyWith(
|
||||||
activeTrackColor: Colors.grey[400],
|
activeTrackColor:
|
||||||
|
Theme.of(context).brightness == Brightness.dark
|
||||||
|
? Colors.black38
|
||||||
|
: Colors.grey[400],
|
||||||
inactiveTrackColor:
|
inactiveTrackColor:
|
||||||
Theme.of(context).primaryColorDark,
|
Theme.of(context).primaryColorDark,
|
||||||
trackHeight: 20.0,
|
trackHeight: 20.0,
|
||||||
@ -315,7 +351,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
overlayColor:
|
overlayColor:
|
||||||
Theme.of(context).accentColor.withAlpha(32),
|
Theme.of(context).accentColor.withAlpha(32),
|
||||||
overlayShape:
|
overlayShape:
|
||||||
RoundSliderOverlayShape(overlayRadius: 14.0),
|
RoundSliderOverlayShape(overlayRadius: 4.0),
|
||||||
),
|
),
|
||||||
child: Slider(
|
child: Slider(
|
||||||
value: data.seekSliderValue,
|
value: data.seekSliderValue,
|
||||||
@ -387,32 +423,68 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
iconSize: 32.0,
|
iconSize: 32.0,
|
||||||
icon: Icon(Icons.replay_10),
|
icon: Icon(Icons.replay_10),
|
||||||
color: Colors.grey[500]),
|
color: Colors.grey[500]),
|
||||||
backplay == BasicPlaybackState.playing
|
Container(
|
||||||
? IconButton(
|
margin: EdgeInsets.symmetric(horizontal: 30),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
height: 60,
|
||||||
onPressed:
|
width: 60,
|
||||||
backplay == BasicPlaybackState.playing
|
decoration: BoxDecoration(
|
||||||
? () {
|
color: Theme.of(context).primaryColor,
|
||||||
audio.pauseAduio();
|
shape: BoxShape.circle,
|
||||||
}
|
border: Border.all(
|
||||||
: null,
|
color: Theme.of(context).brightness ==
|
||||||
iconSize: 60.0,
|
Brightness.dark
|
||||||
icon: Icon(
|
? Colors.black12
|
||||||
LineIcons.pause_circle,
|
: Colors.white10,
|
||||||
size: 40,
|
width: 1),
|
||||||
|
boxShadow: Theme.of(context).brightness ==
|
||||||
|
Brightness.dark
|
||||||
|
? _customShadowNight
|
||||||
|
: _customShadow),
|
||||||
|
child: backplay == BasicPlaybackState.playing
|
||||||
|
? Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(30)),
|
||||||
|
onTap:
|
||||||
|
backplay == BasicPlaybackState.playing
|
||||||
|
? () {
|
||||||
|
audio.pauseAduio();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
child: Icon(
|
||||||
|
Icons.pause,
|
||||||
|
size: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(30)),
|
||||||
|
onTap:
|
||||||
|
backplay == BasicPlaybackState.playing
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
audio.resumeAudio();
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
child: Icon(
|
||||||
|
Icons.play_arrow,
|
||||||
|
size: 40,
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
color: Colors.grey[500])
|
),
|
||||||
: IconButton(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
|
||||||
onPressed:
|
|
||||||
backplay == BasicPlaybackState.playing
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
audio.resumeAudio();
|
|
||||||
},
|
|
||||||
iconSize: 60.0,
|
|
||||||
icon: Icon(LineIcons.play_circle, size: 40),
|
|
||||||
color: Colors.grey[500]),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||||
onPressed: backplay == BasicPlaybackState.playing
|
onPressed: backplay == BasicPlaybackState.playing
|
||||||
@ -497,6 +569,11 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
FileImage(File("${data.item1.imagePath}")),
|
FileImage(File("${data.item1.imagePath}")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||||
|
width: 200,
|
||||||
|
child: Text(data.item1.feedTitle, maxLines: 1, overflow: TextOverflow.fade,),
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Navigator.push(
|
onPressed: () => Navigator.push(
|
||||||
@ -530,105 +607,127 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
//padding: EdgeInsets.only(bottom: 10.0),
|
//padding: EdgeInsets.only(bottom: 10.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
// borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
// borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Selector<AudioPlayerNotifier, List<EpisodeBrief>>(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
selector: (_, audio) => audio.queue.playlist,
|
||||||
mainAxisSize: MainAxisSize.min,
|
builder: (_, playlist, __) {
|
||||||
children: <Widget>[
|
return ListView.builder(
|
||||||
SingleChildScrollView(
|
shrinkWrap: true,
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
child: Container(
|
itemCount: playlist.length,
|
||||||
height: 50,
|
itemBuilder: (BuildContext context, int index) {
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
print(playlist.length);
|
||||||
alignment: Alignment.center,
|
if (index == 0) {
|
||||||
child: Row(
|
return Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
height: 60,
|
||||||
mainAxisSize: MainAxisSize.min,
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
children: <Widget>[
|
alignment: Alignment.center,
|
||||||
Text(
|
child: Row(
|
||||||
'NEXT TO PLAY',
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
style: TextStyle(
|
mainAxisSize: MainAxisSize.min,
|
||||||
color: Theme.of(context).accentColor,
|
children: <Widget>[
|
||||||
fontWeight: FontWeight.bold),
|
Text(
|
||||||
|
'Playlist',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 40,
|
||||||
|
width: 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(20)),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).brightness ==
|
||||||
|
Brightness.dark
|
||||||
|
? Colors.black12
|
||||||
|
: Colors.white10,
|
||||||
|
width: 1),
|
||||||
|
boxShadow: Theme.of(context).brightness ==
|
||||||
|
Brightness.dark
|
||||||
|
? _customShadowNight
|
||||||
|
: _customShadow),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(20)),
|
||||||
|
onTap: () => audio.playNext(),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 40,
|
||||||
|
width: 80,
|
||||||
|
child: Icon(
|
||||||
|
Icons.skip_next,
|
||||||
|
size: 30,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Spacer(),
|
);
|
||||||
],
|
} else {
|
||||||
),
|
return Column(
|
||||||
),
|
children: <Widget>[
|
||||||
),
|
Material(
|
||||||
Expanded(
|
color: Colors.transparent,
|
||||||
child: Selector<AudioPlayerNotifier, List<EpisodeBrief>>(
|
child: InkWell(
|
||||||
selector: (_, audio) => audio.queue.playlist,
|
onTap: () {
|
||||||
builder: (_, playlist, __) {
|
audio.episodeLoad(playlist[index]);
|
||||||
return ListView.builder(
|
},
|
||||||
shrinkWrap: true,
|
child: Container(
|
||||||
scrollDirection: Axis.vertical,
|
height: 60,
|
||||||
itemCount: playlist.length,
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
alignment: Alignment.centerLeft,
|
||||||
print(playlist.length);
|
child: Row(
|
||||||
return index == 0
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
? Center()
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
: Column(
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Material(
|
Container(
|
||||||
color: Colors.transparent,
|
padding: EdgeInsets.all(10.0),
|
||||||
child: InkWell(
|
child: ClipRRect(
|
||||||
onTap: () {
|
borderRadius:
|
||||||
audio.episodeLoad(playlist[index]);
|
BorderRadius.all(Radius.circular(15.0)),
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 60,
|
height: 30.0,
|
||||||
padding:
|
width: 30.0,
|
||||||
EdgeInsets.symmetric(horizontal: 10),
|
child: Image.file(File(
|
||||||
alignment: Alignment.centerLeft,
|
"${playlist[index].imagePath}"))),
|
||||||
// decoration: BoxDecoration(
|
),
|
||||||
// color: Theme.of(context)
|
),
|
||||||
// .scaffoldBackgroundColor,
|
Expanded(
|
||||||
// ),
|
child: Container(
|
||||||
child: Row(
|
alignment: Alignment.centerLeft,
|
||||||
mainAxisAlignment:
|
child: Text(
|
||||||
MainAxisAlignment.center,
|
playlist[index].title,
|
||||||
crossAxisAlignment:
|
maxLines: 1,
|
||||||
CrossAxisAlignment.center,
|
overflow: TextOverflow.ellipsis,
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(15.0)),
|
|
||||||
child: Container(
|
|
||||||
height: 30.0,
|
|
||||||
width: 30.0,
|
|
||||||
child: Image.file(File(
|
|
||||||
"${playlist[index].imagePath}"))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
playlist[index].title,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Divider(height: 2),
|
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
),
|
},
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'hometab.dart';
|
|
||||||
import 'package:tsacdop/home/appbar/importompl.dart';
|
|
||||||
import 'package:tsacdop/home/audioplayer.dart';
|
|
||||||
import 'home_groups.dart';
|
|
||||||
|
|
||||||
class Home extends StatelessWidget {
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Stack(children: <Widget>[
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Import(),
|
|
||||||
Container(child: ScrollPodcasts()),
|
|
||||||
Expanded(
|
|
||||||
child: MainTab(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(child: PlayerWidget()),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -43,100 +43,105 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
height: (_width - 20) / 3 + 140,
|
height: (_width - 20) / 3 + 140,
|
||||||
)
|
)
|
||||||
: groups[_groupIndex].podcastList.length == 0
|
: groups[_groupIndex].podcastList.length == 0
|
||||||
? Column(
|
? Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
height: (_width - 20) / 3 + 140,
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: <Widget>[
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
GestureDetector(
|
mainAxisSize: MainAxisSize.min,
|
||||||
onVerticalDragEnd: (event) {
|
children: <Widget>[
|
||||||
if (event.primaryVelocity > 200) {
|
GestureDetector(
|
||||||
if (groups.length == 1) {
|
onVerticalDragEnd: (event) {
|
||||||
Fluttertoast.showToast(
|
if (event.primaryVelocity > 200) {
|
||||||
msg: 'Add some groups',
|
if (groups.length == 1) {
|
||||||
gravity: ToastGravity.BOTTOM,
|
Fluttertoast.showToast(
|
||||||
);
|
msg: 'Add some groups',
|
||||||
} else {
|
gravity: ToastGravity.BOTTOM,
|
||||||
if (mounted)
|
);
|
||||||
|
} else {
|
||||||
|
if (mounted)
|
||||||
|
setState(() {
|
||||||
|
(_groupIndex != 0)
|
||||||
|
? _groupIndex--
|
||||||
|
: _groupIndex = groups.length - 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (event.primaryVelocity < -200) {
|
||||||
|
if (groups.length == 1) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Add some groups',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
(_groupIndex != 0)
|
(_groupIndex < groups.length - 1)
|
||||||
? _groupIndex--
|
? _groupIndex++
|
||||||
: _groupIndex = groups.length - 1;
|
: _groupIndex = 0;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (event.primaryVelocity < -200) {
|
},
|
||||||
if (groups.length == 1) {
|
child: Column(
|
||||||
Fluttertoast.showToast(
|
children: <Widget>[
|
||||||
msg: 'Add some groups',
|
Container(
|
||||||
gravity: ToastGravity.BOTTOM,
|
child: Row(
|
||||||
);
|
children: <Widget>[
|
||||||
} else {
|
Container(
|
||||||
setState(() {
|
padding: EdgeInsets.symmetric(
|
||||||
(_groupIndex < groups.length - 1)
|
horizontal: 15.0),
|
||||||
? _groupIndex++
|
child: Text(
|
||||||
: _groupIndex = 0;
|
groups[_groupIndex].name,
|
||||||
});
|
style: Theme.of(context)
|
||||||
}
|
.textTheme
|
||||||
}
|
.bodyText1
|
||||||
},
|
.copyWith(
|
||||||
child: Column(
|
color: Theme.of(context)
|
||||||
children: <Widget>[
|
.accentColor),
|
||||||
Container(
|
)),
|
||||||
child: Row(
|
Spacer(),
|
||||||
children: <Widget>[
|
Container(
|
||||||
Container(
|
height: 30,
|
||||||
padding: EdgeInsets.symmetric(
|
padding:
|
||||||
horizontal: 15.0),
|
EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: Text(
|
alignment: Alignment.bottomRight,
|
||||||
groups[_groupIndex].name,
|
child: InkWell(
|
||||||
style: Theme.of(context)
|
onTap: () {
|
||||||
.textTheme
|
Navigator.push(
|
||||||
.bodyText1
|
context,
|
||||||
.copyWith(
|
SlideLeftRoute(
|
||||||
color: Theme.of(context)
|
page: PodcastManage()),
|
||||||
.accentColor),
|
);
|
||||||
)),
|
},
|
||||||
Spacer(),
|
child: Container(
|
||||||
Container(
|
height: 30,
|
||||||
height: 30,
|
padding: EdgeInsets.all(5.0),
|
||||||
padding:
|
child: Text(
|
||||||
EdgeInsets.symmetric(horizontal: 15),
|
'See All',
|
||||||
alignment: Alignment.bottomRight,
|
style: Theme.of(context)
|
||||||
child: InkWell(
|
.textTheme
|
||||||
onTap: () {
|
.bodyText1
|
||||||
Navigator.push(
|
.copyWith(
|
||||||
context,
|
color: Theme.of(context)
|
||||||
SlideLeftRoute(page: PodcastManage()),
|
.accentColor),
|
||||||
);
|
)),
|
||||||
},
|
),
|
||||||
child: Container(
|
|
||||||
height: 30,
|
|
||||||
padding: EdgeInsets.all(5.0),
|
|
||||||
child: Text(
|
|
||||||
'See All',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyText1
|
|
||||||
.copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.accentColor),
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
Container(
|
||||||
Container(
|
height: 70,
|
||||||
height: 70,
|
color:
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
Container(
|
Container(
|
||||||
height: (_width - 20) / 3 + 40,
|
height: (_width - 20) / 3 + 40,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
margin: EdgeInsets.symmetric(horizontal: 15),
|
margin: EdgeInsets.symmetric(horizontal: 15),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: DefaultTabController(
|
: DefaultTabController(
|
||||||
length: groups[_groupIndex].podcastList.length,
|
length: groups[_groupIndex].podcastList.length,
|
||||||
@ -194,7 +199,7 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
)),
|
)),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Container(
|
Container(
|
||||||
height: 30,
|
height: 30.0,
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.symmetric(horizontal: 15),
|
EdgeInsets.symmetric(horizontal: 15),
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
@ -223,7 +228,6 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
// color: Colors.white10,
|
|
||||||
height: 70,
|
height: 70,
|
||||||
width: _width,
|
width: _width,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
@ -1,33 +1,31 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart' hide NestedScrollView;
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
import 'package:tsacdop/home/playlist.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:tsacdop/class/audiostate.dart';
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/home/playlist.dart';
|
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
import 'package:tsacdop/util/episodegrid.dart';
|
import 'package:tsacdop/util/episodegrid.dart';
|
||||||
import 'package:tsacdop/util/mypopupmenu.dart';
|
import 'package:tsacdop/util/mypopupmenu.dart';
|
||||||
|
|
||||||
class MainTab extends StatefulWidget {
|
import 'package:tsacdop/home/appbar/importompl.dart';
|
||||||
|
import 'package:tsacdop/home/audioplayer.dart';
|
||||||
|
import 'home_groups.dart';
|
||||||
|
|
||||||
|
class Home extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_MainTabState createState() => _MainTabState();
|
_HomeState createState() => _HomeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
|
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
TabController _controller;
|
TabController _controller;
|
||||||
bool _loadPlay;
|
Decoration _getIndicator(BuildContext context) {
|
||||||
static String _stringForSeconds(int seconds) {
|
|
||||||
if (seconds == null) return null;
|
|
||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
|
||||||
}
|
|
||||||
|
|
||||||
Decoration getIndicator(BuildContext context) {
|
|
||||||
return UnderlineTabIndicator(
|
return UnderlineTabIndicator(
|
||||||
borderSide: BorderSide(color: Theme.of(context).accentColor, width: 2),
|
borderSide: BorderSide(color: Theme.of(context).accentColor, width: 3),
|
||||||
insets: EdgeInsets.only(
|
insets: EdgeInsets.only(
|
||||||
left: 10.0,
|
left: 10.0,
|
||||||
right: 10.0,
|
right: 10.0,
|
||||||
@ -35,6 +33,147 @@ class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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: <Widget>[
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Import(),
|
||||||
|
Expanded(
|
||||||
|
child: NestedScrollView(
|
||||||
|
headerSliverBuilder:
|
||||||
|
(BuildContext context, bool innerBoxScrolled) {
|
||||||
|
return <Widget>[
|
||||||
|
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: <Widget>[
|
||||||
|
Tab(
|
||||||
|
child: Text('Recent Update'),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
child: Text('Favorite'),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
child: Text('Download'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pinned: true,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
body: TabBarView(
|
||||||
|
controller: _controller,
|
||||||
|
children: <Widget>[
|
||||||
|
_RecentUpdate(),
|
||||||
|
_MyFavorite(),
|
||||||
|
_MyDownload(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Selector<AudioPlayerNotifier, bool>(
|
||||||
|
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: <Widget>[
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
_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<PlaylistButton> {
|
||||||
|
bool _loadPlay;
|
||||||
|
static String _stringForSeconds(int seconds) {
|
||||||
|
if (seconds == null) return null;
|
||||||
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
_getPlaylist() async {
|
_getPlaylist() async {
|
||||||
await Provider.of<AudioPlayerNotifier>(context, listen: false)
|
await Provider.of<AudioPlayerNotifier>(context, listen: false)
|
||||||
.loadPlaylist();
|
.loadPlaylist();
|
||||||
@ -43,7 +182,15 @@ class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget playlist(BuildContext context) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadPlay = false;
|
||||||
|
_getPlaylist();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||||
return MyPopupMenuButton<int>(
|
return MyPopupMenuButton<int>(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@ -163,86 +310,14 @@ class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_controller = TabController(length: 3, vsync: this);
|
|
||||||
_loadPlay = false;
|
|
||||||
_getPlaylist();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
|
||||||
height: 50,
|
|
||||||
alignment: Alignment.bottomLeft,
|
|
||||||
child: TabBar(
|
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
|
||||||
isScrollable: true,
|
|
||||||
labelPadding: EdgeInsets.all(10.0),
|
|
||||||
controller: _controller,
|
|
||||||
indicator: getIndicator(context),
|
|
||||||
tabs: <Widget>[
|
|
||||||
Text(
|
|
||||||
'Recent Update',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Favorites',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Downloads',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
playlist(context),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(height: 2, color: Theme.of(context).primaryColorDark),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
child: TabBarView(
|
|
||||||
controller: _controller,
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
child: RecentUpdate()),
|
|
||||||
Container(
|
|
||||||
child: MyFavorite()),
|
|
||||||
Container(
|
|
||||||
child: MyDownload()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RecentUpdate extends StatefulWidget {
|
class _RecentUpdate extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_RecentUpdateState createState() => _RecentUpdateState();
|
_RecentUpdateState createState() => _RecentUpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RecentUpdateState extends State<RecentUpdate> {
|
class _RecentUpdateState extends State<_RecentUpdate> {
|
||||||
int _updateCount = 0;
|
int _updateCount = 0;
|
||||||
Future<List<EpisodeBrief>> _getRssItem(int top) async {
|
Future<List<EpisodeBrief>> _getRssItem(int top) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
@ -252,34 +327,24 @@ class _RecentUpdateState extends State<RecentUpdate> {
|
|||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollController _controller;
|
_loadMoreEpisode() async {
|
||||||
|
if (mounted) setState(() => _loadMore = true);
|
||||||
|
await Future.delayed(Duration(seconds: 3));
|
||||||
|
if (mounted)
|
||||||
|
setState(() {
|
||||||
|
_top = _top + 33;
|
||||||
|
_loadMore = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
int _top;
|
int _top;
|
||||||
bool _loadMore;
|
bool _loadMore;
|
||||||
_scrollListener() async {
|
|
||||||
if (_controller.offset == _controller.position.maxScrollExtent) {
|
|
||||||
if (mounted) setState(() => _loadMore = true);
|
|
||||||
await Future.delayed(Duration(seconds: 3));
|
|
||||||
if (mounted)
|
|
||||||
setState(() {
|
|
||||||
_top = _top + 33;
|
|
||||||
_loadMore = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadMore = false;
|
_loadMore = false;
|
||||||
_top = 33;
|
_top = 33;
|
||||||
_controller = ScrollController();
|
|
||||||
_controller.addListener(_scrollListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -289,14 +354,92 @@ class _RecentUpdateState extends State<RecentUpdate> {
|
|||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) print(snapshot.error);
|
if (snapshot.hasError) print(snapshot.error);
|
||||||
return (snapshot.hasData)
|
return (snapshot.hasData)
|
||||||
? CustomScrollView(
|
? NotificationListener<ScrollNotification>(
|
||||||
controller: _controller,
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
if (scrollInfo.metrics.pixels ==
|
||||||
primary: false,
|
scrollInfo.metrics.maxScrollExtent &&
|
||||||
slivers: <Widget>[
|
snapshot.data.length == _top) _loadMoreEpisode();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
child: CustomScrollView(
|
||||||
|
key: PageStorageKey<String>('update'),
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
slivers: <Widget>[
|
||||||
|
EpisodeGrid(
|
||||||
|
episodes: snapshot.data,
|
||||||
|
updateCount: _updateCount,
|
||||||
|
),
|
||||||
|
SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return _loadMore
|
||||||
|
? Container(
|
||||||
|
height: 2, child: LinearProgressIndicator())
|
||||||
|
: Center();
|
||||||
|
},
|
||||||
|
childCount: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
: Center(child: CircularProgressIndicator());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyFavorite extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_MyFavoriteState createState() => _MyFavoriteState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyFavoriteState extends State<_MyFavorite> {
|
||||||
|
Future<List<EpisodeBrief>> _getLikedRssItem(_top) async {
|
||||||
|
var dbHelper = DBHelper();
|
||||||
|
List<EpisodeBrief> 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;
|
||||||
|
bool _loadMore;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadMore = false;
|
||||||
|
_top = 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<List<EpisodeBrief>>(
|
||||||
|
future: _getLikedRssItem(_top),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasError) print(snapshot.error);
|
||||||
|
return (snapshot.hasData)
|
||||||
|
? NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
|
if (scrollInfo.metrics.pixels ==
|
||||||
|
scrollInfo.metrics.maxScrollExtent &&
|
||||||
|
snapshot.data.length == _top) _loadMoreEpisode();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
child: CustomScrollView(
|
||||||
|
key: PageStorageKey<String>('favorite'),
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
slivers: <Widget>[
|
||||||
EpisodeGrid(
|
EpisodeGrid(
|
||||||
podcast: snapshot.data,
|
episodes: snapshot.data,
|
||||||
updateCount: _updateCount,
|
|
||||||
),
|
),
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
@ -309,40 +452,8 @@ class _RecentUpdateState extends State<RecentUpdate> {
|
|||||||
childCount: 1,
|
childCount: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
: Center(child: CircularProgressIndicator());
|
),
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyFavorite extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
_MyFavoriteState createState() => _MyFavoriteState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyFavoriteState extends State<MyFavorite> {
|
|
||||||
Future<List<EpisodeBrief>> _getLikedRssItem() async {
|
|
||||||
var dbHelper = DBHelper();
|
|
||||||
List<EpisodeBrief> episodes = await dbHelper.getLikedRssItem();
|
|
||||||
return episodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FutureBuilder<List<EpisodeBrief>>(
|
|
||||||
future: _getLikedRssItem(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.hasError) print(snapshot.error);
|
|
||||||
return (snapshot.hasData)
|
|
||||||
? CustomScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
primary: false,
|
|
||||||
slivers: <Widget>[
|
|
||||||
EpisodeGrid(
|
|
||||||
podcast: snapshot.data,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
: Center(child: CircularProgressIndicator());
|
: Center(child: CircularProgressIndicator());
|
||||||
},
|
},
|
||||||
@ -350,12 +461,12 @@ class _MyFavoriteState extends State<MyFavorite> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyDownload extends StatefulWidget {
|
class _MyDownload extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_MyDownloadState createState() => _MyDownloadState();
|
_MyDownloadState createState() => _MyDownloadState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyDownloadState extends State<MyDownload> {
|
class _MyDownloadState extends State<_MyDownload> {
|
||||||
Future<List<EpisodeBrief>> _getDownloadedRssItem() async {
|
Future<List<EpisodeBrief>> _getDownloadedRssItem() async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<EpisodeBrief> episodes = await dbHelper.getDownloadedRssItem();
|
List<EpisodeBrief> episodes = await dbHelper.getDownloadedRssItem();
|
||||||
@ -374,7 +485,7 @@ class _MyDownloadState extends State<MyDownload> {
|
|||||||
primary: false,
|
primary: false,
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
EpisodeGrid(
|
EpisodeGrid(
|
||||||
podcast: snapshot.data,
|
episodes: snapshot.data,
|
||||||
showDownload: true,
|
showDownload: true,
|
||||||
)
|
)
|
||||||
],
|
],
|
@ -49,7 +49,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
|||||||
ScrollController _controller;
|
ScrollController _controller;
|
||||||
_scrollListener() {
|
_scrollListener() {
|
||||||
double value = _controller.offset;
|
double value = _controller.offset;
|
||||||
setState(() => _topHeight = (100 - value) > 0 ? 100 - value : 0);
|
setState(() => _topHeight = (100 - value) > 60 ? 100 - value : 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _topHeight;
|
double _topHeight;
|
||||||
@ -58,12 +58,12 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_topHeight = 100;
|
_topHeight = 100;
|
||||||
_controller = ScrollController();
|
_controller = ScrollController()..addListener(_scrollListener);
|
||||||
_controller.addListener(_scrollListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_controller.removeListener(_scrollListener);
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: _topHeight == 0 ? Text('Playlist') : Center(),
|
title: _topHeight == 60 ? Text('Playlist') : Center(),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
@ -95,56 +95,89 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Transform.scale(
|
Container(
|
||||||
alignment: Alignment.topLeft,
|
height: _topHeight,
|
||||||
scale: _topHeight / 100,
|
child: Row(
|
||||||
child: Container(
|
children: <Widget>[
|
||||||
height: _topHeight,
|
Container(
|
||||||
padding: EdgeInsets.only(
|
height: _topHeight,
|
||||||
bottom: (_topHeight - 60) > 0 ? _topHeight - 60 : 0,
|
padding: EdgeInsets.only(
|
||||||
left: 60),
|
left: 70,
|
||||||
alignment: Alignment.bottomLeft,
|
|
||||||
child: RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
text: 'Total ',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).accentColor,
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
),
|
||||||
children: <TextSpan>[
|
alignment: Alignment.centerLeft,
|
||||||
TextSpan(
|
child: RichText(
|
||||||
text: data.item1.length.toString(),
|
text: TextSpan(
|
||||||
style: TextStyle(
|
text: _topHeight > 90 ? 'Playlist\n' : '',
|
||||||
color: Theme.of(context).accentColor,
|
style: TextStyle(
|
||||||
fontSize: 40,
|
color:
|
||||||
)),
|
Theme.of(context).textTheme.bodyText1.color,
|
||||||
TextSpan(
|
fontSize: 30,
|
||||||
text: data.item1.length < 2
|
),
|
||||||
? ' episode'
|
children: <TextSpan>[
|
||||||
: ' episodes ',
|
TextSpan(
|
||||||
style: TextStyle(
|
text: data.item1.length.toString(),
|
||||||
color: Theme.of(context).accentColor,
|
style: GoogleFonts.cairo(
|
||||||
fontSize: 20,
|
textStyle: TextStyle(
|
||||||
)),
|
color: Theme.of(context).accentColor,
|
||||||
TextSpan(
|
fontSize: 30,
|
||||||
text: _sumPlaylistLength(data.item1).toString(),
|
),
|
||||||
style: GoogleFonts.teko(
|
),
|
||||||
textStyle: TextStyle(
|
),
|
||||||
color: Theme.of(context).accentColor,
|
TextSpan(
|
||||||
fontSize: 60,
|
text: data.item1.length < 2
|
||||||
)),
|
? ' episode '
|
||||||
|
: ' episodes ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
fontSize: 20,
|
||||||
|
)),
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
_sumPlaylistLength(data.item1).toString(),
|
||||||
|
style: GoogleFonts.cairo(
|
||||||
|
textStyle: TextStyle(
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
fontSize: 30,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' mins',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
fontSize: 20,
|
||||||
|
)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
TextSpan(
|
),
|
||||||
text: ' mins',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).accentColor,
|
|
||||||
fontSize: 20,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Spacer(),
|
||||||
|
_topHeight > 65
|
||||||
|
? Center()
|
||||||
|
: Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
right: 20, bottom: 80 - _topHeight),
|
||||||
|
child: data.item2
|
||||||
|
? Padding(
|
||||||
|
padding: EdgeInsets.only(right: 15),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 15,
|
||||||
|
child: WaveLoader()),
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
icon: Icon(Icons.play_circle_filled,
|
||||||
|
size: 40,
|
||||||
|
color:
|
||||||
|
Theme.of(context).accentColor),
|
||||||
|
onPressed: () => audio.playlistLoad(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Divider(
|
||||||
|
height: 3,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: AnimatedList(
|
child: AnimatedList(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
@ -224,15 +257,19 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
|||||||
trailing: index == 0
|
trailing: index == 0
|
||||||
? data.item2
|
? data.item2
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
right: 12.0),
|
right: 15, top: 20),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 15,
|
height: 15,
|
||||||
child: WaveLoader()),
|
child: WaveLoader()),
|
||||||
)
|
)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
icon: Icon(Icons.play_arrow),
|
icon: Icon(
|
||||||
|
Icons.play_arrow,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentColor,
|
||||||
|
),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
audio.playlistLoad())
|
audio.playlistLoad())
|
||||||
: Transform.rotate(
|
: Transform.rotate(
|
||||||
|
@ -54,6 +54,8 @@ class DBHelper {
|
|||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, link ,update_count FROM PodcastLocal WHERE id = ?',
|
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, link ,update_count FROM PodcastLocal WHERE id = ?',
|
||||||
[s]);
|
[s]);
|
||||||
|
int count = Sqflite.firstIntValue(await dbClient.rawQuery(
|
||||||
|
'SELECT COUNT(*) FROM Episodes WHERE feed_id = ?', [s]));
|
||||||
podcastLocal.add(PodcastLocal(
|
podcastLocal.add(PodcastLocal(
|
||||||
list.first['title'],
|
list.first['title'],
|
||||||
list.first['imageUrl'],
|
list.first['imageUrl'],
|
||||||
@ -64,7 +66,8 @@ class DBHelper {
|
|||||||
list.first['imagePath'],
|
list.first['imagePath'],
|
||||||
list.first['provider'],
|
list.first['provider'],
|
||||||
list.first['link'],
|
list.first['link'],
|
||||||
upateCount: list.first['update_count']));
|
upateCount: list.first['update_count'],
|
||||||
|
episodeCount: count));
|
||||||
});
|
});
|
||||||
return podcastLocal;
|
return podcastLocal;
|
||||||
}
|
}
|
||||||
@ -398,13 +401,6 @@ class DBHelper {
|
|||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < (result - count); i++) {
|
for (int i = 0; i < (result - count); i++) {
|
||||||
print(feed.items[i].title);
|
print(feed.items[i].title);
|
||||||
// if (feed.items[i].itunes.summary != null) {
|
|
||||||
// feed.items[i].itunes.summary.contains('<')
|
|
||||||
// ? description = feed.items[i].itunes.summary
|
|
||||||
// : description = feed.items[i].description;
|
|
||||||
// } else {
|
|
||||||
// description = feed.items[i].description;
|
|
||||||
// }
|
|
||||||
description = _getDescription(
|
description = _getDescription(
|
||||||
feed.items[i].content.value ?? '',
|
feed.items[i].content.value ?? '',
|
||||||
feed.items[i].description ?? '',
|
feed.items[i].description ?? '',
|
||||||
@ -444,11 +440,11 @@ class DBHelper {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result - count;
|
return (result - count) < 0 ? 0 : (result - count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> getRssItem(String id) async {
|
Future<List<EpisodeBrief>> getRssItem(String id, int i) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
@ -456,7 +452,7 @@ class DBHelper {
|
|||||||
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
|
||||||
E.downloaded, P.primaryColor , E.media_id
|
E.downloaded, P.primaryColor , E.media_id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE P.id = ? ORDER BY E.milliseconds DESC""", [id]);
|
WHERE P.id = ? ORDER BY E.milliseconds DESC LIMIT ?""", [id, i]);
|
||||||
for (int x = 0; x < list.length; x++) {
|
for (int x = 0; x < list.length; x++) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
@ -557,14 +553,14 @@ class DBHelper {
|
|||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> getLikedRssItem() async {
|
Future<List<EpisodeBrief>> getLikedRssItem(int i) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = List();
|
List<EpisodeBrief> episodes = List();
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
||||||
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
||||||
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.liked = 1 ORDER BY E.milliseconds DESC LIMIT 99""");
|
WHERE E.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""",[i]);
|
||||||
for (int x = 0; x < list.length; x++) {
|
for (int x = 0; x < list.length; x++) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
list[x]['title'],
|
list[x]['title'],
|
||||||
|
@ -5,9 +5,11 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
@ -45,9 +47,10 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
if (mounted) setState(() {});
|
if (mounted) setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal) async {
|
Future<List<EpisodeBrief>> _getRssItem(
|
||||||
|
PodcastLocal podcastLocal, int i) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<EpisodeBrief> episodes = await dbHelper.getRssItem(podcastLocal.id);
|
List<EpisodeBrief> episodes = await dbHelper.getRssItem(podcastLocal.id, i);
|
||||||
if (podcastLocal.provider.contains('fireside')) {
|
if (podcastLocal.provider.contains('fireside')) {
|
||||||
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
||||||
await data.getData();
|
await data.getData();
|
||||||
@ -156,7 +159,25 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
double top = 0;
|
double _topHeight = 0;
|
||||||
|
|
||||||
|
ScrollController _controller;
|
||||||
|
int _top;
|
||||||
|
bool _loadMore;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadMore = false;
|
||||||
|
_top = 33;
|
||||||
|
_controller = ScrollController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -178,177 +199,239 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
onRefresh: () => _updateRssItem(widget.podcastLocal),
|
onRefresh: () => _updateRssItem(widget.podcastLocal),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FutureBuilder<List<EpisodeBrief>>(
|
Column(
|
||||||
future: _getRssItem(widget.podcastLocal),
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
builder: (context, snapshot) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
if (snapshot.hasError) print(snapshot.error);
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
return (snapshot.hasData)
|
children: <Widget>[
|
||||||
? CustomScrollView(
|
Expanded(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
child: FutureBuilder<List<EpisodeBrief>>(
|
||||||
primary: true,
|
future: _getRssItem(widget.podcastLocal, _top),
|
||||||
slivers: <Widget>[
|
builder: (context, snapshot) {
|
||||||
SliverAppBar(
|
if (snapshot.hasError) print(snapshot.error);
|
||||||
brightness: Brightness.dark,
|
return (snapshot.hasData)
|
||||||
actions: <Widget>[
|
? CustomScrollView(
|
||||||
PopupMenuButton<String>(
|
controller: _controller
|
||||||
shape: RoundedRectangleBorder(
|
..addListener(() async {
|
||||||
borderRadius: BorderRadius.all(
|
if (_controller.offset ==
|
||||||
Radius.circular(10))),
|
_controller
|
||||||
elevation: 2,
|
.position.maxScrollExtent &&
|
||||||
tooltip: 'Menu',
|
snapshot.data.length == _top) {
|
||||||
itemBuilder: (context) => [
|
if (mounted)
|
||||||
widget.podcastLocal.link != null
|
setState(() => _loadMore = true);
|
||||||
? PopupMenuItem(
|
await Future.delayed(
|
||||||
value: widget.podcastLocal.link,
|
Duration(seconds: 3));
|
||||||
|
if (mounted)
|
||||||
|
setState(() {
|
||||||
|
_top = _top + 33;
|
||||||
|
_loadMore = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
//primary: true,
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverAppBar(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
actions: <Widget>[
|
||||||
|
PopupMenuButton<String>(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10))),
|
||||||
|
elevation: 2,
|
||||||
|
tooltip: 'Menu',
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
widget.podcastLocal.link != null
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: widget
|
||||||
|
.podcastLocal.link,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.link,
|
||||||
|
color: Theme.of(
|
||||||
|
context)
|
||||||
|
.tabBarTheme
|
||||||
|
.labelColor),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets
|
||||||
|
.symmetric(
|
||||||
|
horizontal:
|
||||||
|
5.0),
|
||||||
|
),
|
||||||
|
Text('Visit Site'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: widget.podcastLocal.rssUrl,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.only(left: 10),
|
EdgeInsets.only(left: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(Icons.link,
|
Icon(
|
||||||
color: Theme.of(context)
|
Icons.rss_feed,
|
||||||
.tabBarTheme
|
color: Theme.of(context)
|
||||||
.labelColor),
|
.tabBarTheme
|
||||||
|
.labelColor,
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.symmetric(
|
EdgeInsets.symmetric(
|
||||||
horizontal: 5.0),
|
horizontal: 5.0),
|
||||||
),
|
),
|
||||||
Text('Visit Site'),
|
Text('View Rss Feed'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
: Center(),
|
],
|
||||||
PopupMenuItem(
|
onSelected: (url) {
|
||||||
value: widget.podcastLocal.rssUrl,
|
_launchUrl(url);
|
||||||
child: Container(
|
},
|
||||||
padding: EdgeInsets.only(left: 10),
|
)
|
||||||
child: Row(
|
],
|
||||||
|
elevation: 0,
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
expandedHeight: 150 +
|
||||||
|
MediaQuery.of(context).padding.top,
|
||||||
|
backgroundColor: _color,
|
||||||
|
floating: true,
|
||||||
|
pinned: true,
|
||||||
|
flexibleSpace: LayoutBuilder(builder:
|
||||||
|
(BuildContext context,
|
||||||
|
BoxConstraints constraints) {
|
||||||
|
_topHeight = constraints.biggest.height;
|
||||||
|
return FlexibleSpaceBar(
|
||||||
|
background: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(
|
Container(
|
||||||
Icons.rss_feed,
|
margin: EdgeInsets.only(
|
||||||
color: Theme.of(context)
|
top: 120 +
|
||||||
.tabBarTheme
|
MediaQuery.of(context)
|
||||||
.labelColor,
|
.padding
|
||||||
|
.top),
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 80, right: 120),
|
||||||
|
color: Colors.white10,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize.min,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
widget.podcastLocal
|
||||||
|
.author ??
|
||||||
|
'',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow
|
||||||
|
.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
Colors.white)),
|
||||||
|
widget.podcastLocal.provider
|
||||||
|
.isNotEmpty
|
||||||
|
? Text(
|
||||||
|
'Hosted on ' +
|
||||||
|
widget
|
||||||
|
.podcastLocal
|
||||||
|
.provider,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors
|
||||||
|
.white),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Container(
|
||||||
padding: EdgeInsets.symmetric(
|
alignment:
|
||||||
horizontal: 5.0),
|
Alignment.centerRight,
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(right: 10),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 120,
|
||||||
|
child: Image.file(File(
|
||||||
|
"${widget.podcastLocal.imagePath}")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: podcastInfo(context),
|
||||||
),
|
),
|
||||||
Text('View Rss Feed'),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
title: _topHeight <
|
||||||
),
|
70 +
|
||||||
],
|
MediaQuery.of(context)
|
||||||
onSelected: (url) {
|
.padding
|
||||||
_launchUrl(url);
|
.top
|
||||||
},
|
? Text(widget.podcastLocal.title,
|
||||||
)
|
|
||||||
],
|
|
||||||
elevation: 0,
|
|
||||||
iconTheme: IconThemeData(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
expandedHeight:
|
|
||||||
150 + MediaQuery.of(context).padding.top,
|
|
||||||
backgroundColor: _color,
|
|
||||||
floating: true,
|
|
||||||
pinned: true,
|
|
||||||
flexibleSpace: LayoutBuilder(builder:
|
|
||||||
(BuildContext context,
|
|
||||||
BoxConstraints constraints) {
|
|
||||||
top = constraints.biggest.height;
|
|
||||||
return FlexibleSpaceBar(
|
|
||||||
background: Stack(
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(
|
|
||||||
top: 120 +
|
|
||||||
MediaQuery.of(context)
|
|
||||||
.padding
|
|
||||||
.top),
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
left: 80, right: 120),
|
|
||||||
color: Colors.white10,
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
widget.podcastLocal.author ??
|
|
||||||
'',
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow:
|
overflow:
|
||||||
TextOverflow.ellipsis,
|
TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white)),
|
color: Colors.white))
|
||||||
widget.podcastLocal.provider
|
: Center(),
|
||||||
.isNotEmpty
|
);
|
||||||
? Text(
|
}),
|
||||||
'Hosted on ' +
|
|
||||||
widget.podcastLocal
|
|
||||||
.provider,
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white),
|
|
||||||
)
|
|
||||||
: Center(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
padding: EdgeInsets.only(right: 10),
|
|
||||||
child: SizedBox(
|
|
||||||
height: 120,
|
|
||||||
child: Image.file(File(
|
|
||||||
"${widget.podcastLocal.imagePath}")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: podcastInfo(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
title: top <
|
SliverList(
|
||||||
70 +
|
delegate: SliverChildBuilderDelegate(
|
||||||
MediaQuery.of(context)
|
(BuildContext context, int index) {
|
||||||
.padding
|
return hostsList(context, hosts);
|
||||||
.top
|
},
|
||||||
? Text(widget.podcastLocal.title,
|
childCount: 1,
|
||||||
maxLines: 1,
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
style:
|
EpisodeGrid(
|
||||||
TextStyle(color: Colors.white))
|
episodes: snapshot.data,
|
||||||
: Center(),
|
showFavorite: true,
|
||||||
);
|
showNumber: true,
|
||||||
}),
|
updateCount:
|
||||||
),
|
widget.podcastLocal.upateCount,
|
||||||
SliverList(
|
episodeCount:
|
||||||
delegate: SliverChildBuilderDelegate(
|
widget.podcastLocal.episodeCount,
|
||||||
(BuildContext context, int index) {
|
),
|
||||||
return hostsList(context, hosts);
|
SliverList(
|
||||||
},
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount: 1,
|
(BuildContext context, int index) {
|
||||||
),
|
return _loadMore
|
||||||
),
|
? Container(
|
||||||
EpisodeGrid(
|
height: 2,
|
||||||
podcast: snapshot.data,
|
child:
|
||||||
showFavorite: true,
|
LinearProgressIndicator())
|
||||||
showNumber: true,
|
: Center();
|
||||||
updateCount: widget.podcastLocal.upateCount,
|
},
|
||||||
),
|
childCount: 1,
|
||||||
],
|
),
|
||||||
)
|
),
|
||||||
: Center(child: CircularProgressIndicator());
|
],
|
||||||
},
|
)
|
||||||
|
: Center(child: CircularProgressIndicator());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Selector<AudioPlayerNotifier, bool>(
|
||||||
|
selector: (_, audio) => audio.playerRunning,
|
||||||
|
builder: (_, data, __) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Container(child: PlayerWidget()),
|
Container(child: PlayerWidget()),
|
||||||
],
|
],
|
||||||
|
@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
@ -123,22 +124,24 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
|||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
padding: EdgeInsets.only(bottom: 40, left: 60),
|
padding: EdgeInsets.only(bottom: 20, left: 60),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Total ',
|
text: 'Total ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).accentColor,
|
color: Theme.of(context).accentColor,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
),
|
),
|
||||||
children: <TextSpan>[
|
children: <TextSpan>[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _fileNum.toString(),
|
text: _fileNum.toString(),
|
||||||
style: TextStyle(
|
style: GoogleFonts.cairo(
|
||||||
color: Theme.of(context).accentColor,
|
textStyle: TextStyle(
|
||||||
fontSize: 40,
|
color: Theme.of(context).accentColor,
|
||||||
)),
|
fontSize: 40,
|
||||||
|
)),
|
||||||
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _fileNum < 2 ? ' episode' : ' episodes ',
|
text: _fileNum < 2 ? ' episode' : ' episodes ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -146,11 +149,13 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
|||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
)),
|
)),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: (_size ~/ 1000000).toString(),
|
text: (_size ~/ 1000000).toString(),
|
||||||
style: TextStyle(
|
style: GoogleFonts.cairo(
|
||||||
color: Theme.of(context).accentColor,
|
textStyle: TextStyle(
|
||||||
fontSize: 60,
|
color: Theme.of(context).accentColor,
|
||||||
)),
|
fontSize: 50,
|
||||||
|
)),
|
||||||
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' Mb',
|
text: ' Mb',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -196,7 +196,7 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
..onEnd = _handleDragEnd
|
..onEnd = _handleDragEnd
|
||||||
..dragStartBehavior = dragStartBehavior;
|
..dragStartBehavior = dragStartBehavior;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageProvider get activeThumbImage => _activeThumbImage;
|
ImageProvider get activeThumbImage => _activeThumbImage;
|
||||||
ImageProvider _activeThumbImage;
|
ImageProvider _activeThumbImage;
|
||||||
set activeThumbImage(ImageProvider value) {
|
set activeThumbImage(ImageProvider value) {
|
||||||
@ -285,7 +285,9 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
positionController.value += delta;
|
positionController.value += delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
positionController.addListener(() {onDrag(positionController.value);});
|
positionController.addListener(() {
|
||||||
|
onDrag(positionController.value);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,8 +299,6 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
reactionController.reverse();
|
reactionController.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
|
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
|
||||||
assert(debugHandleEvent(event, entry));
|
assert(debugHandleEvent(event, entry));
|
||||||
@ -309,7 +309,7 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
Color _cachedThumbColor;
|
Color _cachedThumbColor;
|
||||||
ImageProvider _cachedThumbImage;
|
ImageProvider _cachedThumbImage;
|
||||||
BoxPainter _cachedThumbPainter;
|
BoxPainter _cachedThumbPainter;
|
||||||
|
|
||||||
BoxDecoration _createDefaultThumbDecoration(
|
BoxDecoration _createDefaultThumbDecoration(
|
||||||
Color color, ImageProvider image) {
|
Color color, ImageProvider image) {
|
||||||
return BoxDecoration(
|
return BoxDecoration(
|
||||||
@ -335,9 +335,7 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
super.describeSemanticsConfiguration(config);
|
super.describeSemanticsConfiguration(config);
|
||||||
config.isToggled = value == true;
|
config.isToggled = value == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
final Canvas canvas = context.canvas;
|
final Canvas canvas = context.canvas;
|
||||||
@ -413,7 +411,7 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
);
|
);
|
||||||
|
|
||||||
var starPaint = Paint()
|
var starPaint = Paint()
|
||||||
..strokeWidth = 4 + (6 * (1 - currentValue))
|
..strokeWidth = 2 + (6 * (1 - currentValue))
|
||||||
..strokeCap = StrokeCap.round
|
..strokeCap = StrokeCap.round
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..color = Color.fromARGB((255 * currentValue).floor(), 255, 255, 255);
|
..color = Color.fromARGB((255 * currentValue).floor(), 255, 255, 255);
|
||||||
@ -450,14 +448,14 @@ class _RenderSwitch extends RenderToggleable {
|
|||||||
_isPainting = false;
|
_isPainting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.drawLine(
|
canvas.drawLine(
|
||||||
Offset(offset.dx + _kSwitchWidth * 0.3, offset.dy + _kSwitchHeight * 0.5),
|
Offset(offset.dx + _kSwitchWidth * 0.3, offset.dy + _kSwitchHeight * 0.5),
|
||||||
Offset(
|
Offset(
|
||||||
offset.dx +
|
offset.dx +
|
||||||
(_kSwitchWidth * 0.3) +
|
(_kSwitchWidth * 0.3) +
|
||||||
(_kSwitchWidth / 2 * (1 - currentValue)),
|
(_kSwitchWidth / 2 * (1 - currentValue)),
|
||||||
offset.dy + _kSwitchHeight * 0.5),
|
offset.dy + _kSwitchHeight * 0.5),
|
||||||
linePaint,
|
linePaint,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,20 +17,22 @@ import 'package:tsacdop/episodes/episodedetail.dart';
|
|||||||
import 'package:tsacdop/util/colorize.dart';
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
|
|
||||||
class EpisodeGrid extends StatelessWidget {
|
class EpisodeGrid extends StatelessWidget {
|
||||||
final List<EpisodeBrief> podcast;
|
final List<EpisodeBrief> episodes;
|
||||||
final bool showFavorite;
|
final bool showFavorite;
|
||||||
final bool showDownload;
|
final bool showDownload;
|
||||||
final bool showNumber;
|
final bool showNumber;
|
||||||
final int updateCount;
|
final int updateCount;
|
||||||
final String heroTag;
|
final String heroTag;
|
||||||
|
final int episodeCount;
|
||||||
EpisodeGrid(
|
EpisodeGrid(
|
||||||
{Key key,
|
{Key key,
|
||||||
@required this.podcast,
|
@required this.episodes,
|
||||||
this.heroTag = '',
|
this.heroTag = '',
|
||||||
this.showDownload = false,
|
this.showDownload = false,
|
||||||
this.showFavorite = false,
|
this.showFavorite = false,
|
||||||
this.showNumber = false,
|
this.showNumber = false,
|
||||||
this.updateCount = 0})
|
this.updateCount = 0,
|
||||||
|
this.episodeCount = 0})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -115,14 +117,14 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||||
? podcast[index].primaryColor.colorizedark()
|
? episodes[index].primaryColor.colorizedark()
|
||||||
: podcast[index].primaryColor.colorizeLight();
|
: episodes[index].primaryColor.colorizeLight();
|
||||||
return Selector<AudioPlayerNotifier,
|
return Selector<AudioPlayerNotifier,
|
||||||
Tuple2<EpisodeBrief, List<String>>>(
|
Tuple2<EpisodeBrief, List<String>>>(
|
||||||
selector: (_, audio) => Tuple2(audio?.episode,
|
selector: (_, audio) => Tuple2(audio?.episode,
|
||||||
audio.queue.playlist.map((e) => e.enclosureUrl).toList()),
|
audio.queue.playlist.map((e) => e.enclosureUrl).toList()),
|
||||||
builder: (_, data, __) => OpenContainerWrapper(
|
builder: (_, data, __) => OpenContainerWrapper(
|
||||||
episode: podcast[index],
|
episode: episodes[index],
|
||||||
closedBuilder: (context, action, boo) => Container(
|
closedBuilder: (context, action, boo) => Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
@ -143,21 +145,11 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
details.globalPosition.dx, details.globalPosition.dy),
|
details.globalPosition.dx, details.globalPosition.dy),
|
||||||
onLongPress: () => _showPopupMenu(
|
onLongPress: () => _showPopupMenu(
|
||||||
_offset,
|
_offset,
|
||||||
podcast[index],
|
episodes[index],
|
||||||
context,
|
context,
|
||||||
data.item1 == podcast[index],
|
data.item1 == episodes[index],
|
||||||
data.item2.contains(podcast[index].enclosureUrl)),
|
data.item2.contains(episodes[index].enclosureUrl)),
|
||||||
onTap: action,
|
onTap: action,
|
||||||
// {
|
|
||||||
// Navigator.push(
|
|
||||||
// context,
|
|
||||||
// ScaleRoute(
|
|
||||||
// page: EpisodeDetail(
|
|
||||||
// episodeItem: podcast[index],
|
|
||||||
// heroTag: heroTag,
|
|
||||||
// )),
|
|
||||||
// )
|
|
||||||
// },
|
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -187,7 +179,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
backgroundColor:
|
backgroundColor:
|
||||||
_c.withOpacity(0.5),
|
_c.withOpacity(0.5),
|
||||||
backgroundImage: FileImage(File(
|
backgroundImage: FileImage(File(
|
||||||
"${podcast[index].imagePath}")),
|
"${episodes[index].imagePath}")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
@ -205,7 +197,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
? Container(
|
? Container(
|
||||||
alignment: Alignment.topRight,
|
alignment: Alignment.topRight,
|
||||||
child: Text(
|
child: Text(
|
||||||
(podcast.length - index).toString(),
|
(episodeCount- index).toString(),
|
||||||
style: GoogleFonts.teko(
|
style: GoogleFonts.teko(
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: _width / 24,
|
fontSize: _width / 24,
|
||||||
@ -224,7 +216,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
padding: EdgeInsets.only(top: 2.0),
|
padding: EdgeInsets.only(top: 2.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
podcast[index].title,
|
episodes[index].title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _width / 32,
|
fontSize: _width / 32,
|
||||||
),
|
),
|
||||||
@ -240,7 +232,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomLeft,
|
alignment: Alignment.bottomLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
podcast[index].dateToString(),
|
episodes[index].dateToString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _width / 35,
|
fontSize: _width / 35,
|
||||||
color: _c,
|
color: _c,
|
||||||
@ -250,7 +242,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
Spacer(),
|
Spacer(),
|
||||||
showDownload
|
showDownload
|
||||||
? DownloadIcon(
|
? DownloadIcon(
|
||||||
episodeBrief: podcast[index])
|
episodeBrief: episodes[index])
|
||||||
: Center(),
|
: Center(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(1),
|
padding: EdgeInsets.all(1),
|
||||||
@ -258,7 +250,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
showFavorite
|
showFavorite
|
||||||
? Container(
|
? Container(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
child: (podcast[index].liked == 0)
|
child: (episodes[index].liked == 0)
|
||||||
? Center()
|
? Center()
|
||||||
: IconTheme(
|
: IconTheme(
|
||||||
data: IconThemeData(size: 15),
|
data: IconThemeData(size: 15),
|
||||||
@ -281,7 +273,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: podcast.length,
|
childCount: episodes.length,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -11,7 +11,7 @@ description: An easy-use podacasts player.
|
|||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 0.1.4
|
version: 0.1.5
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.6.0 <3.0.0"
|
sdk: ">=2.6.0 <3.0.0"
|
||||||
@ -51,7 +51,7 @@ dev_dependencies:
|
|||||||
workmanager: ^0.2.2
|
workmanager: ^0.2.2
|
||||||
flutter_colorpicker: ^0.3.2
|
flutter_colorpicker: ^0.3.2
|
||||||
app_settings: ^3.0.1
|
app_settings: ^3.0.1
|
||||||
fl_chart: ^0.8.3
|
fl_chart: ^0.8.7
|
||||||
audio_service: ^0.6.2
|
audio_service: ^0.6.2
|
||||||
just_audio: ^0.1.3
|
just_audio: ^0.1.3
|
||||||
rxdart: ^0.23.1
|
rxdart: ^0.23.1
|
||||||
@ -60,8 +60,6 @@ dev_dependencies:
|
|||||||
url: https://github.com/galonsos/line_icons.git
|
url: https://github.com/galonsos/line_icons.git
|
||||||
flutter_file_dialog: ^0.0.5
|
flutter_file_dialog: ^0.0.5
|
||||||
flutter_linkify: ^3.1.0
|
flutter_linkify: ^3.1.0
|
||||||
animations: ^1.0.0+5
|
|
||||||
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://www.dartlang.org/tools/pub/pubspec
|
# following page: https://www.dartlang.org/tools/pub/pubspec
|
||||||
|
Loading…
x
Reference in New Issue
Block a user