feat: update setting pages to material you
This commit is contained in:
parent
e97a493135
commit
92dd3dd34e
|
@ -87,8 +87,8 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
final audio = context.watch<AudioPlayerNotifier>();
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: context.priamryContainer,
|
||||
systemNavigationBarColor: context.priamryContainer,
|
||||
statusBarColor: widget.episodeItem!.cardColor(context),
|
||||
systemNavigationBarColor: widget.episodeItem!.cardColor(context),
|
||||
systemNavigationBarContrastEnforced: false,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
statusBarBrightness: context.brightness,
|
||||
|
@ -104,7 +104,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
backgroundColor: context.onPrimary,
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
|
@ -116,7 +116,8 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||
return <Widget>[
|
||||
SliverAppBar(
|
||||
backgroundColor: context.priamryContainer,
|
||||
backgroundColor:
|
||||
widget.episodeItem!.cardColor(context),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
title: _showTitle
|
||||
|
@ -290,33 +291,35 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
),
|
||||
),
|
||||
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
|
||||
selector: (_, audio) =>
|
||||
Tuple2(audio.playerRunning, audio.playerHeight),
|
||||
builder: (_, data, __) {
|
||||
final height = kMinPlayerHeight[data.item2!.index];
|
||||
return Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
padding:
|
||||
EdgeInsets.only(bottom: data.item1 ? height : 0),
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
height: _showMenu ? 50 : 0,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: MenuBar(
|
||||
episodeItem: widget.episodeItem,
|
||||
heroTag: widget.heroTag,
|
||||
hide: widget.hide),
|
||||
),
|
||||
selector: (_, audio) =>
|
||||
Tuple2(audio.playerRunning, audio.playerHeight),
|
||||
builder: (_, data, __) {
|
||||
final height = kMinPlayerHeight[data.item2!.index];
|
||||
return Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
padding: EdgeInsets.only(bottom: data.item1 ? height : 0),
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
height: _showMenu ? 50 : 0,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: MenuBar(
|
||||
episodeItem: widget.episodeItem,
|
||||
heroTag: widget.heroTag,
|
||||
hide: widget.hide),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<AudioPlayerNotifier, EpisodeBrief?>(
|
||||
selector: (_, audio) => audio.episode,
|
||||
builder: (_, data, __) => Container(
|
||||
child: PlayerWidget(
|
||||
playerKey: _playerKey,
|
||||
isPlayingPage: data == widget.episodeItem))),
|
||||
selector: (_, audio) => audio.episode,
|
||||
builder: (_, data, __) => Container(
|
||||
child: PlayerWidget(
|
||||
playerKey: _playerKey,
|
||||
isPlayingPage: data == widget.episodeItem),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -28,7 +28,7 @@ class MenuBarState extends State<MenuBar> {
|
|||
return Container(
|
||||
height: 50.0,
|
||||
decoration: BoxDecoration(
|
||||
color: context.priamryContainer,
|
||||
color: widget.episodeItem!.cardColor(context),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
@ -48,7 +48,6 @@ class MenuBarState extends State<MenuBar> {
|
|||
child: Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
color: context.priamryContainer,
|
||||
child: widget.hide!
|
||||
? Center()
|
||||
: CircleAvatar(
|
||||
|
|
|
@ -7,27 +7,245 @@ import '../widgets/custom_widget.dart';
|
|||
|
||||
const String version = '0.6.0';
|
||||
|
||||
class AboutApp extends StatefulWidget {
|
||||
class AboutApp extends StatelessWidget {
|
||||
@override
|
||||
_AboutAppState createState() => _AboutAppState();
|
||||
}
|
||||
Widget build(BuildContext context) {
|
||||
OverlayEntry _createOverlayEntry(TapDownDetails detail) {
|
||||
// RenderBox renderBox = context.findRenderObject();
|
||||
final offset = detail.globalPosition;
|
||||
return OverlayEntry(
|
||||
builder: (constext) => Positioned(
|
||||
left: offset.dx - 5,
|
||||
top: offset.dy - 120,
|
||||
child: Container(
|
||||
width: 20,
|
||||
height: 120,
|
||||
color: Colors.transparent,
|
||||
alignment: Alignment.topCenter,
|
||||
child: HeartSet(height: 120, width: 20)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _AboutAppState extends State<AboutApp> {
|
||||
ScrollController? _scrollController;
|
||||
late bool _scroll;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scroll = false;
|
||||
_scrollController = ScrollController()
|
||||
..addListener(() {
|
||||
if (_scrollController!.offset > 0 && !_scroll && mounted) {
|
||||
setState(() => _scroll = true);
|
||||
}
|
||||
if (_scrollController!.offset <= 0 && _scroll && mounted) {
|
||||
setState(() => _scroll = false);
|
||||
}
|
||||
});
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: context.onPrimary,
|
||||
statusBarIconBrightness: context.iconBrightness,
|
||||
systemNavigationBarColor: context.onPrimary,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
backgroundColor: context.onPrimary,
|
||||
title: Text(s.homeToprightMenuAbout),
|
||||
scrolledUnderElevation: 1,
|
||||
leading: CustomBackButton(),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 110.0,
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Image(
|
||||
image: AssetImage('assets/logo.png'),
|
||||
height: 80,
|
||||
),
|
||||
Text(s.version(version)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Text(
|
||||
'Tsacdop is a podcast player built with flutter, a clean, simply beautiful and friendly app.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
'https://tsacdop.stonegate.me/#/privacy'.launchUrl,
|
||||
style: TextButton.styleFrom(
|
||||
primary: context.accentColor,
|
||||
textStyle: TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
s.privacyPolicy,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
height: 4,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: context.accentColor, shape: BoxShape.circle),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
'https://tsacdop.stonegate.me/#/changelog'
|
||||
.launchUrl,
|
||||
style: TextButton.styleFrom(
|
||||
primary: context.accentColor,
|
||||
textStyle: TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(s.changelog,
|
||||
style: TextStyle(color: context.accentColor)),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_listItem(context, 'Twitter @tsacdop',
|
||||
LineIcons.twitter, 'https://twitter.com/tsacdop'),
|
||||
_listItem(context, 'GitHub', LineIcons.alternateGithub,
|
||||
'https://github.com/stonega/tsacdop'),
|
||||
_listItem(context, 'Telegram', LineIcons.telegram,
|
||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||
_listItem(context, 'Reddit', LineIcons.redditLogo,
|
||||
'https://www.reddit.com/r/Tsacdop'),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: ElevatedButton(
|
||||
onPressed: () =>
|
||||
'https://www.buymeacoffee.com/stonegate'
|
||||
.launchUrl,
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: Color(0xffffdd00),
|
||||
elevation: 0,
|
||||
enableFeedback: false,
|
||||
),
|
||||
child: Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.0),
|
||||
alignment: Alignment.center,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Buy Me A Coffee',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Image(
|
||||
image:
|
||||
AssetImage('assets/buymeacoffee.png'),
|
||||
height: 20,
|
||||
fit: BoxFit.fitHeight,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 25),
|
||||
Text(
|
||||
s.translators,
|
||||
style: TextStyle(
|
||||
color: context.accentColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
Icon(Icons.favorite, color: Colors.red, size: 20),
|
||||
],
|
||||
),
|
||||
_translatorInfo(context, name: 'Atrate'),
|
||||
_translatorInfo(context, name: 'ppp', flag: 'fr'),
|
||||
_translatorInfo(context,
|
||||
name: 'Joel Israel', flag: 'mx'),
|
||||
_translatorInfo(context,
|
||||
name: 'Bruno Pinheiro', flag: 'pt'),
|
||||
_translatorInfo(context,
|
||||
name: 'Edoardo Maria Elidoro', flag: 'it'),
|
||||
_translatorInfo(context,
|
||||
name: 'Murat T. Akyuz', flag: 'tr'),
|
||||
],
|
||||
),
|
||||
),
|
||||
//Spacer(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTapDown: (detail) async {
|
||||
OverlayEntry _overlayEntry;
|
||||
_overlayEntry = _createOverlayEntry(detail);
|
||||
Overlay.of(context)!.insert(_overlayEntry);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
_overlayEntry.remove();
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
'assets/text.png',
|
||||
height: 25,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
FlutterLogo(
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _listItem(
|
||||
|
@ -47,7 +265,7 @@ class _AboutAppState extends State<AboutApp> {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(icons, color: Theme.of(context).accentColor),
|
||||
Icon(icons, color: context.accentColor),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
|
@ -72,16 +290,17 @@ class _AboutAppState extends State<AboutApp> {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(LineIcons.user, color: Theme.of(context).accentColor),
|
||||
Icon(LineIcons.user, color: context.accentColor),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
)),
|
||||
child: Text(
|
||||
name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
),
|
||||
if (flag != null)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
|
@ -95,248 +314,4 @@ class _AboutAppState extends State<AboutApp> {
|
|||
],
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
OverlayEntry _createOverlayEntry(TapDownDetails detail) {
|
||||
// RenderBox renderBox = context.findRenderObject();
|
||||
var offset = detail.globalPosition;
|
||||
return OverlayEntry(
|
||||
builder: (constext) => Positioned(
|
||||
left: offset.dx - 5,
|
||||
top: offset.dy - 120,
|
||||
child: Container(
|
||||
width: 20,
|
||||
height: 120,
|
||||
color: Colors.transparent,
|
||||
alignment: Alignment.topCenter,
|
||||
child: HeartSet(height: 120, width: 20)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
appBar: AppBar(
|
||||
title: Text(s.homeToprightMenuAbout),
|
||||
leading: CustomBackButton(),
|
||||
elevation: _scroll ? 1 : 0,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 110.0,
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Image(
|
||||
image: AssetImage('assets/logo.png'),
|
||||
height: 80,
|
||||
),
|
||||
Text(s.version(version)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Text(
|
||||
'Tsacdop is a podcast player built with flutter, a clean, simply beautiful and friendly app.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
'https://tsacdop.stonegate.me/#/privacy'
|
||||
.launchUrl,
|
||||
style: TextButton.styleFrom(
|
||||
primary: context.accentColor,
|
||||
textStyle:
|
||||
TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
s.privacyPolicy,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
height: 4,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: context.accentColor,
|
||||
shape: BoxShape.circle),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
'https://tsacdop.stonegate.me/#/changelog'
|
||||
.launchUrl,
|
||||
style: TextButton.styleFrom(
|
||||
primary: context.accentColor,
|
||||
textStyle:
|
||||
TextStyle(fontWeight: FontWeight.bold)),
|
||||
child: Text(s.changelog,
|
||||
style: TextStyle(color: context.accentColor)),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_listItem(context, 'Twitter @tsacdop',
|
||||
LineIcons.twitter, 'https://twitter.com/tsacdop'),
|
||||
_listItem(
|
||||
context,
|
||||
'GitHub',
|
||||
LineIcons.alternateGithub,
|
||||
'https://github.com/stonega/tsacdop'),
|
||||
_listItem(context, 'Telegram', LineIcons.telegram,
|
||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||
_listItem(context, 'Reddit', LineIcons.redditLogo,
|
||||
'https://www.reddit.com/r/Tsacdop'),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: ElevatedButton(
|
||||
onPressed: () =>
|
||||
'https://www.buymeacoffee.com/stonegate'
|
||||
.launchUrl,
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: context.accentColor),
|
||||
child: Container(
|
||||
height: 30.0,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 4.0),
|
||||
alignment: Alignment.center,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text('Buy Me A Coffee',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold)),
|
||||
SizedBox(width: 10),
|
||||
Image(
|
||||
image: AssetImage(
|
||||
'assets/buymeacoffee.png'),
|
||||
height: 20,
|
||||
fit: BoxFit.fitHeight,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 25),
|
||||
Text(
|
||||
s.translators,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
Icon(Icons.favorite, color: Colors.red, size: 20),
|
||||
],
|
||||
),
|
||||
_translatorInfo(context, name: 'Atrate'),
|
||||
_translatorInfo(context, name: 'ppp', flag: 'fr'),
|
||||
_translatorInfo(context,
|
||||
name: 'Joel Israel', flag: 'mx'),
|
||||
_translatorInfo(context,
|
||||
name: 'Bruno Pinheiro', flag: 'pt'),
|
||||
_translatorInfo(context,
|
||||
name: 'Edoardo Maria Elidoro', flag: 'it'),
|
||||
_translatorInfo(context,
|
||||
name: 'Murat T. Akyuz', flag: 'tr'),
|
||||
],
|
||||
),
|
||||
),
|
||||
//Spacer(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTapDown: (detail) async {
|
||||
OverlayEntry _overlayEntry;
|
||||
_overlayEntry = _createOverlayEntry(detail);
|
||||
Overlay.of(context)!.insert(_overlayEntry);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
_overlayEntry.remove();
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
'assets/text.png',
|
||||
height: 25,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
FlutterLogo(
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,140 +120,147 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
|||
children: <Widget>[
|
||||
SafeArea(
|
||||
bottom: false,
|
||||
child: StretchingOverscrollIndicator(
|
||||
axisDirection: AxisDirection.down,
|
||||
child: NestedScrollView(
|
||||
innerScrollPositionKeyBuilder: () {
|
||||
return Key('tab${_controller!.index}');
|
||||
},
|
||||
pinnedHeaderSliverHeightBuilder: () => 50,
|
||||
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||
return <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 50.0,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: addFeature,
|
||||
tapTarget: Icon(Icons.add_circle_outline),
|
||||
title: s.featureDiscoverySearch,
|
||||
backgroundColor: Colors.cyan[600],
|
||||
buttonColor: Colors.cyan[500],
|
||||
description: s.featureDiscoverySearchDes,
|
||||
child: IconButton(
|
||||
tooltip: s.add,
|
||||
splashRadius: 20,
|
||||
icon: Icon(Icons.add_circle_outline),
|
||||
onPressed: () async {
|
||||
await showSearch<int?>(
|
||||
context: context,
|
||||
delegate: MyHomePageDelegate(
|
||||
searchFieldLabel:
|
||||
s.searchPodcast),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => {
|
||||
Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? settings.setTheme = ThemeMode.dark
|
||||
: settings.setTheme =
|
||||
ThemeMode.light
|
||||
child: NestedScrollView(
|
||||
innerScrollPositionKeyBuilder: () {
|
||||
return Key('tab${_controller!.index}');
|
||||
},
|
||||
pinnedHeaderSliverHeightBuilder: () => 50,
|
||||
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||
return <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 50.0,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: addFeature,
|
||||
tapTarget: Icon(Icons.add_circle_outline),
|
||||
title: s.featureDiscoverySearch,
|
||||
backgroundColor: Colors.cyan[600],
|
||||
buttonColor: Colors.cyan[500],
|
||||
description: s.featureDiscoverySearchDes,
|
||||
child: IconButton(
|
||||
tooltip: s.add,
|
||||
splashRadius: 20,
|
||||
icon: Icon(Icons.add_circle_outline),
|
||||
onPressed: () async {
|
||||
await showSearch<int?>(
|
||||
context: context,
|
||||
delegate: MyHomePageDelegate(
|
||||
searchFieldLabel:
|
||||
s.searchPodcast),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
'Tsacdop',
|
||||
style: GoogleFonts.quicksand(
|
||||
color: context.accentColor,
|
||||
textStyle: TextStyle(fontSize: 25)),
|
||||
),
|
||||
),
|
||||
featureDiscoveryOverlay(context,
|
||||
featureId: menuFeature,
|
||||
tapTarget: Icon(Icons.more_vert),
|
||||
backgroundColor: Colors.cyan[500],
|
||||
buttonColor: Colors.cyan[600],
|
||||
title: s.featureDiscoveryOMPL,
|
||||
description: s.featureDiscoveryOMPLDes,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(right: 5.0),
|
||||
child: PopupMenu(),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => {
|
||||
Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? settings.setTheme = ThemeMode.dark
|
||||
: settings.setTheme =
|
||||
ThemeMode.light
|
||||
},
|
||||
child: Text(
|
||||
'Tsacdop',
|
||||
style: GoogleFonts.quicksand(
|
||||
color: context.accentColor,
|
||||
textStyle: TextStyle(fontSize: 25)),
|
||||
),
|
||||
),
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: menuFeature,
|
||||
tapTarget: Icon(Icons.more_vert),
|
||||
backgroundColor: Colors.cyan[500],
|
||||
buttonColor: Colors.cyan[600],
|
||||
title: s.featureDiscoveryOMPL,
|
||||
description: s.featureDiscoveryOMPLDes,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(right: 5.0),
|
||||
child: PopupMenu(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Import(),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
width: context.width,
|
||||
child: ScrollPodcasts(),
|
||||
),
|
||||
),
|
||||
SliverPersistentHeader(
|
||||
delegate: _SliverAppBarDelegate(
|
||||
TabBar(
|
||||
indicator: _getIndicator(context),
|
||||
isScrollable: true,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
controller: _controller,
|
||||
tabs: <Widget>[
|
||||
Tab(
|
||||
child: Text(s.homeTabMenuRecent),
|
||||
),
|
||||
Tab(
|
||||
child: Text(s.homeTabMenuFavotite),
|
||||
),
|
||||
Tab(
|
||||
child: Text(s.download),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
pinned: true,
|
||||
Import(),
|
||||
],
|
||||
),
|
||||
];
|
||||
},
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
width: context.width,
|
||||
child: ScrollPodcasts(),
|
||||
),
|
||||
),
|
||||
SliverPersistentHeader(
|
||||
delegate: _SliverAppBarDelegate(
|
||||
TabBar(
|
||||
indicator: _getIndicator(context),
|
||||
isScrollable: true,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
controller: _controller,
|
||||
children: <Widget>[
|
||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
||||
Key('tab0'), _RecentUpdate()),
|
||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
||||
Key('tab1'), _MyFavorite()),
|
||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
||||
Key('tab2'), _MyDownload()),
|
||||
tabs: <Widget>[
|
||||
Tab(
|
||||
child: Text(s.homeTabMenuRecent),
|
||||
),
|
||||
Tab(
|
||||
child: Text(s.homeTabMenuFavotite),
|
||||
),
|
||||
Tab(
|
||||
child: Text(s.download),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Selector<AudioPlayerNotifier, bool>(
|
||||
selector: (_, audio) => audio.playerRunning,
|
||||
builder: (_, data, __) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
|
||||
);
|
||||
},
|
||||
pinned: true,
|
||||
),
|
||||
];
|
||||
},
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _controller,
|
||||
children: <Widget>[
|
||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
||||
Key('tab0'),
|
||||
_RecentUpdate(),
|
||||
),
|
||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
||||
Key('tab1'),
|
||||
_MyFavorite(),
|
||||
),
|
||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
||||
Key('tab2'),
|
||||
_MyDownload(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Selector<AudioPlayerNotifier, bool>(
|
||||
selector: (_, audio) => audio.playerRunning,
|
||||
builder: (_, data, __) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(child: PlayerWidget(playerKey: _playerKey)),
|
||||
Container(
|
||||
child: PlayerWidget(playerKey: _playerKey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -345,6 +352,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
|||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 1,
|
||||
icon: Icon(Icons.playlist_play),
|
||||
color: context.priamryContainer,
|
||||
tooltip: s.menu,
|
||||
itemBuilder: (context) => [
|
||||
MyPopupMenuItem(
|
||||
|
@ -1219,83 +1227,82 @@ class _MyDownloadState extends State<_MyDownload>
|
|||
final s = context.s;
|
||||
return Consumer<DownloadState>(
|
||||
builder: (_, data, __) => FutureBuilder<List<EpisodeBrief>>(
|
||||
future: _getDownloadedEpisodes(_sortBy, hideListened: _hideListened),
|
||||
builder: (context, snapshot) {
|
||||
var episodes = snapshot.data ?? [];
|
||||
return ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: CustomScrollView(
|
||||
key: PageStorageKey<String>('download_list'),
|
||||
slivers: <Widget>[
|
||||
DownloadList(),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 40,
|
||||
color: context.primaryColor,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Text(s.downloaded)),
|
||||
Spacer(),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
icon: SizedBox(
|
||||
width: 30,
|
||||
height: 15,
|
||||
child: HideListened(
|
||||
hideListened: _hideListened ?? false,
|
||||
),
|
||||
future: _getDownloadedEpisodes(_sortBy, hideListened: _hideListened),
|
||||
builder: (context, snapshot) {
|
||||
var episodes = snapshot.data ?? [];
|
||||
return ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: CustomScrollView(
|
||||
key: PageStorageKey<String>('download_list'),
|
||||
slivers: <Widget>[
|
||||
DownloadList(),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 40,
|
||||
color: context.primaryColor,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Text(s.downloaded)),
|
||||
Spacer(),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
icon: SizedBox(
|
||||
width: 30,
|
||||
height: 15,
|
||||
child: HideListened(
|
||||
hideListened: _hideListened ?? false,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => _hideListened = !_hideListened!);
|
||||
},
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: LayoutButton(
|
||||
layout: _layout ?? Layout.one,
|
||||
onPressed: (layout) => setState(() {
|
||||
_layout = layout;
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
episodes.length == 0
|
||||
? SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 110),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(LineIcons.download,
|
||||
size: 80, color: Colors.grey[500]),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: 10)),
|
||||
Text(
|
||||
s.noEpisodeDownload,
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
)
|
||||
],
|
||||
onPressed: () {
|
||||
setState(() => _hideListened = !_hideListened!);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: EpisodeGrid(
|
||||
episodes: episodes,
|
||||
layout: _layout,
|
||||
openPodcast: true,
|
||||
initNum: 0,
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: LayoutButton(
|
||||
layout: _layout ?? Layout.one,
|
||||
onPressed: (layout) => setState(() {
|
||||
_layout = layout;
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
episodes.length == 0
|
||||
? SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 110),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(LineIcons.download,
|
||||
size: 80, color: Colors.grey[500]),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10)),
|
||||
Text(
|
||||
s.noEpisodeDownload,
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
: EpisodeGrid(
|
||||
episodes: episodes,
|
||||
layout: _layout,
|
||||
openPodcast: true,
|
||||
initNum: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,11 +118,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
);
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
(_groupIndex < groups.length - 1)
|
||||
? _groupIndex++
|
||||
: _groupIndex = 0;
|
||||
});
|
||||
setState(
|
||||
() {
|
||||
(_groupIndex < groups.length - 1)
|
||||
? _groupIndex++
|
||||
: _groupIndex = 0;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,13 +136,13 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 15.0),
|
||||
child: Text(
|
||||
groups[_groupIndex]!.name!,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0),
|
||||
child: Text(
|
||||
groups[_groupIndex]!.name!,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
|
@ -150,11 +152,12 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(
|
||||
page: context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefalt!
|
||||
? PodcastList()
|
||||
: PodcastManage()),
|
||||
page: context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefalt!
|
||||
? PodcastList()
|
||||
: PodcastManage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -431,24 +434,26 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
);
|
||||
|
||||
Widget _updateIndicator(PodcastLocal podcastLocal) => FutureBuilder<int?>(
|
||||
future: _getPodcastUpdateCounts(podcastLocal.id),
|
||||
initialData: 0,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data! > 0
|
||||
? Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 10,
|
||||
width: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
border: Border.all(color: context.primaryColor, width: 2),
|
||||
shape: BoxShape.circle),
|
||||
),
|
||||
)
|
||||
: Center();
|
||||
});
|
||||
future: _getPodcastUpdateCounts(podcastLocal.id),
|
||||
initialData: 0,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data! > 0
|
||||
? Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 10,
|
||||
width: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
border:
|
||||
Border.all(color: context.primaryColor, width: 2),
|
||||
shape: BoxShape.circle),
|
||||
),
|
||||
)
|
||||
: Center();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class PodcastPreview extends StatefulWidget {
|
||||
|
@ -463,12 +468,6 @@ class PodcastPreview extends StatefulWidget {
|
|||
class _PodcastPreviewState extends State<PodcastPreview> {
|
||||
Future? _getRssItem;
|
||||
|
||||
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
||||
final dbHelper = DBHelper();
|
||||
final episodes = await dbHelper.getRssItemTop(podcastLocal.id);
|
||||
return episodes;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -510,10 +509,12 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
|||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(widget.podcastLocal!.title!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.bold, color: c)),
|
||||
child: Text(
|
||||
widget.podcastLocal!.title!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.bold, color: c),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
|
@ -531,6 +532,12 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
||||
final dbHelper = DBHelper();
|
||||
final episodes = await dbHelper.getRssItemTop(podcastLocal.id);
|
||||
return episodes;
|
||||
}
|
||||
}
|
||||
|
||||
class ShowEpisode extends StatelessWidget {
|
||||
|
@ -539,6 +546,365 @@ class ShowEpisode extends StatelessWidget {
|
|||
final DBHelper _dbHelper = DBHelper();
|
||||
ShowEpisode({Key? key, this.episodes, this.podcastLocal}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = context.width;
|
||||
final s = context.s;
|
||||
final audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||
return CustomScrollView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
primary: false,
|
||||
slivers: <Widget>[
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 1.5,
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 8.0,
|
||||
crossAxisSpacing: 6.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final c = podcastLocal!.backgroudColor(context);
|
||||
return Selector<AudioPlayerNotifier,
|
||||
tuple.Tuple3<EpisodeBrief?, List<String>, bool>>(
|
||||
selector: (_, audio) => tuple.Tuple3(
|
||||
audio.episode,
|
||||
audio.queue.episodes.map((e) => e!.enclosureUrl).toList(),
|
||||
audio.playerRunning),
|
||||
builder: (_, data, __) => FutureBuilder<
|
||||
tuple.Tuple5<int, bool, bool, bool, List<int>>>(
|
||||
future: _initData(episodes![index]),
|
||||
initialData: tuple.Tuple5(0, false, false, false, []),
|
||||
builder: (context, snapshot) {
|
||||
final isListened = snapshot.data!.item1;
|
||||
final isLiked = snapshot.data!.item2;
|
||||
final isDownloaded = snapshot.data!.item3;
|
||||
final tapToOpen = snapshot.data!.item4;
|
||||
final menuList = snapshot.data!.item5;
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: FocusedMenuHolder(
|
||||
blurSize: 0.0,
|
||||
menuItemExtent: 45,
|
||||
menuBoxDecoration: BoxDecoration(
|
||||
color: context.priamryContainer,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(15.0),
|
||||
),
|
||||
),
|
||||
duration: Duration(milliseconds: 100),
|
||||
tapMode:
|
||||
tapToOpen ? TapMode.onTap : TapMode.onLongPress,
|
||||
animateMenuItems: false,
|
||||
blurBackgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? Colors.white38
|
||||
: Colors.black38,
|
||||
bottomOffsetHeight: 10,
|
||||
menuOffset: 6,
|
||||
menuItems: <FocusedMenuItem>[
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: Text(data.item1 != episodes![index] ||
|
||||
!data.item3
|
||||
? s.play
|
||||
: s.playing),
|
||||
trailingIcon: Icon(
|
||||
LineIcons.playCircle,
|
||||
color: context.accentColor,
|
||||
),
|
||||
onPressed: () {
|
||||
if (data.item1 != episodes![index] ||
|
||||
!data.item3) {
|
||||
audio.episodeLoad(episodes![index]);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(1))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: data.item2.contains(
|
||||
episodes![index].enclosureUrl)
|
||||
? Text(s.remove)
|
||||
: Text(s.later),
|
||||
trailingIcon: Icon(
|
||||
LineIcons.clock,
|
||||
color: Colors.cyan,
|
||||
),
|
||||
onPressed: () {
|
||||
if (!data.item2.contains(
|
||||
episodes![index].enclosureUrl)) {
|
||||
audio.addToPlaylist(episodes![index]);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastAddPlaylist,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
audio.delFromPlaylist(episodes![index]);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastRemovePlaylist,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(2))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title:
|
||||
isLiked ? Text(s.unlike) : Text(s.like),
|
||||
trailingIcon: Icon(LineIcons.heart,
|
||||
color: Colors.red, size: 21),
|
||||
onPressed: () async {
|
||||
if (isLiked) {
|
||||
await _setUnliked(
|
||||
episodes![index].enclosureUrl);
|
||||
audio.setEpisodeState = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: s.unliked,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
await _saveLiked(
|
||||
episodes![index].enclosureUrl);
|
||||
audio.setEpisodeState = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: s.liked,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(3))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: isListened > 0
|
||||
? Text(s.listened,
|
||||
style: TextStyle(
|
||||
color: context.textColor
|
||||
.withOpacity(0.5)))
|
||||
: Text(
|
||||
s.markListened,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailingIcon: SizedBox(
|
||||
width: 23,
|
||||
height: 23,
|
||||
child: CustomPaint(
|
||||
painter: ListenedAllPainter(Colors.blue,
|
||||
stroke: 1.5)),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (isListened < 1) {
|
||||
await _markListened(episodes![index]);
|
||||
audio.setEpisodeState = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: s.markListened,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(4))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: isDownloaded
|
||||
? Text(s.downloaded,
|
||||
style: TextStyle(
|
||||
color: context.textColor
|
||||
.withOpacity(0.5)))
|
||||
: Text(s.download),
|
||||
trailingIcon: Icon(LineIcons.download,
|
||||
color: Colors.green),
|
||||
onPressed: () {
|
||||
if (!isDownloaded) {
|
||||
_requestDownload(context,
|
||||
episode: episodes![index]);
|
||||
// downloader
|
||||
// .startTask(episodes[index]);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(5))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: Text(s.playNext),
|
||||
trailingIcon: Icon(
|
||||
LineIcons.lightningBolt,
|
||||
color: Colors.amber,
|
||||
),
|
||||
onPressed: () {
|
||||
audio.moveToTop(episodes![index]);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.playNextDes,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
ScaleRoute(
|
||||
page: EpisodeDetail(
|
||||
episodeItem: episodes![index],
|
||||
heroTag: 'scroll',
|
||||
//unique hero tag
|
||||
)),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: podcastLocal!.cardColor(context),
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Hero(
|
||||
tag:
|
||||
'${episodes![index].enclosureUrl}scroll',
|
||||
child: Container(
|
||||
height: width / 18,
|
||||
width: width / 18,
|
||||
child: CircleAvatar(
|
||||
backgroundImage:
|
||||
podcastLocal!.avatarImage,
|
||||
),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Selector<
|
||||
AudioPlayerNotifier,
|
||||
tuple
|
||||
.Tuple2<EpisodeBrief?, bool>>(
|
||||
selector: (_, audio) => tuple.Tuple2(
|
||||
audio.episode,
|
||||
audio.playerRunning),
|
||||
builder: (_, data, __) {
|
||||
return (episodes![index]
|
||||
.enclosureUrl ==
|
||||
data.item1
|
||||
?.enclosureUrl &&
|
||||
data.item2)
|
||||
? Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
margin:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: WaveLoader(
|
||||
color: context
|
||||
.accentColor))
|
||||
: Center();
|
||||
}),
|
||||
episodes![index].isNew == 1
|
||||
? Text(
|
||||
'New',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontStyle: FontStyle.italic),
|
||||
)
|
||||
: Center(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 2.0),
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
episodes![index].title!,
|
||||
style: TextStyle(
|
||||
//fontSize: _width / 32,
|
||||
),
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
episodes![index]
|
||||
.pubDate!
|
||||
.toDate(context),
|
||||
overflow: TextOverflow.visible,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: width / 35,
|
||||
color: c,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
if (episodes![index].duration != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
episodes![index].duration!.toTime,
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
// color: _c,
|
||||
// fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
episodes![index].duration == 0 ||
|
||||
episodes![index]
|
||||
.enclosureLength ==
|
||||
null ||
|
||||
episodes![index]
|
||||
.enclosureLength ==
|
||||
0
|
||||
? Center()
|
||||
: Text(
|
||||
'|',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
),
|
||||
),
|
||||
if (episodes![index].enclosureLength !=
|
||||
null &&
|
||||
episodes![index].enclosureLength != 0)
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'${episodes![index].enclosureLength! ~/ 1000000}MB',
|
||||
style:
|
||||
TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: math.min(episodes!.length, 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<tuple.Tuple5<int, bool, bool, bool, List<int>>> _initData(
|
||||
EpisodeBrief episode) async {
|
||||
final menuList = await _getEpisodeMenu();
|
||||
|
@ -653,380 +1019,6 @@ class ShowEpisode extends StatelessWidget {
|
|||
);
|
||||
return ifUseData;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = context.width;
|
||||
final s = context.s;
|
||||
final audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||
return CustomScrollView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
primary: false,
|
||||
slivers: <Widget>[
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 1.5,
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 6.0,
|
||||
crossAxisSpacing: 6.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final c = podcastLocal!.backgroudColor(context);
|
||||
return Selector<AudioPlayerNotifier,
|
||||
tuple.Tuple3<EpisodeBrief?, List<String>, bool>>(
|
||||
selector: (_, audio) => tuple.Tuple3(
|
||||
audio.episode,
|
||||
audio.queue.episodes.map((e) => e!.enclosureUrl).toList(),
|
||||
audio.playerRunning),
|
||||
builder: (_, data, __) => FutureBuilder<
|
||||
tuple.Tuple5<int, bool, bool, bool, List<int>>>(
|
||||
future: _initData(episodes![index]),
|
||||
initialData: tuple.Tuple5(0, false, false, false, []),
|
||||
builder: (context, snapshot) {
|
||||
final isListened = snapshot.data!.item1;
|
||||
final isLiked = snapshot.data!.item2;
|
||||
final isDownloaded = snapshot.data!.item3;
|
||||
final tapToOpen = snapshot.data!.item4;
|
||||
final menuList = snapshot.data!.item5;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
color: context.background,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: FocusedMenuHolder(
|
||||
blurSize: 0.0,
|
||||
menuItemExtent: 45,
|
||||
menuBoxDecoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(15.0))),
|
||||
duration: Duration(milliseconds: 100),
|
||||
tapMode:
|
||||
tapToOpen ? TapMode.onTap : TapMode.onLongPress,
|
||||
animateMenuItems: false,
|
||||
blurBackgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? Colors.white38
|
||||
: Colors.black38,
|
||||
bottomOffsetHeight: 10,
|
||||
menuOffset: 6,
|
||||
menuItems: <FocusedMenuItem>[
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
title: Text(data.item1 != episodes![index] ||
|
||||
!data.item3
|
||||
? s.play
|
||||
: s.playing),
|
||||
trailingIcon: Icon(
|
||||
LineIcons.playCircle,
|
||||
color: context.accentColor,
|
||||
),
|
||||
onPressed: () {
|
||||
if (data.item1 != episodes![index] ||
|
||||
!data.item3) {
|
||||
audio.episodeLoad(episodes![index]);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(1))
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
title: data.item2.contains(
|
||||
episodes![index].enclosureUrl)
|
||||
? Text(s.remove)
|
||||
: Text(s.later),
|
||||
trailingIcon: Icon(
|
||||
LineIcons.clock,
|
||||
color: Colors.cyan,
|
||||
),
|
||||
onPressed: () {
|
||||
if (!data.item2.contains(
|
||||
episodes![index].enclosureUrl)) {
|
||||
audio.addToPlaylist(episodes![index]);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastAddPlaylist,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
audio.delFromPlaylist(episodes![index]);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastRemovePlaylist,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(2))
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
title:
|
||||
isLiked ? Text(s.unlike) : Text(s.like),
|
||||
trailingIcon: Icon(LineIcons.heart,
|
||||
color: Colors.red, size: 21),
|
||||
onPressed: () async {
|
||||
if (isLiked) {
|
||||
await _setUnliked(
|
||||
episodes![index].enclosureUrl);
|
||||
audio.setEpisodeState = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: s.unliked,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
await _saveLiked(
|
||||
episodes![index].enclosureUrl);
|
||||
audio.setEpisodeState = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: s.liked,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(3))
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
title: isListened > 0
|
||||
? Text(s.listened,
|
||||
style: TextStyle(
|
||||
color: context.textColor
|
||||
.withOpacity(0.5)))
|
||||
: Text(
|
||||
s.markListened,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailingIcon: SizedBox(
|
||||
width: 23,
|
||||
height: 23,
|
||||
child: CustomPaint(
|
||||
painter: ListenedAllPainter(Colors.blue,
|
||||
stroke: 1.5)),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (isListened < 1) {
|
||||
await _markListened(episodes![index]);
|
||||
audio.setEpisodeState = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: s.markListened,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(4))
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
title: isDownloaded
|
||||
? Text(s.downloaded,
|
||||
style: TextStyle(
|
||||
color: context.textColor
|
||||
.withOpacity(0.5)))
|
||||
: Text(s.download),
|
||||
trailingIcon: Icon(LineIcons.download,
|
||||
color: Colors.green),
|
||||
onPressed: () {
|
||||
if (!isDownloaded) {
|
||||
_requestDownload(context,
|
||||
episode: episodes![index]);
|
||||
// downloader
|
||||
// .startTask(episodes[index]);
|
||||
}
|
||||
}),
|
||||
if (menuList.contains(5))
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
title: Text(s.playNext),
|
||||
trailingIcon: Icon(
|
||||
LineIcons.lightningBolt,
|
||||
color: Colors.amber,
|
||||
),
|
||||
onPressed: () {
|
||||
audio.moveToTop(episodes![index]);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.playNextDes,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}),
|
||||
],
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
ScaleRoute(
|
||||
page: EpisodeDetail(
|
||||
episodeItem: episodes![index],
|
||||
heroTag: 'scroll',
|
||||
//unique hero tag
|
||||
)),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Hero(
|
||||
tag:
|
||||
'${episodes![index].enclosureUrl}scroll',
|
||||
child: Container(
|
||||
height: width / 18,
|
||||
width: width / 18,
|
||||
child: CircleAvatar(
|
||||
backgroundImage:
|
||||
podcastLocal!.avatarImage,
|
||||
),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Selector<
|
||||
AudioPlayerNotifier,
|
||||
tuple
|
||||
.Tuple2<EpisodeBrief?, bool>>(
|
||||
selector: (_, audio) => tuple.Tuple2(
|
||||
audio.episode,
|
||||
audio.playerRunning),
|
||||
builder: (_, data, __) {
|
||||
return (episodes![index]
|
||||
.enclosureUrl ==
|
||||
data.item1
|
||||
?.enclosureUrl &&
|
||||
data.item2)
|
||||
? Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
margin:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: WaveLoader(
|
||||
color: context
|
||||
.accentColor))
|
||||
: Center();
|
||||
}),
|
||||
episodes![index].isNew == 1
|
||||
? Text(
|
||||
'New',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontStyle: FontStyle.italic),
|
||||
)
|
||||
: Center(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 2.0),
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
episodes![index].title!,
|
||||
style: TextStyle(
|
||||
//fontSize: _width / 32,
|
||||
),
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
episodes![index]
|
||||
.pubDate!
|
||||
.toDate(context),
|
||||
overflow: TextOverflow.visible,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: width / 35,
|
||||
color: c,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
if (episodes![index].duration != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
episodes![index].duration!.toTime,
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
// color: _c,
|
||||
// fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
episodes![index].duration == 0 ||
|
||||
episodes![index]
|
||||
.enclosureLength ==
|
||||
null ||
|
||||
episodes![index]
|
||||
.enclosureLength ==
|
||||
0
|
||||
? Center()
|
||||
: Text(
|
||||
'|',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
),
|
||||
),
|
||||
if (episodes![index].enclosureLength !=
|
||||
null &&
|
||||
episodes![index].enclosureLength !=
|
||||
0)
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'${episodes![index].enclosureLength! ~/ 1000000}MB',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: math.min(episodes!.length, 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//Circle Indicator
|
||||
|
|
|
@ -673,10 +673,10 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: color,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: context.primaryColor,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
child: WillPopScope(
|
||||
onWillPop: () {
|
||||
|
|
|
@ -33,18 +33,233 @@ class DataBackup extends StatefulWidget {
|
|||
|
||||
class _DataBackupState extends State<DataBackup> {
|
||||
final _gpodder = Gpodder();
|
||||
var _syncing = false;
|
||||
// var _syncing = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: context.iconBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness: context.brightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
title: Text(s.settingsBackup),
|
||||
leading: CustomBackButton(),
|
||||
backgroundColor: context.primaryColor,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.fromLTRB(70, 0, 70, 0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.subscribe,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 70.0, right: 20, top: 10, bottom: 10),
|
||||
child: Text(s.subscribeExportDes),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 70.0, right: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: Colors.green[700]!),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.save,
|
||||
color: Colors.green[700],
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.save,
|
||||
style: TextStyle(color: Colors.green[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
final file = await _exportOmpl(context);
|
||||
await _saveFile(file);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: Colors.blue[700]!),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.share,
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
color: Colors.blue[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.share,
|
||||
style: TextStyle(color: Colors.blue[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportOmpl(context);
|
||||
await _shareFile(file);
|
||||
}),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0),
|
||||
child: Divider(height: 1),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
s.settings,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 70.0, right: 20, top: 10, bottom: 10),
|
||||
child: Text(s.settingsExportDes),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 70.0, right: 10),
|
||||
child: Wrap(children: [
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: Colors.green[700]!),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.save,
|
||||
color: Colors.green[700],
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.save,
|
||||
style: TextStyle(color: Colors.green[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportSetting(context);
|
||||
await _saveFile(file);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: Colors.blue[700]!),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.share,
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
color: Colors.blue[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.share,
|
||||
style: TextStyle(color: Colors.blue[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportSetting(context);
|
||||
await _shareFile(file);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: Colors.red[700]!),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.paperclip,
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
color: Colors.red[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.import,
|
||||
style: TextStyle(color: Colors.red[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
_getFilePath(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0),
|
||||
child: Divider(height: 1),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<File> _exportOmpl(BuildContext context) async {
|
||||
var groups = context.read<GroupList>().groups;
|
||||
var opml = PodcastsBackup(groups).omplBuilder();
|
||||
var tempdir = await getTemporaryDirectory();
|
||||
var now = DateTime.now();
|
||||
var datePlus = now.year.toString() +
|
||||
final groups = context.read<GroupList>().groups;
|
||||
final opml = PodcastsBackup(groups).omplBuilder();
|
||||
final tempdir = await getTemporaryDirectory();
|
||||
final now = DateTime.now();
|
||||
final datePlus = now.year.toString() +
|
||||
now.month.toString() +
|
||||
now.day.toString() +
|
||||
now.second.toString();
|
||||
var file = File(path.join(tempdir.path, 'tsacdop_opml_$datePlus.xml'));
|
||||
final file = File(path.join(tempdir.path, 'tsacdop_opml_$datePlus.xml'));
|
||||
await file.writeAsString(opml.toXmlString());
|
||||
return file;
|
||||
}
|
||||
|
@ -147,27 +362,6 @@ class _DataBackupState extends State<DataBackup> {
|
|||
return await storage.getStringList();
|
||||
}
|
||||
|
||||
Future<void> _syncNow() async {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_syncing = true;
|
||||
});
|
||||
}
|
||||
final gpodder = Gpodder();
|
||||
final status = await gpodder.getChanges();
|
||||
|
||||
if (status == 200) {
|
||||
final groupList = context.read<GroupList>();
|
||||
await gpodder.updateChange();
|
||||
await groupList.gpodderSyncNow();
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_syncing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<int?>> _getSyncStatus() async {
|
||||
var dateTimeStorage = KeyValueStorage(gpodderSyncDateTimeKey);
|
||||
var statusStorage = KeyValueStorage(gpodderSyncStatusKey);
|
||||
|
@ -175,353 +369,6 @@ class _DataBackupState extends State<DataBackup> {
|
|||
final statusIndex = await statusStorage.getInt();
|
||||
return [syncDateTime, statusIndex];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: context.iconBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness: context.brightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
title: Text(s.settingsBackup),
|
||||
leading: CustomBackButton(),
|
||||
backgroundColor: context.primaryColor,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
FutureBuilder<List<String?>?>(
|
||||
future: _getLoginInfo(),
|
||||
initialData: [],
|
||||
builder: (context, snapshot) {
|
||||
final loginInfo = snapshot.data!;
|
||||
return Container(
|
||||
height: 160,
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Hero(
|
||||
tag: 'gpodder.net',
|
||||
child: CircleAvatar(
|
||||
minRadius: 40,
|
||||
backgroundColor: context.primaryColor,
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
width: 60,
|
||||
child: Image.asset('assets/gpodder.png')),
|
||||
),
|
||||
),
|
||||
if (_syncing)
|
||||
Positioned(
|
||||
left: context.width / 2 - 40,
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
width: 80,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_syncing)
|
||||
Positioned(
|
||||
bottom: 39,
|
||||
left: context.width / 2 - 12,
|
||||
child: _OpenEye()),
|
||||
if (_syncing)
|
||||
Positioned(
|
||||
bottom: 39,
|
||||
left: context.width / 2 + 3,
|
||||
child: _OpenEye()),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
loginInfo.isEmpty
|
||||
? s.intergateWith('gpodder.net')
|
||||
: s.loggedInAs(loginInfo.first!),
|
||||
style: TextStyle(color: Colors.purple[700])),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.purple[700]!)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.user,
|
||||
color: Colors.purple[700],
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(loginInfo.isEmpty ? s.login : s.logout,
|
||||
style:
|
||||
TextStyle(color: Colors.purple[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
if (loginInfo.isEmpty) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _LoginGpodder(),
|
||||
fullscreenDialog: true));
|
||||
} else {
|
||||
_logout();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
FutureBuilder<List<String?>?>(
|
||||
future: _getLoginInfo(),
|
||||
initialData: [],
|
||||
builder: (context, snapshot) {
|
||||
final loginInfo = snapshot.data!;
|
||||
if (loginInfo.isNotEmpty) {
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 70.0, right: 20, top: 10, bottom: 10),
|
||||
onTap: _syncNow,
|
||||
title: Text(s.syncNow),
|
||||
trailing: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _GpodderInfo()));
|
||||
},
|
||||
icon: Icon(LineIcons.infoCircle),
|
||||
),
|
||||
subtitle: FutureBuilder<List<int?>>(
|
||||
future: _getSyncStatus(),
|
||||
initialData: [0, 0],
|
||||
builder: (context, snapshot) {
|
||||
final dateTime = snapshot.data![0]!;
|
||||
final status = snapshot.data![1];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${s.lastUpdate}: ${dateTime.toDate(context)}'),
|
||||
SizedBox(width: 8),
|
||||
Row(
|
||||
children: [
|
||||
Text('${s.status}: '),
|
||||
_syncStauts(status),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
return Center();
|
||||
}),
|
||||
// ListTile(
|
||||
// onTap: () async {
|
||||
// final subscribeWorker = context.read<GroupList>();
|
||||
// await subscribeWorker.cancelWork();
|
||||
// subscribeWorker.setWorkManager();
|
||||
// },
|
||||
// title: Text('reset'),
|
||||
// ),
|
||||
Divider(height: 1),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.fromLTRB(70, 0, 70, 0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.subscribe,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 70.0, right: 20, top: 10, bottom: 10),
|
||||
child: Text(s.subscribeExportDes),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 70.0, right: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.green[700]!)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.save,
|
||||
color: Colors.green[700],
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.save,
|
||||
style: TextStyle(color: Colors.green[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportOmpl(context);
|
||||
await _saveFile(file);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.blue[700]!)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.share,
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
color: Colors.blue[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.share,
|
||||
style: TextStyle(color: Colors.blue[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportOmpl(context);
|
||||
await _shareFile(file);
|
||||
}),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settings,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 70.0, right: 20, top: 10, bottom: 10),
|
||||
child: Text(s.settingsExportDes),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 70.0, right: 10),
|
||||
child: Wrap(children: [
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.green[700]!)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.save,
|
||||
color: Colors.green[700],
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.save,
|
||||
style: TextStyle(color: Colors.green[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportSetting(context);
|
||||
await _saveFile(file);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.blue[700]!)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.share,
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
color: Colors.blue[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.share,
|
||||
style: TextStyle(color: Colors.blue[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
var file = await _exportSetting(context);
|
||||
await _shareFile(file);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ButtonTheme(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.red[700]!)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.paperclip,
|
||||
size: context.textTheme.headline6!.fontSize,
|
||||
color: Colors.red[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.import,
|
||||
style: TextStyle(color: Colors.red[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
_getFilePath(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
Divider(height: 1)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OpenEye extends StatefulWidget {
|
||||
|
@ -710,7 +557,6 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
|||
context.s.settingsSyncing,
|
||||
style: TextStyle(color: Colors.white),
|
||||
);
|
||||
break;
|
||||
case LoginStatus.start:
|
||||
return SizedBox(
|
||||
height: 20,
|
||||
|
@ -725,7 +571,6 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
|||
context.s.login,
|
||||
style: TextStyle(color: Colors.white),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,86 +25,6 @@ class PlayedHistory extends StatefulWidget {
|
|||
|
||||
class _PlayedHistoryState extends State<PlayedHistory>
|
||||
with SingleTickerProviderStateMixin {
|
||||
/// Get play history.
|
||||
Future<List<PlayHistory>> _getPlayHistory(int top) async {
|
||||
var dbHelper = DBHelper();
|
||||
List<PlayHistory> playHistory;
|
||||
playHistory = await dbHelper.getPlayHistory(top);
|
||||
for (var record in playHistory) {
|
||||
await record.getEpisode();
|
||||
}
|
||||
return playHistory;
|
||||
}
|
||||
|
||||
bool _loadMore = false;
|
||||
|
||||
Future<void> _loadMoreData() async {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadMore = true;
|
||||
});
|
||||
}
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_top = _top + 10;
|
||||
_loadMore = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int _top = 10;
|
||||
|
||||
Future<List<SubHistory>> getSubHistory() async {
|
||||
var dbHelper = DBHelper();
|
||||
return await dbHelper.getSubHistory();
|
||||
}
|
||||
|
||||
TabController? _controller;
|
||||
List<int> list = const [0, 1, 2, 3, 4, 5, 6];
|
||||
|
||||
Future<List<FlSpot>> getData() async {
|
||||
var dbHelper = DBHelper();
|
||||
var stats = <FlSpot>[];
|
||||
|
||||
for (var day in list) {
|
||||
var mins = await dbHelper.listenMins(7 - day);
|
||||
stats.add(FlSpot(day.toDouble(), mins));
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
Future recoverSub(BuildContext context, String url) async {
|
||||
Fluttertoast.showToast(
|
||||
msg: context.s.toastPodcastRecovering,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
var subscribeWorker = context.watch<GroupList>();
|
||||
try {
|
||||
var options = BaseOptions(
|
||||
connectTimeout: 10000,
|
||||
receiveTimeout: 10000,
|
||||
);
|
||||
var response = await Dio(options).get(url);
|
||||
var p = RssFeed.parse(response.data);
|
||||
var podcast = OnlinePodcast(
|
||||
rss: url,
|
||||
title: p.title,
|
||||
publisher: p.author,
|
||||
description: p.description,
|
||||
image: p.itunes!.image!.href);
|
||||
var item = SubscribeItem(podcast.rss, podcast.title,
|
||||
imgUrl: podcast.image, group: 'Home');
|
||||
subscribeWorker.setSubscribeItem(item);
|
||||
} catch (e) {
|
||||
developer.log(e.toString(), name: 'Recover podcast error');
|
||||
Fluttertoast.showToast(
|
||||
msg: context.s.toastRecoverFailed,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -122,13 +42,9 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
|||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: context.brightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.primaryColor,
|
||||
backgroundColor: context.onPrimary,
|
||||
body: SafeArea(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||
|
@ -389,6 +305,86 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Get play history.
|
||||
Future<List<PlayHistory>> _getPlayHistory(int top) async {
|
||||
var dbHelper = DBHelper();
|
||||
List<PlayHistory> playHistory;
|
||||
playHistory = await dbHelper.getPlayHistory(top);
|
||||
for (var record in playHistory) {
|
||||
await record.getEpisode();
|
||||
}
|
||||
return playHistory;
|
||||
}
|
||||
|
||||
bool _loadMore = false;
|
||||
|
||||
Future<void> _loadMoreData() async {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadMore = true;
|
||||
});
|
||||
}
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_top = _top + 10;
|
||||
_loadMore = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int _top = 10;
|
||||
|
||||
Future<List<SubHistory>> getSubHistory() async {
|
||||
var dbHelper = DBHelper();
|
||||
return await dbHelper.getSubHistory();
|
||||
}
|
||||
|
||||
TabController? _controller;
|
||||
List<int> list = const [0, 1, 2, 3, 4, 5, 6];
|
||||
|
||||
Future<List<FlSpot>> getData() async {
|
||||
var dbHelper = DBHelper();
|
||||
var stats = <FlSpot>[];
|
||||
|
||||
for (var day in list) {
|
||||
var mins = await dbHelper.listenMins(7 - day);
|
||||
stats.add(FlSpot(day.toDouble(), mins));
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
Future recoverSub(BuildContext context, String url) async {
|
||||
Fluttertoast.showToast(
|
||||
msg: context.s.toastPodcastRecovering,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
var subscribeWorker = context.watch<GroupList>();
|
||||
try {
|
||||
var options = BaseOptions(
|
||||
connectTimeout: 10000,
|
||||
receiveTimeout: 10000,
|
||||
);
|
||||
var response = await Dio(options).get(url);
|
||||
var p = RssFeed.parse(response.data);
|
||||
var podcast = OnlinePodcast(
|
||||
rss: url,
|
||||
title: p.title,
|
||||
publisher: p.author,
|
||||
description: p.description,
|
||||
image: p.itunes!.image!.href);
|
||||
var item = SubscribeItem(podcast.rss, podcast.title,
|
||||
imgUrl: podcast.image, group: 'Home');
|
||||
subscribeWorker.setSubscribeItem(item);
|
||||
} catch (e) {
|
||||
developer.log(e.toString(), name: 'Recover podcast error');
|
||||
Fluttertoast.showToast(
|
||||
msg: context.s.toastRecoverFailed,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
|
|
|
@ -21,6 +21,246 @@ class LayoutSetting extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _LayoutSettingState extends State<LayoutSetting> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingsLayout),
|
||||
leading: CustomBackButton(),
|
||||
elevation: 0,
|
||||
backgroundColor: context.primaryColor,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsPopupMenu,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PopupMenuSetting())),
|
||||
contentPadding: EdgeInsets.only(left: 70.0, right: 20),
|
||||
title: Text(s.settingsPopupMenu),
|
||||
subtitle: Text(s.settingsPopupMenuDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
s.player,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
title: Text(s.settingsPlayerHeight),
|
||||
subtitle: Text(s.settingsPlayerHeightDes),
|
||||
trailing: Selector<AudioPlayerNotifier, PlayerHeight?>(
|
||||
selector: (_, audio) => audio.playerHeight,
|
||||
builder: (_, data, __) => MyDropdownButton(
|
||||
hint: Text(_getHeightString(data)),
|
||||
underline: Center(),
|
||||
elevation: 1,
|
||||
value: data!.index,
|
||||
items: <int>[0, 1, 2].map<DropdownMenuItem<int>>((e) {
|
||||
return DropdownMenuItem<int>(
|
||||
value: e,
|
||||
child: Text(
|
||||
_getHeightString(PlayerHeight.values[e])));
|
||||
}).toList(),
|
||||
onChanged: (dynamic index) =>
|
||||
audio.setPlayerHeight = PlayerHeight.values[index]),
|
||||
),
|
||||
),
|
||||
if (environment['apiKey'] != '') Divider(height: 1),
|
||||
if (environment['apiKey'] != '')
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
if (environment['apiKey'] != '')
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.search,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
if (environment['apiKey'] != '')
|
||||
FutureBuilder<bool>(
|
||||
future: _getHideDiscovery(),
|
||||
initialData: false,
|
||||
builder: (context, snapshot) => ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
onTap: () => _saveHideDiscovery(!snapshot.data!),
|
||||
title: Text(s.hidePodcastDiscovery),
|
||||
subtitle: Text(s.hidePodcastDiscoveryDes),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: snapshot.data!,
|
||||
onChanged: _saveHideDiscovery),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (environment['apiKey'] != '')
|
||||
FutureBuilder(
|
||||
future: _getSearchEngine(),
|
||||
initialData: SearchEngine.listenNotes,
|
||||
builder: (context, snapshot) => ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
title: Text(s.defaultSearchEngine),
|
||||
subtitle: Text(s.defaultSearchEngineDes),
|
||||
trailing: MyDropdownButton(
|
||||
hint: Text(''),
|
||||
underline: Center(),
|
||||
elevation: 1,
|
||||
value: snapshot.data,
|
||||
items: [
|
||||
DropdownMenuItem<SearchEngine>(
|
||||
value: SearchEngine.podcastIndex,
|
||||
child: Text('Podcastindex')),
|
||||
DropdownMenuItem<SearchEngine>(
|
||||
value: SearchEngine.listenNotes,
|
||||
child: Text('ListenNotes')),
|
||||
],
|
||||
onChanged: (dynamic value) =>
|
||||
_saveSearchEngine(value)),
|
||||
),
|
||||
),
|
||||
Divider(height: 1),
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text('Default page',
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openPlaylistDefault,
|
||||
builder: (_, data, __) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
onTap: () => context
|
||||
.read<SettingState>()
|
||||
.openPlaylistDefault = !data!,
|
||||
title: Text('Open playlist page by default'),
|
||||
subtitle: Text(
|
||||
'Open playlist page instead of homepage by default'),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: data!,
|
||||
onChanged: (boo) => context
|
||||
.read<SettingState>()
|
||||
.openPlaylistDefault = boo),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openAllPodcastDefalt,
|
||||
builder: (_, data, __) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
onTap: () => context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefault = !data!,
|
||||
title: Text('Open all podcasts page by default'),
|
||||
subtitle: Text(
|
||||
'Open all podcasts page instead of group page by default'),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: data!,
|
||||
onChanged: (boo) => context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefault = boo),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Divider(height: 1),
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
s.settingsDefaultGrid,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
),
|
||||
),
|
||||
ListView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.vertical,
|
||||
children: <Widget>[
|
||||
FutureBuilder<bool>(
|
||||
future: _hideListened(),
|
||||
initialData: false,
|
||||
builder: (context, snapshot) => ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 70, right: 10),
|
||||
onTap: () => _saveHideListened(!snapshot.data!),
|
||||
title: Text('Hide listened'),
|
||||
subtitle: Text('Hide listened episodes by default'),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: snapshot.data!,
|
||||
onChanged: _saveHideListened),
|
||||
),
|
||||
),
|
||||
),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridPodcast,
|
||||
key: podcastLayoutKey),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridRecent,
|
||||
key: recentLayoutKey),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridFavorite,
|
||||
key: favLayoutKey),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridDownload,
|
||||
key: downloadLayoutKey),
|
||||
]),
|
||||
Divider(height: 1),
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
final _hideDiscoveyStorage = KeyValueStorage(hidePodcastDiscoveryKey);
|
||||
Future<Layout> _getLayout(String key) async {
|
||||
final keyValueStorage = KeyValueStorage(key);
|
||||
|
@ -38,8 +278,8 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||
}
|
||||
|
||||
Future<bool> _hideListened() async {
|
||||
var hideListenedStorage = KeyValueStorage(hideListenedKey);
|
||||
var hideListened = await hideListenedStorage.getBool(defaultValue: false);
|
||||
final hideListenedStorage = KeyValueStorage(hideListenedKey);
|
||||
final hideListened = await hideListenedStorage.getBool(defaultValue: false);
|
||||
return hideListened;
|
||||
}
|
||||
|
||||
|
@ -181,244 +421,4 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: context.primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingsLayout),
|
||||
leading: CustomBackButton(),
|
||||
elevation: 0,
|
||||
backgroundColor: context.primaryColor,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsPopupMenu,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PopupMenuSetting())),
|
||||
contentPadding: EdgeInsets.only(left: 70.0, right: 20),
|
||||
title: Text(s.settingsPopupMenu),
|
||||
subtitle: Text(s.settingsPopupMenuDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.player,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
title: Text(s.settingsPlayerHeight),
|
||||
subtitle: Text(s.settingsPlayerHeightDes),
|
||||
trailing: Selector<AudioPlayerNotifier, PlayerHeight?>(
|
||||
selector: (_, audio) => audio.playerHeight,
|
||||
builder: (_, data, __) => MyDropdownButton(
|
||||
hint: Text(_getHeightString(data)),
|
||||
underline: Center(),
|
||||
elevation: 1,
|
||||
value: data!.index,
|
||||
items: <int>[0, 1, 2].map<DropdownMenuItem<int>>((e) {
|
||||
return DropdownMenuItem<int>(
|
||||
value: e,
|
||||
child: Text(
|
||||
_getHeightString(PlayerHeight.values[e])));
|
||||
}).toList(),
|
||||
onChanged: (dynamic index) =>
|
||||
audio.setPlayerHeight = PlayerHeight.values[index]),
|
||||
),
|
||||
),
|
||||
if (environment['apiKey'] != '') Divider(height: 1),
|
||||
if (environment['apiKey'] != '')
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
if (environment['apiKey'] != '')
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.search,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
if (environment['apiKey'] != '')
|
||||
FutureBuilder<bool>(
|
||||
future: _getHideDiscovery(),
|
||||
initialData: false,
|
||||
builder: (context, snapshot) => ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
onTap: () => _saveHideDiscovery(!snapshot.data!),
|
||||
title: Text(s.hidePodcastDiscovery),
|
||||
subtitle: Text(s.hidePodcastDiscoveryDes),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: snapshot.data!,
|
||||
onChanged: _saveHideDiscovery),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (environment['apiKey'] != '')
|
||||
FutureBuilder(
|
||||
future: _getSearchEngine(),
|
||||
initialData: SearchEngine.listenNotes,
|
||||
builder: (context, snapshot) => ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
title: Text(s.defaultSearchEngine),
|
||||
subtitle: Text(s.defaultSearchEngineDes),
|
||||
trailing: MyDropdownButton(
|
||||
hint: Text(''),
|
||||
underline: Center(),
|
||||
elevation: 1,
|
||||
value: snapshot.data,
|
||||
items: [
|
||||
DropdownMenuItem<SearchEngine>(
|
||||
value: SearchEngine.podcastIndex,
|
||||
child: Text('Podcastindex')),
|
||||
DropdownMenuItem<SearchEngine>(
|
||||
value: SearchEngine.listenNotes,
|
||||
child: Text('ListenNotes')),
|
||||
],
|
||||
onChanged: (dynamic value) =>
|
||||
_saveSearchEngine(value)),
|
||||
),
|
||||
),
|
||||
Divider(height: 1),
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text('Default page',
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openPlaylistDefault,
|
||||
builder: (_, data, __) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
onTap: () => context
|
||||
.read<SettingState>()
|
||||
.openPlaylistDefault = !data!,
|
||||
title: Text('Open playlist page by default'),
|
||||
subtitle: Text(
|
||||
'Open playlist page instead of homepage by default'),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: data!,
|
||||
onChanged: (boo) => context
|
||||
.read<SettingState>()
|
||||
.openPlaylistDefault = boo),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openAllPodcastDefalt,
|
||||
builder: (_, data, __) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.fromLTRB(70, 10, 10, 10),
|
||||
onTap: () => context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefault = !data!,
|
||||
title: Text('Open all podcasts page by default'),
|
||||
subtitle: Text(
|
||||
'Open all podcasts page instead of group page by default'),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: data!,
|
||||
onChanged: (boo) => context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefault = boo),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Divider(height: 1),
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsDefaultGrid,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
),
|
||||
ListView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.vertical,
|
||||
children: <Widget>[
|
||||
FutureBuilder<bool>(
|
||||
future: _hideListened(),
|
||||
initialData: false,
|
||||
builder: (context, snapshot) => ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 70, right: 10),
|
||||
onTap: () => _saveHideListened(!snapshot.data!),
|
||||
title: Text('Hide listened'),
|
||||
subtitle: Text('Hide listened episodes by default'),
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Switch(
|
||||
value: snapshot.data!,
|
||||
onChanged: _saveHideListened),
|
||||
),
|
||||
),
|
||||
),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridPodcast,
|
||||
key: podcastLayoutKey),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridRecent,
|
||||
key: recentLayoutKey),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridFavorite,
|
||||
key: favLayoutKey),
|
||||
_setDefaultGridView(context,
|
||||
text: s.settingsDefaultGridDownload,
|
||||
key: downloadLayoutKey),
|
||||
]),
|
||||
Divider(height: 1),
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../util/extension_helper.dart';
|
||||
import '../widgets/custom_widget.dart';
|
||||
import 'licenses.dart';
|
||||
|
||||
class Libries extends StatelessWidget {
|
||||
_launchUrl(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.s.settingsLibraries),
|
||||
|
@ -50,14 +36,14 @@ class Libries extends StatelessWidget {
|
|||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Column(
|
||||
children: google.map<Widget>(
|
||||
(e) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 80),
|
||||
onTap: () => _launchUrl(e.link),
|
||||
onTap: () => e.link.launchUrl,
|
||||
title: Text(e.name),
|
||||
subtitle: Text(e.license),
|
||||
);
|
||||
|
@ -72,14 +58,14 @@ class Libries extends StatelessWidget {
|
|||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Column(
|
||||
children: fonts.map<Widget>(
|
||||
(e) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 80),
|
||||
onTap: () => _launchUrl(e.link),
|
||||
onTap: () => e.link.launchUrl,
|
||||
title: Text(e.name),
|
||||
subtitle: Text(e.license),
|
||||
);
|
||||
|
@ -94,14 +80,14 @@ class Libries extends StatelessWidget {
|
|||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
Container(
|
||||
child: Column(
|
||||
children: plugins.map<Widget>(
|
||||
(e) {
|
||||
return ListTile(
|
||||
onTap: () => _launchUrl(e.link),
|
||||
onTap: () => e.link.launchUrl,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 80),
|
||||
title: Text(e.name),
|
||||
|
|
|
@ -38,198 +38,15 @@ class PlaySetting extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _PlaySettingState extends State<PlaySetting> {
|
||||
String _volumeEffect(BuildContext context, int? i) {
|
||||
final s = context.s;
|
||||
if (i == 2000) {
|
||||
return s.playerHeightShort;
|
||||
} else if (i == 3000) {
|
||||
return s.playerHeightMed;
|
||||
}
|
||||
return s.playerHeightTall;
|
||||
}
|
||||
|
||||
Future<bool> _getMarkListenedSkip() async {
|
||||
final storage = KeyValueStorage(markListenedAfterSkipKey);
|
||||
return storage.getBool(defaultValue: false);
|
||||
}
|
||||
|
||||
Future<void> _saveMarkListenedSkip(bool boo) async {
|
||||
final storage = KeyValueStorage(markListenedAfterSkipKey);
|
||||
await storage.saveBool(boo);
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Widget _modeWidget(BuildContext context) {
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
return Selector<SettingState, Tuple2<int?, int?>>(
|
||||
selector: (_, settings) =>
|
||||
Tuple2(settings.autoSleepTimerMode, settings.defaultSleepTimer),
|
||||
builder: (_, data, __) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => settings.setAutoSleepTimerMode = 0,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
decoration: BoxDecoration(
|
||||
color: data.item1 == 0
|
||||
? context.accentColor
|
||||
: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5),
|
||||
topLeft: Radius.circular(5)),
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(context.s.endOfEpisode,
|
||||
style: TextStyle(
|
||||
color: data.item1 == 0 ? Colors.white : null)),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => settings.setAutoSleepTimerMode = 1,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
decoration: BoxDecoration(
|
||||
color: data.item1 == 1
|
||||
? context.accentColor
|
||||
: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5)),
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(context.s.minsCount(data.item2!),
|
||||
style: TextStyle(
|
||||
color: data.item1 == 1 ? Colors.white : null)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _scheduleWidget(BuildContext context) {
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
final s = context.s;
|
||||
return Selector<SettingState, Tuple2<int?, int?>>(
|
||||
selector: (_, settings) =>
|
||||
Tuple2(settings.autoSleepTimerStart, settings.autoSleepTimerEnd),
|
||||
builder: (_, data, __) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
var startTime = data.item1!;
|
||||
final timeOfDay = await showCustomTimePicker(
|
||||
context: context,
|
||||
cancelText: s.cancel,
|
||||
confirmText: s.confirm,
|
||||
helpText: '',
|
||||
initialTime: TimeOfDay(
|
||||
hour: startTime ~/ 60, minute: startTime % 60));
|
||||
if (timeOfDay != null) {
|
||||
startTime = timeOfDay.hour * 60 + timeOfDay.minute;
|
||||
if (startTime != data.item2) {
|
||||
settings.setAutoSleepTimerStart = startTime;
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastTimeEqualEnd,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5),
|
||||
topLeft: Radius.circular(5)),
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(s.from(data.item1!.toTime)),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
var endTime = data.item2!;
|
||||
final timeOfDay = await showCustomTimePicker(
|
||||
context: context,
|
||||
cancelText: s.cancel,
|
||||
confirmText: s.confirm,
|
||||
helpText: '',
|
||||
initialTime:
|
||||
TimeOfDay(hour: endTime ~/ 60, minute: endTime % 60));
|
||||
if (timeOfDay != null) {
|
||||
endTime = timeOfDay.hour * 60 + timeOfDay.minute;
|
||||
if (endTime != data.item1) {
|
||||
settings.setAutoSleepTimerEnd = endTime;
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastTimeEqualStart,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5))),
|
||||
child: Text(s.to(data.item2!.toTime),
|
||||
style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var settings = context.watch<SettingState>();
|
||||
var audio = context.watch<AudioPlayerNotifier>();
|
||||
final settings = context.watch<SettingState>();
|
||||
final audio = context.watch<AudioPlayerNotifier>();
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: context.brightness,
|
||||
systemNavigationBarColor: context.primaryColor,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
title: Text(s.play),
|
||||
leading: CustomBackButton(),
|
||||
|
@ -463,6 +280,186 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _volumeEffect(BuildContext context, int? i) {
|
||||
final s = context.s;
|
||||
if (i == 2000) {
|
||||
return s.playerHeightShort;
|
||||
} else if (i == 3000) {
|
||||
return s.playerHeightMed;
|
||||
}
|
||||
return s.playerHeightTall;
|
||||
}
|
||||
|
||||
Future<bool> _getMarkListenedSkip() async {
|
||||
final storage = KeyValueStorage(markListenedAfterSkipKey);
|
||||
return storage.getBool(defaultValue: false);
|
||||
}
|
||||
|
||||
Future<void> _saveMarkListenedSkip(bool boo) async {
|
||||
final storage = KeyValueStorage(markListenedAfterSkipKey);
|
||||
await storage.saveBool(boo);
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Widget _modeWidget(BuildContext context) {
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
return Selector<SettingState, Tuple2<int?, int?>>(
|
||||
selector: (_, settings) =>
|
||||
Tuple2(settings.autoSleepTimerMode, settings.defaultSleepTimer),
|
||||
builder: (_, data, __) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => settings.setAutoSleepTimerMode = 0,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
decoration: BoxDecoration(
|
||||
color: data.item1 == 0
|
||||
? context.accentColor
|
||||
: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5),
|
||||
topLeft: Radius.circular(5)),
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(context.s.endOfEpisode,
|
||||
style: TextStyle(
|
||||
color: data.item1 == 0 ? Colors.white : null)),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => settings.setAutoSleepTimerMode = 1,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
decoration: BoxDecoration(
|
||||
color: data.item1 == 1
|
||||
? context.accentColor
|
||||
: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5)),
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(context.s.minsCount(data.item2!),
|
||||
style: TextStyle(
|
||||
color: data.item1 == 1 ? Colors.white : null)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _scheduleWidget(BuildContext context) {
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
final s = context.s;
|
||||
return Selector<SettingState, Tuple2<int?, int?>>(
|
||||
selector: (_, settings) =>
|
||||
Tuple2(settings.autoSleepTimerStart, settings.autoSleepTimerEnd),
|
||||
builder: (_, data, __) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
var startTime = data.item1!;
|
||||
final timeOfDay = await showCustomTimePicker(
|
||||
context: context,
|
||||
cancelText: s.cancel,
|
||||
confirmText: s.confirm,
|
||||
helpText: '',
|
||||
initialTime: TimeOfDay(
|
||||
hour: startTime ~/ 60, minute: startTime % 60));
|
||||
if (timeOfDay != null) {
|
||||
startTime = timeOfDay.hour * 60 + timeOfDay.minute;
|
||||
if (startTime != data.item2) {
|
||||
settings.setAutoSleepTimerStart = startTime;
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastTimeEqualEnd,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(5),
|
||||
topLeft: Radius.circular(5)),
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(s.from(data.item1!.toTime)),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
var endTime = data.item2!;
|
||||
final timeOfDay = await showCustomTimePicker(
|
||||
context: context,
|
||||
cancelText: s.cancel,
|
||||
confirmText: s.confirm,
|
||||
helpText: '',
|
||||
initialTime:
|
||||
TimeOfDay(hour: endTime ~/ 60, minute: endTime % 60));
|
||||
if (timeOfDay != null) {
|
||||
endTime = timeOfDay.hour * 60 + timeOfDay.minute;
|
||||
if (endTime != data.item1) {
|
||||
settings.setAutoSleepTimerEnd = endTime;
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastTimeEqualStart,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(5),
|
||||
topRight: Radius.circular(5))),
|
||||
child: Text(s.to(data.item2!.toTime),
|
||||
style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NotificationLayout extends StatefulWidget {
|
||||
|
|
|
@ -71,208 +71,201 @@ class _SettingsState extends State<Settings> {
|
|||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
statusBarIconBrightness: context.brightness,
|
||||
systemNavigationBarColor: context.onPrimary,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settings),
|
||||
leading: CustomBackButton(),
|
||||
elevation: _showTitle ? 1 : 0,
|
||||
backgroundColor: context.primaryColor,
|
||||
backgroundColor: context.onPrimary,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: _controller,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsPrefrence,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => ThemeSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.adjust, color: context.accentColor),
|
||||
title: Text(s.settingsAppearance),
|
||||
subtitle: Text(s.settingsAppearanceDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => LayoutSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.stopCircle, color: Colors.blueAccent),
|
||||
title: Text(s.settingsLayout),
|
||||
subtitle: Text(s.settingsLayoutDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PlaySetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.playCircle, color: Colors.redAccent),
|
||||
title: Text(s.play),
|
||||
subtitle: Text(s.settingsPlayDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SyncingSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.alternateCloudDownload,
|
||||
color: Colors.yellow[700]),
|
||||
title: Text(s.settingsSyncing),
|
||||
subtitle: Text(s.settingsSyncingDes)),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
body: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: _controller,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsPrefrence,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => ThemeSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.adjust, color: context.accentColor),
|
||||
title: Text(s.settingsAppearance),
|
||||
subtitle: Text(s.settingsAppearanceDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => LayoutSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.stopCircle, color: Colors.blueAccent),
|
||||
title: Text(s.settingsLayout),
|
||||
subtitle: Text(s.settingsLayoutDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PlaySetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.playCircle, color: Colors.redAccent),
|
||||
title: Text(s.play),
|
||||
subtitle: Text(s.settingsPlayDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StorageSetting())),
|
||||
builder: (context) => SyncingSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.save, color: Colors.green[700]),
|
||||
title: Text(s.settingStorage),
|
||||
subtitle: Text(s.settingsStorageDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PlayedHistory())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(Icons.update, color: Colors.indigo[700]),
|
||||
title: Text(s.settingsHistory),
|
||||
subtitle: Text(s.settingsHistoryDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => generalSheet(context,
|
||||
title: s.settingsLanguages, child: LanguagesSetting())
|
||||
.then((value) => setState(() {})),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.language, color: Colors.purpleAccent),
|
||||
title: Text(s.settingsLanguages),
|
||||
subtitle: Text(s.settingsLanguagesDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
//_exportOmpl(context);
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => DataBackup()));
|
||||
},
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading:
|
||||
Icon(LineIcons.codeFile, color: Colors.lightGreen[700]),
|
||||
title: Text(s.settingsBackup),
|
||||
subtitle: Text(s.settingsBackupDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsInfo,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => Libries())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.bookOpen, color: Colors.purple[700]),
|
||||
title: Text(s.settingsLibraries),
|
||||
subtitle: Text(s.settingsLibrariesDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => generalSheet(
|
||||
context,
|
||||
title: s.settingsFeedback,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_feedbackItem(LineIcons.github, s.feedbackGithub,
|
||||
'https://github.com/stonega/tsacdop/issues'),
|
||||
_feedbackItem(LineIcons.telegram, s.feedbackTelegram,
|
||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||
_feedbackItem(
|
||||
LineIcons.envelopeOpenText,
|
||||
s.feedbackEmail,
|
||||
'mailto:<tsacdop.app@gmail.com>?subject=Tsacdop Feedback'),
|
||||
_feedbackItem(LineIcons.googlePlay, s.feedbackPlay,
|
||||
'https://play.google.com/store/apps/details?id=com.stonegate.tsacdop'),
|
||||
],
|
||||
),
|
||||
leading: Icon(LineIcons.alternateCloudDownload,
|
||||
color: Colors.yellow[700]),
|
||||
title: Text(s.settingsSyncing),
|
||||
subtitle: Text(s.settingsSyncingDes)),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => StorageSetting())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.save, color: Colors.green[700]),
|
||||
title: Text(s.settingStorage),
|
||||
subtitle: Text(s.settingsStorageDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PlayedHistory())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(Icons.update, color: Colors.indigo[700]),
|
||||
title: Text(s.settingsHistory),
|
||||
subtitle: Text(s.settingsHistoryDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => generalSheet(context,
|
||||
title: s.settingsLanguages, child: LanguagesSetting())
|
||||
.then((value) => setState(() {})),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.language, color: Colors.purpleAccent),
|
||||
title: Text(s.settingsLanguages),
|
||||
subtitle: Text(s.settingsLanguagesDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
//_exportOmpl(context);
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => DataBackup()));
|
||||
},
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading:
|
||||
Icon(LineIcons.codeFile, color: Colors.lightGreen[700]),
|
||||
title: Text(s.settingsBackup),
|
||||
subtitle: Text(s.settingsBackupDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsInfo,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => Libries())),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.bookOpen, color: Colors.purple[700]),
|
||||
title: Text(s.settingsLibraries),
|
||||
subtitle: Text(s.settingsLibrariesDes),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => generalSheet(
|
||||
context,
|
||||
title: s.settingsFeedback,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_feedbackItem(LineIcons.github, s.feedbackGithub,
|
||||
'https://github.com/stonega/tsacdop/issues'),
|
||||
_feedbackItem(LineIcons.telegram, s.feedbackTelegram,
|
||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||
_feedbackItem(LineIcons.envelopeOpenText, s.feedbackEmail,
|
||||
'mailto:<tsacdop.app@gmail.com>?subject=Tsacdop Feedback'),
|
||||
_feedbackItem(LineIcons.googlePlay, s.feedbackPlay,
|
||||
'https://play.google.com/store/apps/details?id=com.stonegate.tsacdop'),
|
||||
],
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.bug, color: Colors.pink[700]),
|
||||
title: Text(s.settingsFeedback),
|
||||
subtitle: Text(s.settingsFeedbackDes),
|
||||
),
|
||||
Divider(
|
||||
height: 2,
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
FeatureDiscovery.clearPreferences(context, <String>{
|
||||
addFeature,
|
||||
menuFeature,
|
||||
playlistFeature,
|
||||
groupsFeature,
|
||||
addGroupFeature,
|
||||
configureGroup,
|
||||
configurePodcast,
|
||||
podcastFeature
|
||||
});
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastDiscovery,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
},
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.capsules, color: Colors.pinkAccent),
|
||||
title: Text(s.settingsDiscovery),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
SlideIntro(goto: Goto.settings))),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.columns, color: Colors.blueGrey),
|
||||
title: Text(s.settingsAppIntro),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.bug, color: Colors.pink[700]),
|
||||
title: Text(s.settingsFeedback),
|
||||
subtitle: Text(s.settingsFeedbackDes),
|
||||
),
|
||||
Divider(
|
||||
height: 2,
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
FeatureDiscovery.clearPreferences(context, <String>{
|
||||
addFeature,
|
||||
menuFeature,
|
||||
playlistFeature,
|
||||
groupsFeature,
|
||||
addGroupFeature,
|
||||
configureGroup,
|
||||
configurePodcast,
|
||||
podcastFeature
|
||||
});
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastDiscovery,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
},
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.capsules, color: Colors.pinkAccent),
|
||||
title: Text(s.settingsDiscovery),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SlideIntro(goto: Goto.settings))),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.columns, color: Colors.blueGrey),
|
||||
title: Text(s.settingsAppIntro),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -23,75 +23,6 @@ class _StorageSettingState extends State<StorageSetting>
|
|||
AnimationController? _controller;
|
||||
late Animation<double> _animation;
|
||||
List<String>? _dirs;
|
||||
Future<void> _getCacheMax() async {
|
||||
var cache =
|
||||
await cacheStorage.getInt(defaultValue: (200 * 1024 * 1024).toInt());
|
||||
if (cache == 0) {
|
||||
await cacheStorage.saveInt((200 * 1024 * 1024).toInt());
|
||||
cache = 200 * 1024 * 1024;
|
||||
}
|
||||
var value = cache ~/ (1024 * 1024);
|
||||
if (value > 100) {
|
||||
_controller = AnimationController(
|
||||
vsync: this, duration: Duration(milliseconds: value * 2));
|
||||
_animation = Tween<double>(begin: 100, end: value.toDouble()).animate(
|
||||
CurvedAnimation(curve: Curves.easeOutQuart, parent: _controller!))
|
||||
..addListener(() {
|
||||
setState(() => _value = _animation.value);
|
||||
});
|
||||
_controller!.forward();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownloadNetwork() async {
|
||||
var storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||
var value = await storage.getBool(defaultValue: false);
|
||||
return value;
|
||||
}
|
||||
|
||||
Future<int?> _getAutoDeleteDays() async {
|
||||
var storage = KeyValueStorage(autoDeleteKey);
|
||||
var days = await storage.getInt();
|
||||
if (days == 0) {
|
||||
storage.saveInt(30);
|
||||
return 30;
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
Future<int?> _getDownloadPasition() async {
|
||||
final storage = KeyValueStorage(downloadPositionKey);
|
||||
final index = await storage.getInt();
|
||||
final externalDirs = await getExternalStorageDirectories();
|
||||
_dirs = [for (var dir in externalDirs!) dir.path];
|
||||
return index;
|
||||
}
|
||||
|
||||
Future<bool> _getDelteAfterPlayed() async {
|
||||
final storage = KeyValueStorage(deleteAfterPlayedKey);
|
||||
return await storage.getBool(defaultValue: false);
|
||||
}
|
||||
|
||||
Future<void> _setAutoDeleteDays(int days) async {
|
||||
var storage = KeyValueStorage(autoDeleteKey);
|
||||
await storage.saveInt(days);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _setAudtDownloadNetwork(bool boo) async {
|
||||
var storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||
await storage.saveBool(boo);
|
||||
}
|
||||
|
||||
Future<void> _setDownloadPosition(int? index) async {
|
||||
final storage = KeyValueStorage(downloadPositionKey);
|
||||
await storage.saveInt(index!);
|
||||
}
|
||||
|
||||
Future<void> _setDeleteAfterPlayed(bool? boo) async {
|
||||
final storage = KeyValueStorage(deleteAfterPlayedKey);
|
||||
await storage.saveBool(boo);
|
||||
}
|
||||
|
||||
late double _value;
|
||||
|
||||
|
@ -113,13 +44,9 @@ class _StorageSettingState extends State<StorageSetting>
|
|||
final s = context.s;
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingStorage),
|
||||
leading: CustomBackButton(),
|
||||
|
@ -345,4 +272,74 @@ class _StorageSettingState extends State<StorageSetting>
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _getCacheMax() async {
|
||||
var cache =
|
||||
await cacheStorage.getInt(defaultValue: (200 * 1024 * 1024).toInt());
|
||||
if (cache == 0) {
|
||||
await cacheStorage.saveInt((200 * 1024 * 1024).toInt());
|
||||
cache = 200 * 1024 * 1024;
|
||||
}
|
||||
var value = cache ~/ (1024 * 1024);
|
||||
if (value > 100) {
|
||||
_controller = AnimationController(
|
||||
vsync: this, duration: Duration(milliseconds: value * 2));
|
||||
_animation = Tween<double>(begin: 100, end: value.toDouble()).animate(
|
||||
CurvedAnimation(curve: Curves.easeOutQuart, parent: _controller!))
|
||||
..addListener(() {
|
||||
setState(() => _value = _animation.value);
|
||||
});
|
||||
_controller!.forward();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownloadNetwork() async {
|
||||
var storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||
var value = await storage.getBool(defaultValue: false);
|
||||
return value;
|
||||
}
|
||||
|
||||
Future<int?> _getAutoDeleteDays() async {
|
||||
var storage = KeyValueStorage(autoDeleteKey);
|
||||
var days = await storage.getInt();
|
||||
if (days == 0) {
|
||||
storage.saveInt(30);
|
||||
return 30;
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
Future<int?> _getDownloadPasition() async {
|
||||
final storage = KeyValueStorage(downloadPositionKey);
|
||||
final index = await storage.getInt();
|
||||
final externalDirs = await getExternalStorageDirectories();
|
||||
_dirs = [for (var dir in externalDirs!) dir.path];
|
||||
return index;
|
||||
}
|
||||
|
||||
Future<bool> _getDelteAfterPlayed() async {
|
||||
final storage = KeyValueStorage(deleteAfterPlayedKey);
|
||||
return await storage.getBool(defaultValue: false);
|
||||
}
|
||||
|
||||
Future<void> _setAutoDeleteDays(int days) async {
|
||||
var storage = KeyValueStorage(autoDeleteKey);
|
||||
await storage.saveInt(days);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _setAudtDownloadNetwork(bool boo) async {
|
||||
var storage = KeyValueStorage(autoDownloadNetworkKey);
|
||||
await storage.saveBool(boo);
|
||||
}
|
||||
|
||||
Future<void> _setDownloadPosition(int? index) async {
|
||||
final storage = KeyValueStorage(downloadPositionKey);
|
||||
await storage.saveInt(index!);
|
||||
}
|
||||
|
||||
Future<void> _setDeleteAfterPlayed(bool? boo) async {
|
||||
final storage = KeyValueStorage(deleteAfterPlayedKey);
|
||||
await storage.saveBool(boo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,7 @@ class SyncingSetting extends StatelessWidget {
|
|||
final s = context.s;
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingsSyncing),
|
||||
|
|
|
@ -13,18 +13,14 @@ class ThemeSetting extends StatelessWidget {
|
|||
final s = context.s;
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingsAppearance),
|
||||
leading: CustomBackButton(),
|
||||
elevation: 0,
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
backgroundColor: context.onPrimary,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
@ -37,96 +33,95 @@ class ThemeSetting extends StatelessWidget {
|
|||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.settingsInterface,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
child: Text(
|
||||
s.settingsInterface,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 200),
|
||||
pageBuilder: (context, animaiton, secondaryAnimation) =>
|
||||
AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
: Color.fromRGBO(15, 15, 15, 1),
|
||||
),
|
||||
child: AlertDialog(
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 40,
|
||||
right: context.width / 3,
|
||||
),
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(10.0))),
|
||||
title: Text(s.settingsTheme),
|
||||
content: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
ClipRRect(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile(
|
||||
title: Text(s.systemDefault),
|
||||
value: ThemeMode.system,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (dynamic value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile(
|
||||
title: Text(s.darkMode),
|
||||
value: ThemeMode.dark,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (dynamic value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile(
|
||||
title: Text(s.lightMode),
|
||||
value: ThemeMode.light,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (dynamic value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 200),
|
||||
pageBuilder: (context, animaiton, secondaryAnimation) =>
|
||||
AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
: Color.fromRGBO(15, 15, 15, 1),
|
||||
),
|
||||
child: AlertDialog(
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 40,
|
||||
right: context.width / 3,
|
||||
),
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0))),
|
||||
title: Text(s.settingsTheme),
|
||||
content: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile(
|
||||
title: Text(s.systemDefault),
|
||||
value: ThemeMode.system,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (dynamic value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile(
|
||||
title: Text(s.darkMode),
|
||||
value: ThemeMode.dark,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (dynamic value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile(
|
||||
title: Text(s.lightMode),
|
||||
value: ThemeMode.light,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (dynamic value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 70.0),
|
||||
// leading: Icon(Icons.colorize),
|
||||
title: Text(s.settingsTheme),
|
||||
|
@ -324,76 +319,77 @@ class __ColorPickerState extends State<_ColorPicker>
|
|||
key: UniqueKey(),
|
||||
controller: _controller,
|
||||
children: Colors.primaries
|
||||
.map<Widget>((color) => ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.fromLTRB(2, 10, 2, 10),
|
||||
crossAxisSpacing: 4,
|
||||
mainAxisSpacing: 4,
|
||||
crossAxisCount: 3,
|
||||
children: <Widget>[
|
||||
_colorCircle(color.shade100),
|
||||
_colorCircle(color.shade200),
|
||||
_colorCircle(color.shade300),
|
||||
_colorCircle(color.shade400),
|
||||
_colorCircle(color.shade500),
|
||||
_colorCircle(color.shade600),
|
||||
_colorCircle(color.shade700),
|
||||
_colorCircle(color.shade800),
|
||||
_colorCircle(color.shade900),
|
||||
...color == Colors.red
|
||||
? _accentList(Colors.redAccent)
|
||||
: color == Colors.pink
|
||||
? _accentList(Colors.pinkAccent)
|
||||
: color == Colors.deepOrange
|
||||
? _accentList(Colors.deepOrangeAccent)
|
||||
: color == Colors.orange
|
||||
? _accentList(Colors.orangeAccent)
|
||||
: color == Colors.amber
|
||||
? _accentList(
|
||||
Colors.amberAccent)
|
||||
: color == Colors.yellow
|
||||
? _accentList(
|
||||
Colors.yellowAccent)
|
||||
: color == Colors.lime
|
||||
? _accentList(
|
||||
Colors.limeAccent)
|
||||
: color ==
|
||||
Colors
|
||||
.lightGreen
|
||||
? _accentList(Colors
|
||||
.lightGreenAccent)
|
||||
: color ==
|
||||
Colors.green
|
||||
? _accentList(Colors
|
||||
.greenAccent)
|
||||
: color ==
|
||||
Colors
|
||||
.teal
|
||||
? _accentList(
|
||||
Colors
|
||||
.tealAccent)
|
||||
: color ==
|
||||
Colors
|
||||
.cyan
|
||||
? _accentList(Colors
|
||||
.cyanAccent)
|
||||
: color ==
|
||||
Colors.lightBlue
|
||||
? _accentList(Colors.lightBlueAccent)
|
||||
: color == Colors.blue
|
||||
? _accentList(Colors.blueAccent)
|
||||
: color == Colors.indigo
|
||||
? _accentList(Colors.indigoAccent)
|
||||
: color == Colors.purple
|
||||
? _accentList(Colors.purpleAccent)
|
||||
: color == Colors.deepPurple
|
||||
? _accentList(Colors.deepPurpleAccent)
|
||||
: []
|
||||
],
|
||||
),
|
||||
))
|
||||
.map<Widget>(
|
||||
(color) => ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.fromLTRB(2, 10, 2, 10),
|
||||
crossAxisSpacing: 4,
|
||||
mainAxisSpacing: 4,
|
||||
crossAxisCount: 3,
|
||||
children: <Widget>[
|
||||
_colorCircle(color.shade100),
|
||||
_colorCircle(color.shade200),
|
||||
_colorCircle(color.shade300),
|
||||
_colorCircle(color.shade400),
|
||||
_colorCircle(color.shade500),
|
||||
_colorCircle(color.shade600),
|
||||
_colorCircle(color.shade700),
|
||||
_colorCircle(color.shade800),
|
||||
_colorCircle(color.shade900),
|
||||
...color == Colors.red
|
||||
? _accentList(Colors.redAccent)
|
||||
: color == Colors.pink
|
||||
? _accentList(Colors.pinkAccent)
|
||||
: color == Colors.deepOrange
|
||||
? _accentList(Colors.deepOrangeAccent)
|
||||
: color == Colors.orange
|
||||
? _accentList(Colors.orangeAccent)
|
||||
: color == Colors.amber
|
||||
? _accentList(Colors.amberAccent)
|
||||
: color == Colors.yellow
|
||||
? _accentList(
|
||||
Colors.yellowAccent)
|
||||
: color == Colors.lime
|
||||
? _accentList(
|
||||
Colors.limeAccent)
|
||||
: color ==
|
||||
Colors.lightGreen
|
||||
? _accentList(Colors
|
||||
.lightGreenAccent)
|
||||
: color ==
|
||||
Colors.green
|
||||
? _accentList(Colors
|
||||
.greenAccent)
|
||||
: color ==
|
||||
Colors
|
||||
.teal
|
||||
? _accentList(
|
||||
Colors
|
||||
.tealAccent)
|
||||
: color ==
|
||||
Colors
|
||||
.cyan
|
||||
? _accentList(
|
||||
Colors
|
||||
.cyanAccent)
|
||||
: color ==
|
||||
Colors.lightBlue
|
||||
? _accentList(Colors.lightBlueAccent)
|
||||
: color == Colors.blue
|
||||
? _accentList(Colors.blueAccent)
|
||||
: color == Colors.indigo
|
||||
? _accentList(Colors.indigoAccent)
|
||||
: color == Colors.purple
|
||||
? _accentList(Colors.purpleAccent)
|
||||
: color == Colors.deepPurple
|
||||
? _accentList(Colors.deepPurpleAccent)
|
||||
: []
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -150,6 +150,7 @@ class SettingState extends ChangeNotifier {
|
|||
color: Colors.grey[100],
|
||||
elevation: 0,
|
||||
titleTextStyle: TextStyle(color: Colors.black),
|
||||
scrolledUnderElevation: 1,
|
||||
iconTheme: IconThemeData(color: Colors.black),
|
||||
systemOverlayStyle: SystemUiOverlayStyle.dark),
|
||||
textTheme: TextTheme(
|
||||
|
@ -259,6 +260,7 @@ class SettingState extends ChangeNotifier {
|
|||
appBarTheme: AppBarTheme(
|
||||
color: Colors.grey[900],
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
systemOverlayStyle: SystemUiOverlayStyle.light),
|
||||
buttonTheme: ButtonThemeData(height: 32),
|
||||
dialogBackgroundColor: _realDark! ? Colors.grey[900] : null,
|
||||
|
|
|
@ -79,6 +79,14 @@ class EpisodeBrief extends Equatable {
|
|||
? primaryColor!.colorizedark()
|
||||
: primaryColor!.colorizeLight();
|
||||
}
|
||||
|
||||
Color cardColor(BuildContext context) {
|
||||
final schema = ColorScheme.fromSeed(
|
||||
seedColor: primaryColor!.colorizedark(),
|
||||
brightness: context.brightness,
|
||||
);
|
||||
return schema.primaryContainer;
|
||||
}
|
||||
|
||||
EpisodeBrief copyWith({
|
||||
String? mediaId,
|
||||
|
|
|
@ -42,12 +42,13 @@ class PodcastLocal extends Equatable {
|
|||
this.description = '',
|
||||
this.updateCount = 0,
|
||||
this.episodeCount = 0,
|
||||
}) : assert(rssUrl != null);
|
||||
});
|
||||
|
||||
ImageProvider get avatarImage {
|
||||
return (File(imagePath!).existsSync()
|
||||
? FileImage(File(imagePath!))
|
||||
: const AssetImage('assets/avatar_backup.png')) as ImageProvider<Object>;
|
||||
? FileImage(File(imagePath!))
|
||||
: const AssetImage('assets/avatar_backup.png'))
|
||||
as ImageProvider<Object>;
|
||||
}
|
||||
|
||||
Color backgroudColor(BuildContext context) {
|
||||
|
@ -56,13 +57,30 @@ class PodcastLocal extends Equatable {
|
|||
: primaryColor!.colorizeLight();
|
||||
}
|
||||
|
||||
Color cardColor(BuildContext context) {
|
||||
final schema = ColorScheme.fromSeed(
|
||||
seedColor: primaryColor!.colorizedark(),
|
||||
brightness: context.brightness,
|
||||
);
|
||||
return schema.primaryContainer;
|
||||
}
|
||||
|
||||
PodcastLocal copyWith({int? updateCount, int? episodeCount}) {
|
||||
return PodcastLocal(title, imageUrl, rssUrl, primaryColor, author, id,
|
||||
imagePath, provider, link, funding,
|
||||
description: description,
|
||||
updateCount: updateCount ?? 0,
|
||||
episodeCount: episodeCount ?? 0,
|
||||
);
|
||||
return PodcastLocal(
|
||||
title,
|
||||
imageUrl,
|
||||
rssUrl,
|
||||
primaryColor,
|
||||
author,
|
||||
id,
|
||||
imagePath,
|
||||
provider,
|
||||
link,
|
||||
funding,
|
||||
description: description,
|
||||
updateCount: updateCount ?? 0,
|
||||
episodeCount: episodeCount ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -2,8 +2,8 @@ import 'dart:convert';
|
|||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:material_color_utilities/material_color_utilities.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import '../generated/l10n.dart';
|
||||
|
@ -16,6 +16,7 @@ extension ContextExtension on BuildContext {
|
|||
Color get onPrimary => Theme.of(this).colorScheme.onPrimary;
|
||||
Color get background => Theme.of(this).colorScheme.background;
|
||||
Color get tertiary => colorScheme.tertiary;
|
||||
Color get tertiaryContainer => colorScheme.tertiaryContainer;
|
||||
Color get onTertiary => colorScheme.onTertiary;
|
||||
Color get secondary => colorScheme.secondary;
|
||||
Color get onsecondary => colorScheme.onSecondary;
|
||||
|
@ -30,6 +31,12 @@ extension ContextExtension on BuildContext {
|
|||
double get height => MediaQuery.of(this).size.height;
|
||||
double get paddingTop => MediaQuery.of(this).padding.top;
|
||||
TextTheme get textTheme => Theme.of(this).textTheme;
|
||||
SystemUiOverlayStyle get overlay => SystemUiOverlayStyle(
|
||||
statusBarColor: onPrimary,
|
||||
statusBarIconBrightness: iconBrightness,
|
||||
systemNavigationBarColor: onPrimary,
|
||||
systemNavigationBarIconBrightness: iconBrightness,
|
||||
);
|
||||
S get s => S.of(this)!;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,17 +36,15 @@ Widget featureDiscoveryOverlay(BuildContext context,
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(description),
|
||||
FlatButton(
|
||||
color: buttonColor,
|
||||
padding: EdgeInsets.zero,
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(primary: buttonColor),
|
||||
child: Text(s.understood,
|
||||
style: context.textTheme.button!.copyWith(color: Colors.white)),
|
||||
onPressed: () async =>
|
||||
FeatureDiscovery.completeCurrentStep(context),
|
||||
),
|
||||
FlatButton(
|
||||
color: buttonColor,
|
||||
padding: EdgeInsets.zero,
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(primary: buttonColor),
|
||||
child: Text(s.dismiss,
|
||||
style: context.textTheme.button!.copyWith(color: Colors.white)),
|
||||
onPressed: () => FeatureDiscovery.dismissAll(context),
|
||||
|
|
10
pubspec.yaml
10
pubspec.yaml
|
@ -4,7 +4,7 @@ description: An open source podcast player.
|
|||
version: 0.6.0+48
|
||||
publish_to: "none"
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
@ -55,13 +55,13 @@ dependencies:
|
|||
just_audio: ^0.9.23
|
||||
flutter_downloader:
|
||||
git:
|
||||
url: https://github.com/stonega/flutter_downloader.git
|
||||
url: https://github.com/tsacdop/flutter_downloader.git
|
||||
focused_menu:
|
||||
git:
|
||||
url: https://github.com/stonega/focused_menu.git
|
||||
url: https://github.com/tsacdop/focused_menu.git
|
||||
webfeed:
|
||||
git:
|
||||
url: https://github.com/stonega/webfeed.git
|
||||
url: https://github.com/tsacdop/webfeed.git
|
||||
equatable: ^2.0.0
|
||||
path_provider: ^2.0.1
|
||||
http_parser: ^4.0.0
|
||||
|
@ -74,7 +74,7 @@ dependencies:
|
|||
dependency_overrides:
|
||||
linkify:
|
||||
git:
|
||||
url: https://github.com/stonega/linkify.git
|
||||
url: https://github.com/tsacdop/linkify.git
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue