feat: update setting pages to material you

This commit is contained in:
xijieyin 2022-06-04 01:33:34 +08:00
parent e97a493135
commit 92dd3dd34e
21 changed files with 2096 additions and 2282 deletions

View File

@ -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),
),
),
],
),
),

View File

@ -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(

View File

@ -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,
),
],
),
),
),
],
),
),
),
),
),
),
);
}
}

View File

@ -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,
),
],
),
);
},
),
);
}

View File

@ -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

View File

@ -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: () {

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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),
],
),
)),
);
}
}

View File

@ -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),

View File

@ -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 {

View File

@ -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),
),
],
),
),
),

View File

@ -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);
}
}

View File

@ -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),

View File

@ -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(),
),
),

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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)!;
}

View File

@ -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),

View File

@ -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: