fix: update material you theme
This commit is contained in:
parent
92dd3dd34e
commit
d032979263
|
@ -104,7 +104,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
|
@ -120,6 +120,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
widget.episodeItem!.cardColor(context),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
scrolledUnderElevation: 0,
|
||||
title: _showTitle
|
||||
? Text(
|
||||
widget.episodeItem?.title ?? '',
|
||||
|
@ -195,8 +196,8 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
s.minsCount(
|
||||
widget.episodeItem!.duration! ~/ 60,
|
||||
),
|
||||
style:
|
||||
TextStyle(color: context.onPrimary),
|
||||
style: TextStyle(
|
||||
color: context.background),
|
||||
)),
|
||||
if (widget.episodeItem!.enclosureLength !=
|
||||
null &&
|
||||
|
@ -214,7 +215,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||
child: Text(
|
||||
'${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
|
||||
style:
|
||||
TextStyle(color: context.onPrimary),
|
||||
TextStyle(color: context.background),
|
||||
),
|
||||
),
|
||||
FutureBuilder<PlayHistory>(
|
||||
|
|
|
@ -30,16 +30,16 @@ class AboutApp extends StatelessWidget {
|
|||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: context.onPrimary,
|
||||
statusBarColor: context.background,
|
||||
statusBarIconBrightness: context.iconBrightness,
|
||||
systemNavigationBarColor: context.onPrimary,
|
||||
systemNavigationBarColor: context.background,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
title: Text(s.homeToprightMenuAbout),
|
||||
scrolledUnderElevation: 1,
|
||||
leading: CustomBackButton(),
|
||||
|
|
|
@ -97,8 +97,8 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
|||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarIconBrightness: context.brightness,
|
||||
statusBarIconBrightness: context.iconBrightness,
|
||||
systemNavigationBarColor: context.onPrimary,
|
||||
statusBarColor: context.onPrimary,
|
||||
systemNavigationBarColor: context.background,
|
||||
statusBarColor: context.background,
|
||||
),
|
||||
child: WillPopScope(
|
||||
onWillPop: () async {
|
||||
|
@ -115,7 +115,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
|||
},
|
||||
child: Scaffold(
|
||||
key: _scaffoldKey,
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
body: Stack(
|
||||
children: <Widget>[
|
||||
SafeArea(
|
||||
|
@ -163,8 +163,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
|||
Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? settings.setTheme = ThemeMode.dark
|
||||
: settings.setTheme =
|
||||
ThemeMode.light
|
||||
: settings.setTheme = ThemeMode.light
|
||||
},
|
||||
child: Text(
|
||||
'Tsacdop',
|
||||
|
|
|
@ -94,113 +94,115 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onVerticalDragEnd: (event) {
|
||||
if (event.primaryVelocity! > 200) {
|
||||
if (groups.length == 1) {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.addSomeGroups,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
(_groupIndex != 0)
|
||||
? _groupIndex--
|
||||
: _groupIndex = groups.length - 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (event.primaryVelocity! < -200) {
|
||||
if (groups.length == 1) {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.addSomeGroups,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(
|
||||
() {
|
||||
(_groupIndex < groups.length - 1)
|
||||
? _groupIndex++
|
||||
: _groupIndex = 0;
|
||||
},
|
||||
);
|
||||
}
|
||||
onVerticalDragEnd: (event) {
|
||||
if (event.primaryVelocity! > 200) {
|
||||
if (groups.length == 1) {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.addSomeGroups,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
(_groupIndex != 0)
|
||||
? _groupIndex--
|
||||
: _groupIndex = groups.length - 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 30,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0),
|
||||
child: Text(
|
||||
groups[_groupIndex]!.name!,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(color: context.accentColor),
|
||||
),
|
||||
} else if (event.primaryVelocity! < -200) {
|
||||
if (groups.length == 1) {
|
||||
Fluttertoast.showToast(
|
||||
msg: s.addSomeGroups,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(
|
||||
() {
|
||||
(_groupIndex < groups.length - 1)
|
||||
? _groupIndex++
|
||||
: _groupIndex = 0;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 30,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
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),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(
|
||||
page: context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefalt!
|
||||
? PodcastList()
|
||||
: PodcastManage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(page: PodcastList()),
|
||||
);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
s.homeGroupsSeeAll,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(
|
||||
color: import
|
||||
? context.primaryColorDark
|
||||
: context.accentColor),
|
||||
),
|
||||
Spacer(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
onTap: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(
|
||||
page: context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefalt!
|
||||
? PodcastList()
|
||||
: PodcastManage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(page: PodcastList()),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
s.homeGroupsSeeAll,
|
||||
style:
|
||||
context.textTheme.bodyText1!.copyWith(
|
||||
color: import
|
||||
? context.primaryColorDark
|
||||
: context.accentColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 70,
|
||||
color: context.background,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
_circleContainer(context),
|
||||
_circleContainer(context),
|
||||
_circleContainer(context)
|
||||
],
|
||||
)),
|
||||
],
|
||||
)),
|
||||
),
|
||||
Container(
|
||||
height: 70,
|
||||
color: context.background,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
_circleContainer(context),
|
||||
_circleContainer(context),
|
||||
_circleContainer(context)
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: (width - 20) / 3 + 40,
|
||||
color: context.onPrimary,
|
||||
color: context.background,
|
||||
margin: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Center(
|
||||
child: _groupIndex == 0
|
||||
|
@ -297,39 +299,39 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(
|
||||
page: context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefalt!
|
||||
? PodcastList()
|
||||
: PodcastManage()),
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(page: PodcastList()),
|
||||
);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
s.homeGroupsSeeAll,
|
||||
style: context.textTheme.bodyText1!
|
||||
.copyWith(
|
||||
color: import
|
||||
? context.primaryColorDark
|
||||
: context.accentColor),
|
||||
),
|
||||
)),
|
||||
onTap: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(
|
||||
page: context
|
||||
.read<SettingState>()
|
||||
.openAllPodcastDefalt!
|
||||
? PodcastList()
|
||||
: PodcastManage()),
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (!import) {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(page: PodcastList()),
|
||||
);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
s.homeGroupsSeeAll,
|
||||
style: context.textTheme.bodyText1!.copyWith(
|
||||
color: import
|
||||
? context.primaryColorDark
|
||||
: context.accentColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -338,7 +340,7 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
height: 70,
|
||||
width: width,
|
||||
alignment: Alignment.centerLeft,
|
||||
color: context.onPrimary,
|
||||
color: context.background,
|
||||
child: TabBar(
|
||||
labelPadding: EdgeInsets.fromLTRB(6.0, 5.0, 6.0, 10.0),
|
||||
indicator: CircleTabIndicator(
|
||||
|
@ -370,47 +372,45 @@ class _ScrollPodcastsState extends State<ScrollPodcasts>
|
|||
),
|
||||
Container(
|
||||
height: (width - 20) / 3 + 40,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: context.background,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: TabBarView(
|
||||
children: groups[_groupIndex]!.podcasts.map<Widget>(
|
||||
(podcastLocal) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: Colors.black12),
|
||||
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
HidePlayerRoute(
|
||||
PodcastDetail(
|
||||
podcastLocal: podcastLocal,
|
||||
),
|
||||
PodcastDetail(
|
||||
podcastLocal: podcastLocal,
|
||||
hide: true),
|
||||
duration: Duration(milliseconds: 300),
|
||||
));
|
||||
},
|
||||
child: PodcastPreview(
|
||||
podcastLocal: podcastLocal,
|
||||
),
|
||||
child: TabBarView(
|
||||
children: groups[_groupIndex]!.podcasts.map<Widget>(
|
||||
(podcastLocal) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: Colors.black12),
|
||||
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
HidePlayerRoute(
|
||||
PodcastDetail(
|
||||
podcastLocal: podcastLocal,
|
||||
),
|
||||
PodcastDetail(
|
||||
podcastLocal: podcastLocal, hide: true),
|
||||
duration: Duration(milliseconds: 300),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: PodcastPreview(
|
||||
podcastLocal: podcastLocal,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -494,7 +494,9 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
|||
episodes: snapshot.data,
|
||||
podcastLocal: widget.podcastLocal,
|
||||
)
|
||||
: Padding(padding: const EdgeInsets.all(5.0));
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -561,8 +563,8 @@ class ShowEpisode extends StatelessWidget {
|
|||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 1.5,
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 8.0,
|
||||
crossAxisSpacing: 6.0,
|
||||
mainAxisSpacing: 20,
|
||||
crossAxisSpacing: 14,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
|
|
|
@ -27,17 +27,6 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||
final List<OnlinePodcast> _podcastList = [];
|
||||
Future? _searchTopPodcast;
|
||||
Future? _getIfHideDiscovery;
|
||||
Future<List<String?>?> _getSearchHistory() {
|
||||
final storage = KeyValueStorage(searchHistoryKey);
|
||||
final history = storage.getStringList();
|
||||
return history;
|
||||
}
|
||||
|
||||
void backToHome() {
|
||||
setState(() {
|
||||
_selectedGenre = null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -46,95 +35,257 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||
_getIfHideDiscovery = _getHideDiscovery();
|
||||
}
|
||||
|
||||
Widget _loadTopPodcasts() => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10), color: context.primaryColor),
|
||||
width: 120,
|
||||
margin: EdgeInsets.fromLTRB(10, 10, 0, 10),
|
||||
padding: EdgeInsets.all(4),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: context.primaryColorDark,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 2,
|
||||
child: LinearProgressIndicator(value: 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<bool>(
|
||||
future: _getIfHideDiscovery!.then((value) => value as bool),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Center();
|
||||
} else if (snapshot.data! || environment['apiKey'] == '') {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: context.textTheme.bodyText1!.fontSize,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
_historyList(),
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.search,
|
||||
size: 80,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
width: 40,
|
||||
height: context.textTheme.bodyText1!.fontSize,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.microphone,
|
||||
size: 30,
|
||||
color: Colors.lightBlue,
|
||||
),
|
||||
SizedBox(width: 50),
|
||||
Icon(
|
||||
LineIcons.broadcastTower,
|
||||
size: 30,
|
||||
color: Colors.deepPurple,
|
||||
),
|
||||
SizedBox(width: 50),
|
||||
Icon(
|
||||
LineIcons.rssSquare,
|
||||
size: 30,
|
||||
color: Colors.blueGrey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(50, 20, 50, 20),
|
||||
child: Center(
|
||||
child: Text(
|
||||
context.s.searchHelper,
|
||||
textAlign: TextAlign.center,
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: Colors.grey[400]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
primary: context.accentColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.grey[500]!)),
|
||||
// highlightedBorderColor: Colors.grey[500],
|
||||
// disabledTextColor: Colors.grey[500],
|
||||
// disabledBorderColor: Colors.grey[500],
|
||||
);
|
||||
} else {
|
||||
return PodcastSlideup(
|
||||
searchEngine: SearchEngine.listenNotes,
|
||||
child: Selector<SearchState, Genre?>(
|
||||
selector: (_, searchState) => searchState.genre,
|
||||
builder: (_, genre, __) => IndexedStack(
|
||||
index: genre == null ? 0 : 1,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_historyList(),
|
||||
SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: FutureBuilder<List<OnlinePodcast>>(
|
||||
future: _searchTopPodcast!.then(
|
||||
(value) => value as List<OnlinePodcast>),
|
||||
builder: (context, snapshot) {
|
||||
return ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: ListView(
|
||||
addAutomaticKeepAlives: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: snapshot.hasData
|
||||
? snapshot.data!
|
||||
.map<Widget>((podcast) {
|
||||
return _podcastCard(
|
||||
podcast,
|
||||
onTap: () {
|
||||
context
|
||||
.read<SearchState>()
|
||||
.selectedPodcast =
|
||||
podcast;
|
||||
widget.onTap!('');
|
||||
},
|
||||
);
|
||||
}).toList()
|
||||
: [
|
||||
_loadTopPodcasts(),
|
||||
_loadTopPodcasts(),
|
||||
_loadTopPodcasts(),
|
||||
_loadTopPodcasts(),
|
||||
]),
|
||||
);
|
||||
}),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 10, 10, 4),
|
||||
child: Text('Categories',
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
ListView(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
children: genres
|
||||
.map<Widget>((e) => ListTile(
|
||||
contentPadding:
|
||||
EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
onTap: () {
|
||||
widget.onTap!('');
|
||||
context.read<SearchState>().setGenre = e;
|
||||
},
|
||||
title: Text(e.name!,
|
||||
style: context.textTheme.headline6),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Center(
|
||||
child: Image(
|
||||
image: context.brightness == Brightness.light
|
||||
? AssetImage('assets/listennotes.png')
|
||||
: AssetImage('assets/listennotes_light.png'),
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Text(context.s.subscribe),
|
||||
onPressed: () {}),
|
||||
),
|
||||
genre == null ? Center() : _TopPodcastList(genre: genre),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
));
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _loadTopPodcasts() => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: context.primaryColor),
|
||||
width: 120,
|
||||
margin: EdgeInsets.fromLTRB(10, 10, 0, 10),
|
||||
padding: EdgeInsets.all(4),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: context.primaryColorDark,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 2,
|
||||
child: LinearProgressIndicator(value: 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: context.textTheme.bodyText1!.fontSize,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
width: 40,
|
||||
height: context.textTheme.bodyText1!.fontSize,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColorDark,
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
primary: context.accentColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
side: BorderSide(color: Colors.grey[500]!)),
|
||||
// highlightedBorderColor: Colors.grey[500],
|
||||
// disabledTextColor: Colors.grey[500],
|
||||
// disabledBorderColor: Colors.grey[500],
|
||||
),
|
||||
child: Text(context.s.subscribe),
|
||||
onPressed: () {}),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _historyList() => FutureBuilder<List<String?>?>(
|
||||
future: _getSearchHistory(),
|
||||
initialData: [],
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final history = snapshot.data!;
|
||||
return Wrap(
|
||||
direction: Axis.horizontal,
|
||||
children: history
|
||||
.map<Widget>((e) => Padding(
|
||||
future: _getSearchHistory(),
|
||||
initialData: [],
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final history = snapshot.data!;
|
||||
return Wrap(
|
||||
direction: Axis.horizontal,
|
||||
children: history
|
||||
.map<Widget>(
|
||||
(e) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 2, 0, 0),
|
||||
child: FlatButton.icon(
|
||||
color: Colors.accents[history.indexOf(e)].withAlpha(70),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
primary: Colors.accents[history.indexOf(e)],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100.0),
|
||||
),
|
||||
),
|
||||
onPressed: () => widget.onTap!(e),
|
||||
label: Text(e!),
|
||||
|
@ -143,32 +294,32 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||
size: 20,
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
return SizedBox(
|
||||
height: 0,
|
||||
);
|
||||
});
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
return Center();
|
||||
},
|
||||
);
|
||||
|
||||
Widget _podcastCard(OnlinePodcast podcast, {VoidCallback? onTap}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: context.primaryColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: context.background,
|
||||
border:
|
||||
Border.all(color: context.textColor.withOpacity(0.1), width: 1)),
|
||||
width: 120,
|
||||
margin: EdgeInsets.fromLTRB(10, 10, 0, 10),
|
||||
width: 140,
|
||||
margin: EdgeInsets.fromLTRB(20, 10, 0, 10),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
|
@ -200,6 +351,18 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<List<String?>?> _getSearchHistory() {
|
||||
final storage = KeyValueStorage(searchHistoryKey);
|
||||
final history = storage.getStringList();
|
||||
return history;
|
||||
}
|
||||
|
||||
void backToHome() {
|
||||
setState(() {
|
||||
_selectedGenre = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<OnlinePodcast>> _getTopPodcasts({int? page}) async {
|
||||
if (environment['apiKey'] == '') return [];
|
||||
final searchEngine = ListenNotesSearch();
|
||||
|
@ -222,167 +385,6 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||
final storage = KeyValueStorage(hidePodcastDiscoveryKey);
|
||||
return await storage.getBool(defaultValue: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<bool>(
|
||||
future: _getIfHideDiscovery!.then((value) => value as bool),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Center();
|
||||
} else if (snapshot.data! || environment['apiKey'] == '') {
|
||||
return ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_historyList(),
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.search,
|
||||
size: 80,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.microphone,
|
||||
size: 30,
|
||||
color: Colors.lightBlue,
|
||||
),
|
||||
SizedBox(width: 50),
|
||||
Icon(
|
||||
LineIcons.broadcastTower,
|
||||
size: 30,
|
||||
color: Colors.deepPurple,
|
||||
),
|
||||
SizedBox(width: 50),
|
||||
Icon(
|
||||
LineIcons.rssSquare,
|
||||
size: 30,
|
||||
color: Colors.blueGrey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(50, 20, 50, 20),
|
||||
child: Center(
|
||||
child: Text(
|
||||
context.s.searchHelper,
|
||||
textAlign: TextAlign.center,
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: Colors.grey[400]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return PodcastSlideup(
|
||||
searchEngine: SearchEngine.listenNotes,
|
||||
child: Selector<SearchState, Genre?>(
|
||||
selector: (_, searchState) => searchState.genre,
|
||||
builder: (_, genre, __) => IndexedStack(
|
||||
index: genre == null ? 0 : 1,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_historyList(),
|
||||
SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: FutureBuilder<List<OnlinePodcast>>(
|
||||
future: _searchTopPodcast!.then(
|
||||
(value) => value as List<OnlinePodcast>),
|
||||
builder: (context, snapshot) {
|
||||
return ScrollConfiguration(
|
||||
behavior: NoGrowBehavior(),
|
||||
child: ListView(
|
||||
addAutomaticKeepAlives: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: snapshot.hasData
|
||||
? snapshot.data!
|
||||
.map<Widget>((podcast) {
|
||||
return _podcastCard(
|
||||
podcast,
|
||||
onTap: () {
|
||||
context
|
||||
.read<SearchState>()
|
||||
.selectedPodcast =
|
||||
podcast;
|
||||
widget.onTap!('');
|
||||
},
|
||||
);
|
||||
}).toList()
|
||||
: [
|
||||
_loadTopPodcasts(),
|
||||
_loadTopPodcasts(),
|
||||
_loadTopPodcasts(),
|
||||
_loadTopPodcasts(),
|
||||
]),
|
||||
);
|
||||
}),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 10, 10, 4),
|
||||
child: Text('Categories',
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: context.accentColor)),
|
||||
),
|
||||
ListView(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
children: genres
|
||||
.map<Widget>((e) => ListTile(
|
||||
contentPadding:
|
||||
EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
onTap: () {
|
||||
widget.onTap!('');
|
||||
context.read<SearchState>().setGenre =
|
||||
e;
|
||||
},
|
||||
title: Text(e.name!,
|
||||
style: context.textTheme.headline6),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Center(
|
||||
child: Image(
|
||||
image: context.brightness == Brightness.light
|
||||
? AssetImage('assets/listennotes.png')
|
||||
: AssetImage(
|
||||
'assets/listennotes_light.png'),
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
genre == null ? Center() : _TopPodcastList(genre: genre),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _TopPodcastList extends StatefulWidget {
|
||||
|
@ -398,20 +400,6 @@ class __TopPodcastListState extends State<_TopPodcastList> {
|
|||
Future? _searchFuture;
|
||||
late bool _loading;
|
||||
late int _page;
|
||||
Future<List<OnlinePodcast>> _getTopPodcasts(
|
||||
{required Genre genre, int? page}) async {
|
||||
final searchEngine = ListenNotesSearch();
|
||||
var searchResult = await searchEngine.fetchBestPodcast(
|
||||
genre: genre.id,
|
||||
page: page,
|
||||
);
|
||||
final podcastTopList = [
|
||||
for (final p in searchResult!.podcasts!) p?.toOnlinePodcast
|
||||
];
|
||||
_podcastList.addAll(podcastTopList.cast());
|
||||
_loading = false;
|
||||
return _podcastList;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -462,12 +450,8 @@ class __TopPodcastListState extends State<_TopPodcastList> {
|
|||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(100))),
|
||||
),
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(),
|
||||
// highlightedBorderColor: context.accentColor,
|
||||
// splashColor: context.accentColor.withOpacity(0.5),
|
||||
child: _loading
|
||||
|
@ -498,4 +482,19 @@ class __TopPodcastListState extends State<_TopPodcastList> {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<OnlinePodcast>> _getTopPodcasts(
|
||||
{required Genre genre, int? page}) async {
|
||||
final searchEngine = ListenNotesSearch();
|
||||
final searchResult = await searchEngine.fetchBestPodcast(
|
||||
genre: genre.id,
|
||||
page: page,
|
||||
);
|
||||
final podcastTopList = [
|
||||
for (final p in searchResult!.podcasts!) p?.toOnlinePodcast
|
||||
];
|
||||
_podcastList.addAll(podcastTopList.cast());
|
||||
_loading = false;
|
||||
return _podcastList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
|
|||
connectTimeout: 30000,
|
||||
receiveTimeout: 90000,
|
||||
);
|
||||
var response = await Dio(options).get(url);
|
||||
final response = await Dio(options).get(url);
|
||||
return RssFeed.parse(response.data);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
|
@ -88,7 +88,10 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
|
|||
child: IconButton(
|
||||
tooltip: context.s.back,
|
||||
splashRadius: 20,
|
||||
icon: Icon(_getIconData(Theme.of(context).platform)),
|
||||
icon: Icon(
|
||||
_getIconData(Theme.of(context).platform),
|
||||
color: context.textColor,
|
||||
),
|
||||
onPressed: () {
|
||||
close(context, 1);
|
||||
},
|
||||
|
@ -164,12 +167,10 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
|
|||
switch (_searchEngine) {
|
||||
case SearchEngine.listenNotes:
|
||||
return _ListenNotesSearch(query: query);
|
||||
break;
|
||||
case SearchEngine.podcastIndex:
|
||||
return _PodcastIndexSearch(query: query);
|
||||
default:
|
||||
return Center();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,14 +218,14 @@ class _RssResultState extends State<RssResult> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
var items = widget.rssFeed!.items!;
|
||||
final items = widget.rssFeed!.items!;
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
color: context.primaryColor,
|
||||
height: 140,
|
||||
height: 160,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
@ -314,8 +315,12 @@ class _RssResultState extends State<RssResult> {
|
|||
url!.launchUrl;
|
||||
},
|
||||
style: {
|
||||
'html': Style(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
),
|
||||
'a': Style(
|
||||
color: context.accentColor,
|
||||
textDecoration: TextDecoration.none,
|
||||
)
|
||||
},
|
||||
shrinkWrap: true,
|
||||
|
@ -331,13 +336,9 @@ class _RssResultState extends State<RssResult> {
|
|||
return Container(
|
||||
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
|
||||
alignment: Alignment.center,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
// highlightedBorderColor: context.accentColor,
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
onSurface: context.accentColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(100))),
|
||||
),
|
||||
child: Text(context.s.loadMore),
|
||||
onPressed: () => setState(
|
||||
|
@ -378,18 +379,12 @@ class __SearchPopupMenuState extends State<_SearchPopupMenu> {
|
|||
_getSearchEngine();
|
||||
}
|
||||
|
||||
Future<void> _getSearchEngine() async {
|
||||
final storage = KeyValueStorage(searchEngineKey);
|
||||
final index = await storage.getInt();
|
||||
setState(() => _searchEngine = SearchEngine.values[index]);
|
||||
widget.onSelected!(_searchEngine);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton<SearchEngine>(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
elevation: 1,
|
||||
color: context.priamryContainer,
|
||||
icon: SizedBox(
|
||||
height: 25,
|
||||
width: 25,
|
||||
|
@ -437,11 +432,18 @@ class __SearchPopupMenuState extends State<_SearchPopupMenu> {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _getSearchEngine() async {
|
||||
final storage = KeyValueStorage(searchEngineKey);
|
||||
final index = await storage.getInt();
|
||||
setState(() => _searchEngine = SearchEngine.values[index]);
|
||||
widget.onSelected!(_searchEngine);
|
||||
}
|
||||
}
|
||||
|
||||
class _ListenNotesSearch extends StatefulWidget {
|
||||
final String? query;
|
||||
_ListenNotesSearch({this.query, Key? key}) : super(key: key);
|
||||
final String query;
|
||||
_ListenNotesSearch({required this.query, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
__ListenNotesSearchState createState() => __ListenNotesSearchState();
|
||||
|
@ -461,9 +463,9 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
|
|||
_searchFuture = _getListenNotesList(widget.query, _nextOffset);
|
||||
}
|
||||
|
||||
Future<void> _saveHistory(String? query) async {
|
||||
Future<void> _saveHistory(String query) async {
|
||||
final storage = KeyValueStorage(searchHistoryKey);
|
||||
final history = await (storage.getStringList() as FutureOr<List<String?>>);
|
||||
final history = await storage.getStringList();
|
||||
if (!history.contains(query)) {
|
||||
if (history.length >= 6) {
|
||||
history.removeLast();
|
||||
|
@ -474,7 +476,7 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
|
|||
}
|
||||
|
||||
Future<List<OnlinePodcast>> _getListenNotesList(
|
||||
String? searchText, int? nextOffset) async {
|
||||
String searchText, int? nextOffset) async {
|
||||
if (nextOffset == 0) _saveHistory(searchText);
|
||||
final searchEngine = ListenNotesSearch();
|
||||
var searchResult;
|
||||
|
@ -544,12 +546,13 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
side: BorderSide(color: context.accentColor),
|
||||
onSurface: context.accentColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100)),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
child: _loading
|
||||
? SizedBox(
|
||||
|
@ -582,7 +585,9 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
|
|||
child: Text(
|
||||
'Powered by ListenNotes',
|
||||
style: GoogleFonts.quicksand(
|
||||
color: Colors.red, textStyle: TextStyle(fontSize: 15)),
|
||||
color: Colors.red,
|
||||
textStyle: TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
// Image(
|
||||
// image: context.brightness == Brightness.light
|
||||
|
@ -662,101 +667,102 @@ class __PodcastIndexSearchState extends State<_PodcastIndexSearch> {
|
|||
return PodcastSlideup(
|
||||
searchEngine: SearchEngine.podcastIndex,
|
||||
child: FutureBuilder<List>(
|
||||
future: _searchFuture.then((value) => value as List<dynamic>),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData && widget.query != null) {
|
||||
future: _searchFuture.then((value) => value as List<dynamic>),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData && widget.query != null) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Platform.isIOS
|
||||
? CupertinoActivityIndicator()
|
||||
: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
if (snapshot.data!.isEmpty) {
|
||||
if (_loadError) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Platform.isIOS
|
||||
? CupertinoActivityIndicator()
|
||||
: CircularProgressIndicator(),
|
||||
child: Text('Network error.',
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: Colors.red)),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Text('No result found.',
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: context.accentColor)),
|
||||
);
|
||||
}
|
||||
if (snapshot.data!.isEmpty) {
|
||||
if (_loadError) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Text('Network error.',
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: Colors.red)),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Text('No result found.',
|
||||
style: context.textTheme.headline6!
|
||||
.copyWith(color: context.accentColor)),
|
||||
);
|
||||
}
|
||||
}
|
||||
var content = snapshot.data!;
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return SearchResult(onlinePodcast: content[index]);
|
||||
},
|
||||
childCount: content.length,
|
||||
),
|
||||
}
|
||||
var content = snapshot.data!;
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return SearchResult(onlinePodcast: content[index]);
|
||||
},
|
||||
childCount: content.length,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: context.accentColor),
|
||||
onSurface: context.accentColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100)),
|
||||
),
|
||||
child: _loading
|
||||
? SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
))
|
||||
: Text(context.s.loadMore),
|
||||
onPressed: () => _loading
|
||||
? null
|
||||
: setState(
|
||||
() {
|
||||
_loading = true;
|
||||
_limit += 10;
|
||||
_searchFuture = _getPodcatsIndexList(
|
||||
widget.query!,
|
||||
limit: _limit);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
side: BorderSide(color: context.accentColor),
|
||||
onSurface: context.accentColor.withOpacity(0.5),
|
||||
),
|
||||
child: _loading
|
||||
? SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: Text(context.s.loadMore),
|
||||
onPressed: () => _loading
|
||||
? null
|
||||
: setState(
|
||||
() {
|
||||
_loading = true;
|
||||
_limit += 10;
|
||||
_searchFuture = _getPodcatsIndexList(
|
||||
widget.query!,
|
||||
limit: _limit);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Powered by PODCASTINDEX',
|
||||
style: GoogleFonts.quicksand(
|
||||
color: Colors.red,
|
||||
textStyle: TextStyle(fontSize: 15)),
|
||||
),
|
||||
)
|
||||
// Image(
|
||||
// image: AssetImage('assets/podcastindex.png'),
|
||||
// height: 15,
|
||||
// ),
|
||||
))
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Powered by PODCASTINDEX',
|
||||
style: GoogleFonts.quicksand(
|
||||
color: Colors.red,
|
||||
textStyle: TextStyle(fontSize: 15)),
|
||||
),
|
||||
)
|
||||
// Image(
|
||||
// image: AssetImage('assets/podcastindex.png'),
|
||||
// height: 15,
|
||||
// ),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -997,12 +1003,10 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
|||
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox(
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
side: BorderSide(color: context.accentColor),
|
||||
onSurface: context.accentColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(100)),
|
||||
),
|
||||
child: _loading
|
||||
? SizedBox(
|
||||
|
|
|
@ -44,6 +44,10 @@ Future main() async {
|
|||
),
|
||||
);
|
||||
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.transparent,
|
||||
statusBarColor: Colors.transparent,
|
||||
));
|
||||
await SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:tsacdop/util/extension_helper.dart';
|
||||
|
||||
class CustomTabView extends StatefulWidget {
|
||||
final int itemCount;
|
||||
|
@ -108,7 +109,7 @@ class _CustomTabsState extends State<CustomTabView>
|
|||
unselectedLabelColor: Colors.grey[700],
|
||||
indicator: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(15)),
|
||||
color: Theme.of(context).accentColor,
|
||||
color: context.accentColor,
|
||||
),
|
||||
tabs: List.generate(
|
||||
widget.itemCount,
|
||||
|
|
|
@ -122,12 +122,12 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
);
|
||||
}
|
||||
if (result > 0) {
|
||||
var autoDownload = await _dbHelper.getAutoDownload(podcastLocal.id);
|
||||
final autoDownload = await _dbHelper.getAutoDownload(podcastLocal.id);
|
||||
if (autoDownload) {
|
||||
var downloader = Provider.of<DownloadState>(context, listen: false);
|
||||
var result = await Connectivity().checkConnectivity();
|
||||
var autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
|
||||
var autoDownloadNetwork = await autoDownloadStorage.getInt();
|
||||
final downloader = Provider.of<DownloadState>(context, listen: false);
|
||||
final result = await Connectivity().checkConnectivity();
|
||||
final autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
|
||||
final autoDownloadNetwork = await autoDownloadStorage.getInt();
|
||||
if (autoDownloadNetwork == 1) {
|
||||
var episodes = await _dbHelper.getNewEpisodes(podcastLocal.id);
|
||||
// For safety
|
||||
|
@ -232,31 +232,32 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
VoidCallback? onTap,
|
||||
required Color backgroundColor}) {
|
||||
return Container(
|
||||
padding: EdgeInsets.fromLTRB(5, 10, 5, 0),
|
||||
width: 60.0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
onTap: onTap,
|
||||
child: CircleAvatar(
|
||||
radius: 20,
|
||||
child: child,
|
||||
backgroundColor: backgroundColor.withOpacity(0.5),
|
||||
),
|
||||
padding: EdgeInsets.fromLTRB(5, 10, 5, 0),
|
||||
width: 60.0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
onTap: onTap,
|
||||
child: CircleAvatar(
|
||||
radius: 20,
|
||||
child: child,
|
||||
backgroundColor: backgroundColor.withOpacity(0.5),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
style: context.textTheme.subtitle2,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
],
|
||||
));
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
style: context.textTheme.subtitle2,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _hostsList(BuildContext context, PodcastLocal podcastLocal) {
|
||||
|
@ -673,11 +674,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: color,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
systemNavigationBarColor: context.primaryColor,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
statusBarColor: color, statusBarIconBrightness: Brightness.light),
|
||||
child: WillPopScope(
|
||||
onWillPop: () {
|
||||
if (_playerKey.currentState != null &&
|
||||
|
@ -690,7 +687,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
},
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: RefreshIndicator(
|
||||
key: _refreshIndicatorKey,
|
||||
displacement: context.paddingTop + 40,
|
||||
|
@ -751,6 +747,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||
),
|
||||
],
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.white,
|
||||
),
|
||||
|
@ -1021,17 +1018,6 @@ class AboutPodcast extends StatefulWidget {
|
|||
class _AboutPodcastState extends State<AboutPodcast> {
|
||||
late String _description;
|
||||
late bool _load;
|
||||
void getDescription(String? id) async {
|
||||
var dbHelper = DBHelper();
|
||||
var description = await dbHelper.getFeedDescription(id);
|
||||
if (description == null || description.isEmpty) {
|
||||
_description = '';
|
||||
} else {
|
||||
var doc = parse(description);
|
||||
_description = parse(doc.body!.text).documentElement!.text;
|
||||
}
|
||||
if (mounted) setState(() => _load = true);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -1042,75 +1028,30 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return !_load
|
||||
? Center()
|
||||
: Linkify(
|
||||
text: _description,
|
||||
onOpen: (link) {
|
||||
link.url!.launchUrl;
|
||||
},
|
||||
linkStyle: TextStyle(
|
||||
color: Theme.of(context).accentColor,
|
||||
decoration: TextDecoration.underline,
|
||||
textBaseline: TextBaseline.ideographic),
|
||||
);
|
||||
// LayoutBuilder(
|
||||
// builder: (context, size) {
|
||||
// final span = TextSpan(text: _description);
|
||||
// final tp = TextPainter(
|
||||
// text: span, maxLines: 3, textDirection: TextDirection.ltr);
|
||||
// tp.layout(maxWidth: size.maxWidth);
|
||||
if (_load)
|
||||
return Linkify(
|
||||
text: _description,
|
||||
onOpen: (link) {
|
||||
link.url!.launchUrl;
|
||||
},
|
||||
linkStyle: TextStyle(
|
||||
color: context.accentColor,
|
||||
decoration: TextDecoration.underline,
|
||||
textBaseline: TextBaseline.ideographic),
|
||||
);
|
||||
return Center();
|
||||
}
|
||||
|
||||
// if (tp.didExceedMaxLines) {
|
||||
// return GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() => _expand = !_expand);
|
||||
// },
|
||||
// child: !_expand
|
||||
// ? Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: <Widget>[
|
||||
// Linkify(
|
||||
// onOpen: (link) {
|
||||
// link.url.launchUrl;
|
||||
// },
|
||||
// text: _description,
|
||||
// linkStyle: TextStyle(
|
||||
// color: Theme.of(context).accentColor,
|
||||
// decoration: TextDecoration.underline,
|
||||
// textBaseline: TextBaseline.ideographic),
|
||||
// maxLines: 3,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
// : Linkify(
|
||||
// onOpen: (link) {
|
||||
// link.url.launchUrl;
|
||||
// },
|
||||
// text: _description,
|
||||
// linkStyle: TextStyle(
|
||||
// color: Theme.of(context).accentColor,
|
||||
// decoration: TextDecoration.underline,
|
||||
// textBaseline: TextBaseline.ideographic),
|
||||
// ),
|
||||
// );
|
||||
// } else {
|
||||
// return Linkify(
|
||||
// text: _description,
|
||||
// onOpen: (link) {
|
||||
// link.url.launchUrl;
|
||||
// },
|
||||
// linkStyle: TextStyle(
|
||||
// color: Theme.of(context).accentColor,
|
||||
// decoration: TextDecoration.underline,
|
||||
// textBaseline: TextBaseline.ideographic),
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
void getDescription(String? id) async {
|
||||
final dbHelper = DBHelper();
|
||||
final description = await dbHelper.getFeedDescription(id);
|
||||
if (description == null || description.isEmpty) {
|
||||
_description = '';
|
||||
} else {
|
||||
final doc = parse(description);
|
||||
_description = parse(doc.body!.text).documentElement!.text;
|
||||
}
|
||||
if (mounted) setState(() => _load = true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1156,8 +1097,7 @@ class _SearchEpisodeState extends State<SearchEpisode> {
|
|||
titlePadding: const EdgeInsets.all(20),
|
||||
actionsPadding: EdgeInsets.zero,
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
s.cancel,
|
||||
|
@ -1165,8 +1105,7 @@ class _SearchEpisodeState extends State<SearchEpisode> {
|
|||
style: TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if ((_query ?? '').isNotEmpty) {
|
||||
widget.onSearch!(_query);
|
||||
|
|
|
@ -85,45 +85,6 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||
int? _seconds;
|
||||
int? _skipSeconds;
|
||||
|
||||
Future<int?> _getSkipSecond(String? id) async {
|
||||
var dbHelper = DBHelper();
|
||||
var seconds = await dbHelper.getSkipSecondsStart(id);
|
||||
_skipSeconds = seconds;
|
||||
return seconds;
|
||||
}
|
||||
|
||||
_saveSkipSeconds(String? id, int? seconds) async {
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.saveSkipSecondsStart(id, seconds);
|
||||
}
|
||||
|
||||
_setAutoDownload(String? id, bool boo) async {
|
||||
var permission = await _checkPermmison();
|
||||
if (permission) {
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.saveAutoDownload(id, boo: boo);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownload(String? id) async {
|
||||
var dbHelper = DBHelper();
|
||||
return await dbHelper.getAutoDownload(id);
|
||||
}
|
||||
|
||||
Future<bool> _checkPermmison() async {
|
||||
var permission = await Permission.storage.status;
|
||||
if (permission != PermissionStatus.granted) {
|
||||
var permissions = await [Permission.storage].request();
|
||||
if (permissions[Permission.storage] == PermissionStatus.granted) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -134,43 +95,20 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||
_controller =
|
||||
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
|
||||
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller)
|
||||
..addListener(() {
|
||||
setState(() {
|
||||
_value = _animation.value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buttonOnMenu(
|
||||
{required Widget icon,
|
||||
VoidCallback? onTap,
|
||||
required String tooltip}) =>
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: icon,
|
||||
),
|
||||
Text(tooltip, style: context.textTheme.subtitle2),
|
||||
],
|
||||
)),
|
||||
),
|
||||
..addListener(
|
||||
() {
|
||||
setState(() {
|
||||
_value = _animation.value;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = widget.podcastLocal!.backgroudColor(context);
|
||||
final s = context.s;
|
||||
var groupList = context.watch<GroupList>();
|
||||
final groupList = context.watch<GroupList>();
|
||||
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -398,9 +336,7 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||
_seconds = value.inSeconds,
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor: context.accentColor
|
||||
.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_seconds = 0;
|
||||
|
@ -411,9 +347,7 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||
color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: context.accentColor
|
||||
.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_saveSkipSeconds(
|
||||
|
@ -431,44 +365,42 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||
});
|
||||
}),
|
||||
_buttonOnMenu(
|
||||
icon: Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
size: _value == 0 ? 1 : 20 * _value!,
|
||||
),
|
||||
tooltip: s.remove,
|
||||
onTap: () {
|
||||
generalDialog(
|
||||
context,
|
||||
title: Text(s.removeConfirm),
|
||||
content: Text(s.removePodcastDes),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor:
|
||||
context.accentColor.withAlpha(70),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
s.cancel,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600]),
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
size: _value == 0 ? 1 : 20 * _value!,
|
||||
),
|
||||
tooltip: s.remove,
|
||||
onTap: () {
|
||||
generalDialog(
|
||||
context,
|
||||
title: Text(s.removeConfirm),
|
||||
content: Text(s.removePodcastDes),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
s.cancel,
|
||||
style:
|
||||
TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: Colors.red.withAlpha(70),
|
||||
onPressed: () {
|
||||
groupList.removePodcast(
|
||||
widget.podcastLocal!);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
s.confirm,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
groupList.removePodcast(
|
||||
widget.podcastLocal!);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
s.confirm,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -477,6 +409,71 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buttonOnMenu(
|
||||
{required Widget icon,
|
||||
VoidCallback? onTap,
|
||||
required String tooltip}) =>
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: icon,
|
||||
),
|
||||
Text(tooltip, style: context.textTheme.subtitle2),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Future<int?> _getSkipSecond(String? id) async {
|
||||
final dbHelper = DBHelper();
|
||||
final seconds = await dbHelper.getSkipSecondsStart(id);
|
||||
_skipSeconds = seconds;
|
||||
return seconds;
|
||||
}
|
||||
|
||||
_saveSkipSeconds(String? id, int? seconds) async {
|
||||
final dbHelper = DBHelper();
|
||||
await dbHelper.saveSkipSecondsStart(id, seconds);
|
||||
}
|
||||
|
||||
_setAutoDownload(String? id, bool boo) async {
|
||||
final permission = await _checkPermmison();
|
||||
if (permission) {
|
||||
final dbHelper = DBHelper();
|
||||
await dbHelper.saveAutoDownload(id, boo: boo);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownload(String? id) async {
|
||||
final dbHelper = DBHelper();
|
||||
return await dbHelper.getAutoDownload(id);
|
||||
}
|
||||
|
||||
Future<bool> _checkPermmison() async {
|
||||
final permission = await Permission.storage.status;
|
||||
if (permission != PermissionStatus.granted) {
|
||||
final permissions = await [Permission.storage].request();
|
||||
if (permissions[Permission.storage] == PermissionStatus.granted) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RenameGroup extends StatefulWidget {
|
||||
|
@ -511,7 +508,7 @@ class _RenameGroupState extends State<RenameGroup> {
|
|||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
statusBarColor: Colors.transparent,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
|
@ -519,27 +516,28 @@ class _RenameGroupState extends State<RenameGroup> {
|
|||
),
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding: EdgeInsets.all(20),
|
||||
actionsPadding: EdgeInsets.zero,
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
s.cancel,
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
if (list.contains(_newName)) {
|
||||
setState(() => _error = 1);
|
||||
} else {
|
||||
var newGroup = PodcastGroup(_newName,
|
||||
final newGroup = PodcastGroup(_newName,
|
||||
color: widget.group!.color,
|
||||
id: widget.group!.id,
|
||||
podcastList: widget.group!.podcastList);
|
||||
|
@ -547,12 +545,16 @@ class _RenameGroupState extends State<RenameGroup> {
|
|||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Text(s.confirm,
|
||||
style: TextStyle(color: Theme.of(context).accentColor)),
|
||||
child: Text(
|
||||
s.confirm,
|
||||
style: TextStyle(color: context.accentColor),
|
||||
),
|
||||
)
|
||||
],
|
||||
title:
|
||||
SizedBox(width: context.width - 160, child: Text(s.editGroupName)),
|
||||
title: SizedBox(
|
||||
width: context.width - 160,
|
||||
child: Text(s.editGroupName),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
|
@ -578,7 +580,7 @@ class _RenameGroupState extends State<RenameGroup> {
|
|||
_newName = value;
|
||||
},
|
||||
),
|
||||
Container(
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: (_error == 1)
|
||||
? Text(
|
||||
|
|
|
@ -42,7 +42,7 @@ class _PodcastManageState extends State<PodcastManage>
|
|||
_menuValue = 0;
|
||||
_index = 0;
|
||||
_menuController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 150));
|
||||
vsync: this, duration: const Duration(milliseconds: 300));
|
||||
_controller = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 500));
|
||||
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
|
||||
|
@ -51,8 +51,8 @@ class _PodcastManageState extends State<PodcastManage>
|
|||
setState(() => _fraction = _animation.value);
|
||||
}
|
||||
});
|
||||
_menuAnimation = Tween(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _menuController, curve: Curves.elasticInOut))
|
||||
_menuAnimation = Tween(begin: 0.0, end: 1.0)
|
||||
.animate(CurvedAnimation(parent: _menuController, curve: Curves.ease))
|
||||
..addListener(() {
|
||||
if (mounted) setState(() => _menuValue = _menuAnimation.value);
|
||||
});
|
||||
|
@ -77,6 +77,274 @@ class _PodcastManageState extends State<PodcastManage>
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: context.priamryContainer,
|
||||
systemNavigationBarColor: context.background,
|
||||
),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: Text(context.s.groups(2)),
|
||||
leading: CustomBackButton(),
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: context.priamryContainer,
|
||||
),
|
||||
actions: <Widget>[
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: addGroupFeature,
|
||||
tapTarget: Icon(Icons.add),
|
||||
title: s.featureDiscoveryGroup,
|
||||
backgroundColor: Colors.cyan[600],
|
||||
description: s.featureDiscoveryGroupDes,
|
||||
buttonColor: Colors.cyan[500],
|
||||
child: IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () => showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 200),
|
||||
pageBuilder: (context, animaiton, secondaryAnimation) =>
|
||||
AddGroup()),
|
||||
icon: Icon(Icons.add_circle_outline)),
|
||||
),
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openAllPodcastDefalt,
|
||||
builder: (_, data, __) {
|
||||
return !data!
|
||||
? IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () => Navigator.push(
|
||||
context, ScaleRoute(page: PodcastList())),
|
||||
icon: Icon(Icons.all_out))
|
||||
: Center();
|
||||
})
|
||||
// _OrderMenu(),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: WillPopScope(
|
||||
onWillPop: () async {
|
||||
context.read<GroupList>().clearOrderChanged();
|
||||
return true;
|
||||
},
|
||||
child: Consumer<GroupList>(
|
||||
builder: (_, groupList, __) {
|
||||
// var _isLoading = groupList.isLoading;
|
||||
final _groups = groupList.groups;
|
||||
if (_groups.isEmpty) return Center();
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
ColoredBox(
|
||||
color: context.priamryContainer,
|
||||
child: CustomTabView(
|
||||
itemCount: _groups.length,
|
||||
tabBuilder: (context, index) => Tab(
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Text(
|
||||
_groups[index]!.name!,
|
||||
),
|
||||
),
|
||||
),
|
||||
pageBuilder: (context, index) =>
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: configurePodcast,
|
||||
tapTarget: Text(s.podcast(1)),
|
||||
title: s.featureDiscoveryGroupPodcast,
|
||||
backgroundColor: Colors.cyan[600],
|
||||
buttonColor: Colors.cyan[500],
|
||||
description: s.featureDiscoveryGroupPodcastDes,
|
||||
child: PodcastGroupList(
|
||||
group: _groups[index],
|
||||
key: ValueKey<String?>(_groups[index]!.name),
|
||||
),
|
||||
),
|
||||
onPositionChange: (value) =>
|
||||
// setState(() =>
|
||||
_index = value,
|
||||
),
|
||||
),
|
||||
if (_showSetting)
|
||||
Positioned.fill(
|
||||
top: 50,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await _menuController.reverse();
|
||||
if (mounted) {
|
||||
setState(() => _showSetting = false);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: context.background.withOpacity(
|
||||
0.8 * math.min(_menuController.value * 2, 1.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 30,
|
||||
bottom: 30,
|
||||
child: _saveButton(),
|
||||
),
|
||||
if (_showSetting)
|
||||
Positioned(
|
||||
right: 100 * _menuValue - 70,
|
||||
bottom: 100,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_menuController.reverse();
|
||||
setState(() => _showSetting = false);
|
||||
_index == 0
|
||||
? Fluttertoast.showToast(
|
||||
msg: s.toastHomeGroupNotSupport,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
)
|
||||
: showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration:
|
||||
const Duration(milliseconds: 300),
|
||||
pageBuilder: (context, animaiton,
|
||||
secondaryAnimation) =>
|
||||
RenameGroup(
|
||||
group: _groups[_index!],
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: 30.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[700],
|
||||
borderRadius:
|
||||
BorderRadius.circular(10.0)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.text_fields,
|
||||
color: Colors.white,
|
||||
size: 15.0,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text(context.s.editGroupName,
|
||||
style:
|
||||
TextStyle(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_menuController.reverse();
|
||||
setState(() => _showSetting = false);
|
||||
_index == 0
|
||||
? Fluttertoast.showToast(
|
||||
msg: s.toastHomeGroupNotSupport,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
)
|
||||
: generalDialog(
|
||||
context,
|
||||
title: Text(s.removeConfirm),
|
||||
content: Text(s.groupRemoveConfirm),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
context.s.cancel,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (_index ==
|
||||
groupList.groups.length -
|
||||
1) {
|
||||
setState(() {
|
||||
_index = _index! - 1;
|
||||
});
|
||||
groupList.delGroup(
|
||||
_groups[_index! + 1]!);
|
||||
} else {
|
||||
groupList.delGroup(
|
||||
_groups[_index!]!);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
context.s.confirm,
|
||||
style: TextStyle(
|
||||
color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[700],
|
||||
borderRadius:
|
||||
BorderRadius.circular(10.0)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
size: 15.0,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.remove,
|
||||
style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _saveButton() {
|
||||
final s = context.s;
|
||||
return Consumer<GroupList>(
|
||||
|
@ -150,288 +418,6 @@ class _PodcastManageState extends State<PodcastManage>
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: context.brightness,
|
||||
systemNavigationBarColor: context.primaryColor,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
// statusBarColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.s.groups(2)),
|
||||
leading: CustomBackButton(),
|
||||
actions: <Widget>[
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: addGroupFeature,
|
||||
tapTarget: Icon(Icons.add),
|
||||
title: s.featureDiscoveryGroup,
|
||||
backgroundColor: Colors.cyan[600],
|
||||
description: s.featureDiscoveryGroupDes,
|
||||
buttonColor: Colors.cyan[500],
|
||||
child: IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () => showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 200),
|
||||
pageBuilder: (context, animaiton, secondaryAnimation) =>
|
||||
AddGroup()),
|
||||
icon: Icon(Icons.add_circle_outline)),
|
||||
),
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openAllPodcastDefalt,
|
||||
builder: (_, data, __) {
|
||||
return !data!
|
||||
? IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () => Navigator.push(
|
||||
context, ScaleRoute(page: PodcastList())),
|
||||
icon: Icon(Icons.all_out))
|
||||
: Center();
|
||||
})
|
||||
// _OrderMenu(),
|
||||
],
|
||||
),
|
||||
body: WillPopScope(
|
||||
onWillPop: () async {
|
||||
context.read<GroupList>().clearOrderChanged();
|
||||
return true;
|
||||
},
|
||||
child: Consumer<GroupList>(
|
||||
builder: (_, groupList, __) {
|
||||
// var _isLoading = groupList.isLoading;
|
||||
var _groups = groupList.groups;
|
||||
return _groups.isEmpty
|
||||
? Center()
|
||||
: Stack(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
color: context.background,
|
||||
child: CustomTabView(
|
||||
itemCount: _groups.length,
|
||||
tabBuilder: (context, index) => Tab(
|
||||
child: Container(
|
||||
height: 30.0,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 10.0),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
// color: Colors.grey[600].withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Text(
|
||||
_groups[index]!.name!,
|
||||
)),
|
||||
),
|
||||
pageBuilder: (context, index) =>
|
||||
featureDiscoveryOverlay(
|
||||
context,
|
||||
featureId: configurePodcast,
|
||||
tapTarget: Text(s.podcast(1)),
|
||||
title: s.featureDiscoveryGroupPodcast,
|
||||
backgroundColor: Colors.cyan[600],
|
||||
buttonColor: Colors.cyan[500],
|
||||
description: s.featureDiscoveryGroupPodcastDes,
|
||||
child: PodcastGroupList(
|
||||
group: _groups[index],
|
||||
key: ValueKey<String?>(_groups[index]!.name),
|
||||
),
|
||||
),
|
||||
onPositionChange: (value) =>
|
||||
// setState(() =>
|
||||
_index = value,
|
||||
),
|
||||
),
|
||||
if (_showSetting)
|
||||
Positioned.fill(
|
||||
top: 50,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await _menuController.reverse();
|
||||
if (mounted) {
|
||||
setState(() => _showSetting = false);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: context.background.withOpacity(0.8 *
|
||||
math.min(_menuController.value * 2, 1.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 30,
|
||||
bottom: 30,
|
||||
child: _saveButton(),
|
||||
),
|
||||
if (_showSetting)
|
||||
Positioned(
|
||||
right: 100 * _menuValue - 70,
|
||||
bottom: 100,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_menuController.reverse();
|
||||
setState(() => _showSetting = false);
|
||||
_index == 0
|
||||
? Fluttertoast.showToast(
|
||||
msg: s.toastHomeGroupNotSupport,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
)
|
||||
: showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(
|
||||
context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration:
|
||||
const Duration(
|
||||
milliseconds: 300),
|
||||
pageBuilder: (context, animaiton,
|
||||
secondaryAnimation) =>
|
||||
RenameGroup(
|
||||
group: _groups[_index!],
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: 30.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[700],
|
||||
borderRadius:
|
||||
BorderRadius.circular(10.0)),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.text_fields,
|
||||
color: Colors.white,
|
||||
size: 15.0,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text(context.s.editGroupName,
|
||||
style: TextStyle(
|
||||
color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_menuController.reverse();
|
||||
setState(() => _showSetting = false);
|
||||
_index == 0
|
||||
? Fluttertoast.showToast(
|
||||
msg: s.toastHomeGroupNotSupport,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
)
|
||||
: generalDialog(
|
||||
context,
|
||||
title: Text(s.removeConfirm),
|
||||
content:
|
||||
Text(s.groupRemoveConfirm),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor: context
|
||||
.accentColor
|
||||
.withAlpha(70),
|
||||
onPressed: () =>
|
||||
Navigator.of(context)
|
||||
.pop(),
|
||||
child: Text(
|
||||
context.s.cancel,
|
||||
style: TextStyle(
|
||||
color:
|
||||
Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: context
|
||||
.accentColor
|
||||
.withAlpha(70),
|
||||
onPressed: () {
|
||||
if (_index ==
|
||||
groupList
|
||||
.groups.length -
|
||||
1) {
|
||||
setState(() {
|
||||
_index = _index! - 1;
|
||||
});
|
||||
groupList.delGroup(
|
||||
_groups[
|
||||
_index! + 1]!);
|
||||
} else {
|
||||
groupList.delGroup(
|
||||
_groups[_index!]!);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
context.s.confirm,
|
||||
style: TextStyle(
|
||||
color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[700],
|
||||
borderRadius:
|
||||
BorderRadius.circular(10.0)),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
size: 15.0,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.remove,
|
||||
style:
|
||||
TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OrderMenu extends StatelessWidget {
|
||||
|
@ -495,78 +481,81 @@ class _AddGroupState extends State<AddGroup> {
|
|||
List list = groupList.groups.map((e) => e!.name).toList();
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
statusBarColor: Colors.transparent,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
: Color.fromRGBO(5, 5, 5, 1),
|
||||
),
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding: EdgeInsets.all(20),
|
||||
actionsPadding: EdgeInsets.zero,
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
s.cancel,
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
onPressed: () async {
|
||||
if (list.contains(_newGroup)) {
|
||||
setState(() => _error = 1);
|
||||
} else {
|
||||
groupList.addGroup(PodcastGroup(_newGroup));
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child:
|
||||
Text(s.confirm, style: TextStyle(color: context.accentColor)),
|
||||
)
|
||||
],
|
||||
title: SizedBox(width: context.width - 160, child: Text(s.newGroup)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
hintText: s.newGroup,
|
||||
hintStyle: TextStyle(fontSize: 18),
|
||||
filled: true,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: context.accentColor, width: 2.0),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: context.accentColor, width: 2.0),
|
||||
),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding: EdgeInsets.all(20),
|
||||
actionsPadding: EdgeInsets.zero,
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
s.cancel,
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
cursorRadius: Radius.circular(2),
|
||||
autofocus: true,
|
||||
maxLines: 1,
|
||||
controller: _controller,
|
||||
onChanged: (value) {
|
||||
_newGroup = value;
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
if (list.contains(_newGroup)) {
|
||||
setState(() => _error = 1);
|
||||
} else {
|
||||
groupList.addGroup(PodcastGroup(_newGroup));
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: (_error == 1)
|
||||
? Text(
|
||||
s.groupExisted,
|
||||
style: TextStyle(color: Colors.red[400]),
|
||||
)
|
||||
: Center(),
|
||||
),
|
||||
child:
|
||||
Text(s.confirm, style: TextStyle(color: context.accentColor)),
|
||||
)
|
||||
],
|
||||
title: SizedBox(width: context.width - 160, child: Text(s.newGroup)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
hintText: s.newGroup,
|
||||
hintStyle: TextStyle(fontSize: 18),
|
||||
filled: true,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: context.accentColor, width: 2.0),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: context.accentColor, width: 2.0),
|
||||
),
|
||||
),
|
||||
cursorRadius: Radius.circular(2),
|
||||
autofocus: true,
|
||||
maxLines: 1,
|
||||
controller: _controller,
|
||||
onChanged: (value) {
|
||||
_newGroup = value;
|
||||
},
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: (_error == 1)
|
||||
? Text(
|
||||
s.groupExisted,
|
||||
style: TextStyle(color: Colors.red[400]),
|
||||
)
|
||||
: Center(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -52,153 +52,6 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
_showEndTimePicker = false;
|
||||
}
|
||||
|
||||
Future<void> _setAutoDownload(bool boo) async {
|
||||
var permission = await _checkPermmison();
|
||||
if (permission) {
|
||||
await _dbHelper.saveAutoDownload(widget.podcastLocal!.id, boo: boo);
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _setNeverUpdate(bool boo) async {
|
||||
await _dbHelper.saveNeverUpdate(widget.podcastLocal!.id, boo: boo);
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _setHideNewMark(bool boo) async {
|
||||
await _dbHelper.saveHideNewMark(widget.podcastLocal!.id, boo: boo);
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _saveSkipSecondsStart(int? seconds) async {
|
||||
await _dbHelper.saveSkipSecondsStart(widget.podcastLocal!.id, seconds);
|
||||
}
|
||||
|
||||
Future<void> _saveSkipSecondsEnd(int seconds) async {
|
||||
await _dbHelper.saveSkipSecondsEnd(widget.podcastLocal!.id, seconds);
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownload(String? id) async {
|
||||
return await _dbHelper.getAutoDownload(id);
|
||||
}
|
||||
|
||||
Future<bool> _getNeverUpdate(String? id) async {
|
||||
return await _dbHelper.getNeverUpdate(id);
|
||||
}
|
||||
|
||||
Future<bool> _getHideNewMark(String? id) async {
|
||||
return await _dbHelper.getHideNewMark(id);
|
||||
}
|
||||
|
||||
Future<int?> _getSkipSecondStart(String? id) async {
|
||||
return await _dbHelper.getSkipSecondsStart(id);
|
||||
}
|
||||
|
||||
Future<int?> _getSkipSecondEnd(String id) async {
|
||||
return await _dbHelper.getSkipSecondsEnd(id);
|
||||
}
|
||||
|
||||
Future<void> _markListened(String? podcastId) async {
|
||||
setState(() {
|
||||
_markStatus = MarkStatus.start;
|
||||
});
|
||||
final episodes = await _dbHelper.getRssItem(podcastId, -1,
|
||||
reverse: true, hideListened: true);
|
||||
for (var episode in episodes) {
|
||||
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
|
||||
await _dbHelper.saveHistory(history);
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_markStatus = MarkStatus.complete;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _refreshArtWork() async {
|
||||
setState(() => _coverStatus = RefreshCoverStatus.start);
|
||||
var options = BaseOptions(
|
||||
connectTimeout: 30000,
|
||||
receiveTimeout: 90000,
|
||||
);
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
var filePath = "${dir.path}/${widget.podcastLocal!.id}.png";
|
||||
var dio = Dio(options);
|
||||
String? imageUrl;
|
||||
|
||||
try {
|
||||
var response = await dio.get(widget.podcastLocal!.rssUrl);
|
||||
try {
|
||||
var p = RssFeed.parse(response.data);
|
||||
imageUrl = p.itunes!.image!.href ?? p.image!.url;
|
||||
} catch (e) {
|
||||
developer.log(e.toString());
|
||||
if (mounted) setState(() => _coverStatus = RefreshCoverStatus.error);
|
||||
}
|
||||
} catch (e) {
|
||||
developer.log(e.toString());
|
||||
if (mounted) setState(() => _coverStatus = RefreshCoverStatus.error);
|
||||
}
|
||||
if (imageUrl != null && imageUrl.contains('http')) {
|
||||
try {
|
||||
img.Image thumbnail;
|
||||
var imageResponse = await dio.get<List<int>>(imageUrl,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
));
|
||||
var image = img.decodeImage(imageResponse.data!)!;
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
if (thumbnail != null) {
|
||||
File(filePath)..writeAsBytesSync(img.encodePng(thumbnail));
|
||||
_dbHelper.updatePodcastImage(
|
||||
id: widget.podcastLocal!.id, filePath: filePath);
|
||||
print('saved image');
|
||||
if (mounted) {
|
||||
setState(() => _coverStatus = RefreshCoverStatus.complete);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
developer.log(e.toString());
|
||||
if (mounted) setState(() => _coverStatus = RefreshCoverStatus.error);
|
||||
}
|
||||
} else if (_coverStatus == RefreshCoverStatus.start && mounted) {
|
||||
setState(() => _coverStatus = RefreshCoverStatus.complete);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _checkPermmison() async {
|
||||
var permission = await Permission.storage.status;
|
||||
if (permission != PermissionStatus.granted) {
|
||||
var permissions = await [Permission.storage].request();
|
||||
if (permissions[Permission.storage] == PermissionStatus.granted) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _getRefreshStatusIcon(RefreshCoverStatus status) {
|
||||
switch (status) {
|
||||
case RefreshCoverStatus.none:
|
||||
return Center();
|
||||
break;
|
||||
case RefreshCoverStatus.start:
|
||||
return CircularProgressIndicator(strokeWidth: 2);
|
||||
break;
|
||||
case RefreshCoverStatus.complete:
|
||||
return Icon(Icons.done);
|
||||
break;
|
||||
case RefreshCoverStatus.error:
|
||||
return Icon(Icons.refresh, color: Colors.red);
|
||||
break;
|
||||
default:
|
||||
return Center();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
|
@ -341,42 +194,43 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
child: _getRefreshStatusIcon(_coverStatus)))),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_removeConfirm = false;
|
||||
_showStartTimePicker = false;
|
||||
_showEndTimePicker = false;
|
||||
_markConfirm = !_markConfirm;
|
||||
});
|
||||
},
|
||||
dense: true,
|
||||
title: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 18,
|
||||
width: 18,
|
||||
child: CustomPaint(
|
||||
painter: ListenedAllPainter(context.accentColor, stroke: 2),
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_removeConfirm = false;
|
||||
_showStartTimePicker = false;
|
||||
_showEndTimePicker = false;
|
||||
_markConfirm = !_markConfirm;
|
||||
});
|
||||
},
|
||||
dense: true,
|
||||
title: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 18,
|
||||
width: 18,
|
||||
child: CustomPaint(
|
||||
painter: ListenedAllPainter(context.accentColor, stroke: 2),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Text(s.menuMarkAllListened,
|
||||
style: textStyle.copyWith(
|
||||
color: context.accentColor,
|
||||
fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Text(s.menuMarkAllListened,
|
||||
style: textStyle.copyWith(
|
||||
color: context.accentColor, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
trailing: Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: _markStatus == MarkStatus.none
|
||||
? Center()
|
||||
: _markStatus == MarkStatus.start
|
||||
? CircularProgressIndicator(strokeWidth: 2)
|
||||
: Icon(Icons.done),
|
||||
),
|
||||
trailing: Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: _markStatus == MarkStatus.none
|
||||
? Center()
|
||||
: _markStatus == MarkStatus.start
|
||||
? CircularProgressIndicator(strokeWidth: 2)
|
||||
: Icon(Icons.done)),
|
||||
)),
|
||||
),
|
||||
),
|
||||
if (_markConfirm)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
|
@ -384,7 +238,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
FlatButton(
|
||||
TextButton(
|
||||
onPressed: () => setState(() {
|
||||
_markConfirm = false;
|
||||
}),
|
||||
|
@ -392,7 +246,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
s.cancel,
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
)),
|
||||
FlatButton(
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (_markStatus != MarkStatus.start) {
|
||||
_markListened(widget.podcastLocal!.id);
|
||||
|
@ -402,7 +256,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
});
|
||||
},
|
||||
child: Text(s.confirm,
|
||||
style: TextStyle(color: context.accentColor))),
|
||||
style: TextStyle(color: context.error))),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -433,15 +287,14 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
FlatButton(
|
||||
TextButton(
|
||||
onPressed: () => setState(() {
|
||||
_removeConfirm = false;
|
||||
}),
|
||||
child:
|
||||
Text(s.cancel, style: TextStyle(color: Colors.grey[600])),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: Colors.red.withAlpha(70),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await groupList.removePodcast(widget.podcastLocal!);
|
||||
Navigator.of(context).pop();
|
||||
|
@ -454,6 +307,147 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _setAutoDownload(bool boo) async {
|
||||
var permission = await _checkPermmison();
|
||||
if (permission) {
|
||||
await _dbHelper.saveAutoDownload(widget.podcastLocal!.id, boo: boo);
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _setNeverUpdate(bool boo) async {
|
||||
await _dbHelper.saveNeverUpdate(widget.podcastLocal!.id, boo: boo);
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _setHideNewMark(bool boo) async {
|
||||
await _dbHelper.saveHideNewMark(widget.podcastLocal!.id, boo: boo);
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _saveSkipSecondsStart(int? seconds) async {
|
||||
await _dbHelper.saveSkipSecondsStart(widget.podcastLocal!.id, seconds);
|
||||
}
|
||||
|
||||
Future<void> _saveSkipSecondsEnd(int seconds) async {
|
||||
await _dbHelper.saveSkipSecondsEnd(widget.podcastLocal!.id, seconds);
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownload(String? id) async {
|
||||
return await _dbHelper.getAutoDownload(id);
|
||||
}
|
||||
|
||||
Future<bool> _getNeverUpdate(String? id) async {
|
||||
return await _dbHelper.getNeverUpdate(id);
|
||||
}
|
||||
|
||||
Future<bool> _getHideNewMark(String? id) async {
|
||||
return await _dbHelper.getHideNewMark(id);
|
||||
}
|
||||
|
||||
Future<int?> _getSkipSecondStart(String? id) async {
|
||||
return await _dbHelper.getSkipSecondsStart(id);
|
||||
}
|
||||
|
||||
Future<int?> _getSkipSecondEnd(String id) async {
|
||||
return await _dbHelper.getSkipSecondsEnd(id);
|
||||
}
|
||||
|
||||
Future<void> _markListened(String? podcastId) async {
|
||||
setState(() {
|
||||
_markStatus = MarkStatus.start;
|
||||
});
|
||||
final episodes = await _dbHelper.getRssItem(podcastId, -1,
|
||||
reverse: true, hideListened: true);
|
||||
for (var episode in episodes) {
|
||||
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
|
||||
await _dbHelper.saveHistory(history);
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_markStatus = MarkStatus.complete;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _refreshArtWork() async {
|
||||
setState(() => _coverStatus = RefreshCoverStatus.start);
|
||||
var options = BaseOptions(
|
||||
connectTimeout: 30000,
|
||||
receiveTimeout: 90000,
|
||||
);
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
var filePath = "${dir.path}/${widget.podcastLocal!.id}.png";
|
||||
var dio = Dio(options);
|
||||
String? imageUrl;
|
||||
|
||||
try {
|
||||
var response = await dio.get(widget.podcastLocal!.rssUrl);
|
||||
try {
|
||||
var p = RssFeed.parse(response.data);
|
||||
imageUrl = p.itunes!.image!.href ?? p.image!.url;
|
||||
} catch (e) {
|
||||
developer.log(e.toString());
|
||||
if (mounted) setState(() => _coverStatus = RefreshCoverStatus.error);
|
||||
}
|
||||
} catch (e) {
|
||||
developer.log(e.toString());
|
||||
if (mounted) setState(() => _coverStatus = RefreshCoverStatus.error);
|
||||
}
|
||||
if (imageUrl != null && imageUrl.contains('http')) {
|
||||
try {
|
||||
img.Image thumbnail;
|
||||
var imageResponse = await dio.get<List<int>>(imageUrl,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
));
|
||||
var image = img.decodeImage(imageResponse.data!)!;
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
File(filePath)..writeAsBytesSync(img.encodePng(thumbnail));
|
||||
_dbHelper.updatePodcastImage(
|
||||
id: widget.podcastLocal!.id, filePath: filePath);
|
||||
print('saved image');
|
||||
if (mounted) {
|
||||
setState(() => _coverStatus = RefreshCoverStatus.complete);
|
||||
}
|
||||
} catch (e) {
|
||||
developer.log(e.toString());
|
||||
if (mounted) setState(() => _coverStatus = RefreshCoverStatus.error);
|
||||
}
|
||||
} else if (_coverStatus == RefreshCoverStatus.start && mounted) {
|
||||
setState(() => _coverStatus = RefreshCoverStatus.complete);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _checkPermmison() async {
|
||||
var permission = await Permission.storage.status;
|
||||
if (permission != PermissionStatus.granted) {
|
||||
var permissions = await [Permission.storage].request();
|
||||
if (permissions[Permission.storage] == PermissionStatus.granted) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _getRefreshStatusIcon(RefreshCoverStatus status) {
|
||||
switch (status) {
|
||||
case RefreshCoverStatus.none:
|
||||
return Center();
|
||||
case RefreshCoverStatus.start:
|
||||
return CircularProgressIndicator(strokeWidth: 2);
|
||||
case RefreshCoverStatus.complete:
|
||||
return Icon(Icons.done);
|
||||
case RefreshCoverStatus.error:
|
||||
return Icon(Icons.refresh, color: Colors.red);
|
||||
default:
|
||||
return Center();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _TimePicker extends StatelessWidget {
|
||||
|
@ -477,15 +471,16 @@ class _TimePicker extends StatelessWidget {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
FlatButton(
|
||||
TextButton(
|
||||
onPressed: onCancel,
|
||||
child: Text(
|
||||
s.cancel,
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
surfaceTintColor: context.priamryContainer),
|
||||
onPressed: onConfirm,
|
||||
child: Text(
|
||||
s.confirm,
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -20,78 +18,6 @@ import 'podcast_detail.dart';
|
|||
import 'podcast_manage.dart';
|
||||
import 'podcast_settings.dart';
|
||||
|
||||
class AboutPodcast extends StatefulWidget {
|
||||
final PodcastLocal? podcastLocal;
|
||||
AboutPodcast({this.podcastLocal, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_AboutPodcastState createState() => _AboutPodcastState();
|
||||
}
|
||||
|
||||
class _AboutPodcastState extends State<AboutPodcast> {
|
||||
String? _description;
|
||||
late bool _load;
|
||||
|
||||
void getDescription(String? id) async {
|
||||
var dbHelper = DBHelper();
|
||||
var description = await dbHelper.getFeedDescription(id);
|
||||
_description = description;
|
||||
setState(() {
|
||||
_load = true;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_load = false;
|
||||
getDescription(widget.podcastLocal!.id);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var _groupList = Provider.of<GroupList>(context, listen: false);
|
||||
final s = context.s;
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20, left: 20, right: context.width / 3, bottom: 20),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
splashColor: context.accentColor.withAlpha(70),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
onPressed: () {
|
||||
_groupList.removePodcast(widget.podcastLocal!);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
textColor: Colors.red,
|
||||
child: Text(
|
||||
s.remove,
|
||||
),
|
||||
),
|
||||
],
|
||||
title: Text(widget.podcastLocal!.title!),
|
||||
content: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
!_load
|
||||
? Center()
|
||||
: _description != null
|
||||
? Html(data: _description)
|
||||
: Center(),
|
||||
if (widget.podcastLocal!.author != null)
|
||||
Text(widget.podcastLocal!.author!,
|
||||
style: TextStyle(color: Colors.blue))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PodcastList extends StatefulWidget {
|
||||
@override
|
||||
_PodcastListState createState() => _PodcastListState();
|
||||
|
@ -108,32 +34,34 @@ class _PodcastListState extends State<PodcastList> {
|
|||
Widget build(BuildContext context) {
|
||||
final width = context.width;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: context.primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.s.podcast(2)),
|
||||
leading: CustomBackButton(),
|
||||
actions: [
|
||||
Selector<SettingState, bool?>(
|
||||
value: context.overlay,
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: context.background,
|
||||
title: Text(context.s.podcast(2)),
|
||||
leading: CustomBackButton(),
|
||||
actions: [
|
||||
Selector<SettingState, bool?>(
|
||||
selector: (_, setting) => setting.openAllPodcastDefalt,
|
||||
builder: (_, data, __) {
|
||||
return data!
|
||||
? IconButton(
|
||||
splashRadius: 20,
|
||||
icon: Icon(Icons.all_out),
|
||||
onPressed: () => Navigator.push(
|
||||
context, ScaleRoute(page: PodcastManage())))
|
||||
: Center();
|
||||
})
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
if (!data!) return Center();
|
||||
return IconButton(
|
||||
splashRadius: 20,
|
||||
icon: Icon(Icons.all_out),
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
ScaleRoute(
|
||||
page: PodcastManage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Container(
|
||||
color: context.primaryColor,
|
||||
child: FutureBuilder<List<PodcastLocal>>(
|
||||
future: _getPodcastLocal(),
|
||||
|
@ -212,9 +140,10 @@ class _PodcastListState extends State<PodcastList> {
|
|||
}
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator()),
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -45,7 +45,7 @@ class _DataBackupState extends State<DataBackup> {
|
|||
systemNavigationBarIconBrightness: context.brightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
title: Text(s.settingsBackup),
|
||||
|
|
|
@ -44,7 +44,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
|||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
body: SafeArea(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||
|
|
|
@ -28,7 +28,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
|||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingsLayout),
|
||||
leading: CustomBackButton(),
|
||||
|
|
|
@ -46,7 +46,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
title: Text(s.play),
|
||||
leading: CustomBackButton(),
|
||||
|
|
|
@ -72,16 +72,16 @@ class _SettingsState extends State<Settings> {
|
|||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: context.brightness,
|
||||
systemNavigationBarColor: context.onPrimary,
|
||||
systemNavigationBarColor: context.background,
|
||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settings),
|
||||
leading: CustomBackButton(),
|
||||
elevation: _showTitle ? 1 : 0,
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
|
|
|
@ -46,7 +46,7 @@ class _StorageSettingState extends State<StorageSetting>
|
|||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingStorage),
|
||||
leading: CustomBackButton(),
|
||||
|
|
|
@ -11,16 +11,16 @@ class ThemeSetting extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
var settings = Provider.of<SettingState>(context, listen: false);
|
||||
final settings = Provider.of<SettingState>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: context.overlay,
|
||||
child: Scaffold(
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
title: Text(s.settingsAppearance),
|
||||
leading: CustomBackButton(),
|
||||
elevation: 0,
|
||||
backgroundColor: context.onPrimary,
|
||||
backgroundColor: context.background,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
@ -52,7 +52,7 @@ class ThemeSetting extends StatelessWidget {
|
|||
pageBuilder: (context, animaiton, secondaryAnimation) =>
|
||||
AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
statusBarColor: Colors.transparent,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
|
@ -151,13 +151,18 @@ class ThemeSetting extends StatelessWidget {
|
|||
ListTile(
|
||||
onTap: () => generalDialog(
|
||||
context,
|
||||
title: Text.rich(TextSpan(text: s.chooseA, children: [
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
text: ' ${s.color}',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.accentColor))
|
||||
])),
|
||||
text: s.chooseA,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: ' ${s.color}',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.accentColor))
|
||||
],
|
||||
),
|
||||
),
|
||||
content: _ColorPicker(
|
||||
onColorChanged: (value) => settings.setAccentColor = value,
|
||||
),
|
||||
|
@ -253,29 +258,6 @@ class __ColorPickerState extends State<_ColorPicker>
|
|||
});
|
||||
}
|
||||
|
||||
Widget _colorCircle(Color color) => Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
onTap: () => widget.onColorChanged!(color),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: color == context.accentColor
|
||||
? Border.all(color: Colors.grey[400]!, width: 4)
|
||||
: null,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
color: color),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
List<Widget> _accentList(MaterialAccentColor color) => [
|
||||
_colorCircle(color.shade100),
|
||||
_colorCircle(color.shade200),
|
||||
_colorCircle(color.shade400),
|
||||
_colorCircle(color.shade700)
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
@ -397,4 +379,27 @@ class __ColorPickerState extends State<_ColorPicker>
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _colorCircle(Color color) => Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
onTap: () => widget.onColorChanged!(color),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: color == context.accentColor
|
||||
? Border.all(color: Colors.grey[400]!, width: 4)
|
||||
: null,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
color: color),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
List<Widget> _accentList(MaterialAccentColor color) => [
|
||||
_colorCircle(color.shade100),
|
||||
_colorCircle(color.shade200),
|
||||
_colorCircle(color.shade400),
|
||||
_colorCircle(color.shade700)
|
||||
];
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ extension ContextExtension on BuildContext {
|
|||
double get paddingTop => MediaQuery.of(this).padding.top;
|
||||
TextTheme get textTheme => Theme.of(this).textTheme;
|
||||
SystemUiOverlayStyle get overlay => SystemUiOverlayStyle(
|
||||
statusBarColor: onPrimary,
|
||||
statusBarColor: background,
|
||||
statusBarIconBrightness: iconBrightness,
|
||||
systemNavigationBarColor: onPrimary,
|
||||
systemNavigationBarColor: background,
|
||||
systemNavigationBarIconBrightness: iconBrightness,
|
||||
);
|
||||
S get s => S.of(this)!;
|
||||
|
|
|
@ -72,11 +72,11 @@ class EpisodeGrid extends StatelessWidget {
|
|||
|
||||
Future<Tuple5<int, bool, bool, bool, List<int>>> _initData(
|
||||
EpisodeBrief episode) async {
|
||||
var menuList = await _getEpisodeMenu();
|
||||
var tapToOpen = await _getTapToOpenPopupMenu();
|
||||
var listened = await _isListened(episode);
|
||||
var liked = await _isLiked(episode);
|
||||
var downloaded = await _isDownloaded(episode);
|
||||
final menuList = await _getEpisodeMenu();
|
||||
final tapToOpen = await _getTapToOpenPopupMenu();
|
||||
final listened = await _isListened(episode);
|
||||
final liked = await _isLiked(episode);
|
||||
final downloaded = await _isDownloaded(episode);
|
||||
return Tuple5(listened, liked, downloaded, tapToOpen, menuList);
|
||||
}
|
||||
|
||||
|
@ -85,8 +85,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}
|
||||
|
||||
Future<List<int>> _getEpisodeMenu() async {
|
||||
var popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
|
||||
var list = await popupMenuStorage.getMenu();
|
||||
final popupMenuStorage = KeyValueStorage(episodePopupMenuKey);
|
||||
final list = await popupMenuStorage.getMenu();
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -95,8 +95,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}
|
||||
|
||||
Future<bool> _getTapToOpenPopupMenu() async {
|
||||
var tapToOpenPopupMenuStorage = KeyValueStorage(tapToOpenPopupMenuKey);
|
||||
var boo = await tapToOpenPopupMenuStorage.getBool(defaultValue: false);
|
||||
final tapToOpenPopupMenuStorage = KeyValueStorage(tapToOpenPopupMenuKey);
|
||||
final boo = await tapToOpenPopupMenuStorage.getBool(defaultValue: false);
|
||||
return boo;
|
||||
}
|
||||
|
||||
|
@ -307,102 +307,131 @@ class EpisodeGrid extends StatelessWidget {
|
|||
Color? color,
|
||||
bool? isLiked,
|
||||
bool? isDownloaded,
|
||||
Color? cardColor,
|
||||
required int isListened,
|
||||
bool? boo}) {
|
||||
var width = context.width;
|
||||
final width = context.width;
|
||||
if (layout == Layout.one) {
|
||||
return _layoutOneCard(context,
|
||||
index: index!,
|
||||
color: color,
|
||||
isLiked: isLiked!,
|
||||
cardColor: cardColor,
|
||||
isListened: isListened,
|
||||
isDownloaded: isDownloaded,
|
||||
boo: boo!);
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: layout == Layout.one ? 1 : 2,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
layout != Layout.one
|
||||
? _circleImage(context,
|
||||
episode: episodes![index!], color: color, boo: boo!)
|
||||
: _pubDate(context,
|
||||
episode: episodes![index!], color: color),
|
||||
Spacer(),
|
||||
_isNewIndicator(episodes![index]),
|
||||
_downloadIndicater(context,
|
||||
episode: episodes![index], isDownloaded: isDownloaded),
|
||||
_numberIndicater(context, index: index, color: color)
|
||||
],
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.bottomCenter,
|
||||
children: [
|
||||
if (isListened > 0)
|
||||
Container(
|
||||
height: 4,
|
||||
color: context.accentColor,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: layout == Layout.one ? 3 : 5,
|
||||
child: layout != Layout.one
|
||||
? _title(episodes![index])
|
||||
: Row(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: layout == Layout.one ? 1 : 2,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_circleImage(context,
|
||||
episode: episodes![index], color: color, boo: boo!),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(child: _title(episodes![index]))
|
||||
children: <Widget>[
|
||||
layout != Layout.one
|
||||
? _circleImage(context,
|
||||
episode: episodes![index!],
|
||||
color: color,
|
||||
boo: boo!)
|
||||
: _pubDate(context,
|
||||
episode: episodes![index!], color: color),
|
||||
Spacer(),
|
||||
_isNewIndicator(episodes![index]),
|
||||
_downloadIndicater(context,
|
||||
episode: episodes![index],
|
||||
isDownloaded: isDownloaded),
|
||||
_numberIndicater(context, index: index, color: color)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: layout == Layout.one ? 3 : 5,
|
||||
child: layout != Layout.one
|
||||
? _title(episodes![index])
|
||||
: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_circleImage(context,
|
||||
episode: episodes![index],
|
||||
color: color,
|
||||
boo: boo!),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Expanded(child: _title(episodes![index]))
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (layout != Layout.one)
|
||||
_pubDate(context,
|
||||
episode: episodes![index], color: color),
|
||||
Spacer(),
|
||||
if (layout != Layout.three &&
|
||||
episodes![index].duration != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
episodes![index].duration!.toTime,
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
if (episodes![index].duration != 0 &&
|
||||
episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0 &&
|
||||
layout != Layout.three)
|
||||
Text(
|
||||
'|',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
),
|
||||
),
|
||||
if (layout != Layout.three &&
|
||||
episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'${episodes![index].enclosureLength! ~/ 1000000}MB',
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(1),
|
||||
),
|
||||
if ((showFavorite || layout != Layout.three) && isLiked!)
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.red,
|
||||
size: width / 35,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (layout != Layout.one)
|
||||
_pubDate(context, episode: episodes![index], color: color),
|
||||
Spacer(),
|
||||
if (layout != Layout.three && episodes![index].duration != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
episodes![index].duration!.toTime,
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
if (episodes![index].duration != 0 &&
|
||||
episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0 &&
|
||||
layout != Layout.three)
|
||||
Text(
|
||||
'|',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
),
|
||||
),
|
||||
if (layout != Layout.three &&
|
||||
episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'${episodes![index].enclosureLength! ~/ 1000000}MB',
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(1),
|
||||
),
|
||||
if ((showFavorite || layout != Layout.three) && isLiked!)
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.red,
|
||||
size: width / 35,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -416,106 +445,127 @@ class EpisodeGrid extends StatelessWidget {
|
|||
Color? color,
|
||||
required bool isLiked,
|
||||
bool? isDownloaded,
|
||||
Color? cardColor,
|
||||
required int isListened,
|
||||
required bool boo}) {
|
||||
var width = context.width;
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.bottomCenter,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: _circleImage(context,
|
||||
episode: episodes![index],
|
||||
color: color,
|
||||
boo: boo,
|
||||
radius: context.width / 8),
|
||||
if (isListened > 0)
|
||||
Container(
|
||||
height: 4,
|
||||
color: context.accentColor,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Column(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(episodes![index].feedTitle!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold, color: color)),
|
||||
),
|
||||
_isNewIndicator(episodes![index]),
|
||||
_downloadIndicater(context,
|
||||
episode: episodes![index],
|
||||
isDownloaded: isDownloaded),
|
||||
_numberIndicater(context, index: index, color: color)
|
||||
],
|
||||
child: Center(
|
||||
child: _circleImage(context,
|
||||
episode: episodes![index],
|
||||
color: color,
|
||||
boo: boo,
|
||||
radius: context.width / 8),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: _title(episodes![index]))),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (episodes![index].duration != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
episodes![index].duration!.toTime,
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
flex: 4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(episodes![index].feedTitle!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color)),
|
||||
),
|
||||
),
|
||||
if (episodes![index].duration != 0 &&
|
||||
episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0 &&
|
||||
layout != Layout.three)
|
||||
Text(
|
||||
'|',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
),
|
||||
),
|
||||
if (episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'${episodes![index].enclosureLength! ~/ 1000000}MB',
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
if (isLiked)
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.red,
|
||||
size: width / 35,
|
||||
),
|
||||
Spacer(),
|
||||
_pubDate(context,
|
||||
episode: episodes![index], color: color),
|
||||
]),
|
||||
)
|
||||
_isNewIndicator(episodes![index]),
|
||||
_downloadIndicater(context,
|
||||
episode: episodes![index],
|
||||
isDownloaded: isDownloaded),
|
||||
_numberIndicater(context,
|
||||
index: index, color: color)
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: _title(episodes![index]))),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (episodes![index].duration != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
episodes![index].duration!.toTime,
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
if (episodes![index].duration != 0 &&
|
||||
episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0 &&
|
||||
layout != Layout.three)
|
||||
Text(
|
||||
'|',
|
||||
style: TextStyle(
|
||||
fontSize: width / 35,
|
||||
),
|
||||
),
|
||||
if (episodes![index].enclosureLength != null &&
|
||||
episodes![index].enclosureLength != 0)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'${episodes![index].enclosureLength! ~/ 1000000}MB',
|
||||
style: TextStyle(fontSize: width / 35),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
if (isLiked)
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.red,
|
||||
size: width / 35,
|
||||
),
|
||||
Spacer(),
|
||||
_pubDate(context,
|
||||
episode: episodes![index], color: color),
|
||||
]),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -533,7 +583,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||
final s = context.s;
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
|
||||
top: 10.0, bottom: 5.0, left: 10.0, right: 10.0),
|
||||
sliver: LiveSliverGrid.options(
|
||||
controller: scrollController,
|
||||
options: options,
|
||||
|
@ -549,8 +599,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
: layout == Layout.two
|
||||
? 2
|
||||
: 1,
|
||||
mainAxisSpacing: 6.0,
|
||||
crossAxisSpacing: 6.0,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
),
|
||||
itemBuilder: (context, index, animation) {
|
||||
final c = episodes![index].backgroudColor(context);
|
||||
|
@ -576,28 +626,31 @@ class EpisodeGrid extends StatelessWidget {
|
|||
future: _initData(episodes![index]),
|
||||
initialData: Tuple5(0, false, false, false, []),
|
||||
builder: (context, snapshot) {
|
||||
var isListened = snapshot.data!.item1;
|
||||
var isLiked = snapshot.data!.item2;
|
||||
var isDownloaded = snapshot.data!.item3;
|
||||
var tapToOpen = snapshot.data!.item4;
|
||||
var menuList = snapshot.data!.item5;
|
||||
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.all(Radius.circular(5.0)),
|
||||
color: isListened > 0
|
||||
? context.brightness == Brightness.light
|
||||
? Colors.grey[200]
|
||||
: Color.fromRGBO(50, 50, 50, 1)
|
||||
: context.background,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: Color.fromRGBO(40, 40, 40, 1),
|
||||
blurRadius: 0.5,
|
||||
spreadRadius: 0.5,
|
||||
),
|
||||
]),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.all(
|
||||
// Radius.circular(15.0),
|
||||
// ),
|
||||
// color: isListened > 0
|
||||
// ? context.brightness == Brightness.light
|
||||
// ? Colors.grey[200]
|
||||
// : Color.fromRGBO(50, 50, 50, 1)
|
||||
// : context.priamryContainer,
|
||||
// ),
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: context.brightness == Brightness.light
|
||||
// ? context.primaryColor
|
||||
// : Color.fromRGBO(40, 40, 40, 1),
|
||||
// blurRadius: 0.5,
|
||||
// spreadRadius: 0.5,
|
||||
// ),
|
||||
// ]),
|
||||
alignment: Alignment.center,
|
||||
child: multiSelect!
|
||||
? Material(
|
||||
|
@ -616,7 +669,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
color: episodes![index].cardColor(context),
|
||||
border: Border.all(
|
||||
color: selectedList!
|
||||
.contains(episodes![index])
|
||||
|
@ -632,6 +686,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||
index: index,
|
||||
isLiked: isLiked,
|
||||
isDownloaded: isDownloaded,
|
||||
isListened: isListened,
|
||||
color: c,
|
||||
boo: boo),
|
||||
),
|
||||
|
@ -639,7 +694,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
color: episodes![index].cardColor(context),
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
border: Border.all(
|
||||
color: context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
|
@ -652,7 +708,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||
menuItemExtent: 45,
|
||||
menuBoxDecoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(15.0)),
|
||||
borderRadius: BorderRadius.circular(20.0)),
|
||||
duration: Duration(milliseconds: 100),
|
||||
tapMode: tapToOpen
|
||||
? TapMode.onTap
|
||||
|
@ -666,10 +722,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||
menuOffset: 6,
|
||||
menuItems: <FocusedMenuItem>[
|
||||
FocusedMenuItem(
|
||||
backgroundColor:
|
||||
context.brightness == Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
backgroundColor: context.priamryContainer,
|
||||
title: Text(
|
||||
data.item1 != episodes![index] ||
|
||||
!data.item4
|
||||
|
@ -687,10 +740,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}),
|
||||
if (menuList.contains(1))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.brightness ==
|
||||
Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
backgroundColor:
|
||||
context.priamryContainer,
|
||||
title: data.item2.contains(
|
||||
episodes![index].enclosureUrl)
|
||||
? Text(s.remove)
|
||||
|
@ -719,10 +770,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}),
|
||||
if (menuList.contains(2))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.brightness ==
|
||||
Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
backgroundColor:
|
||||
context.priamryContainer,
|
||||
title: isLiked
|
||||
? Text(s.unlike)
|
||||
: Text(s.like),
|
||||
|
@ -749,10 +798,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}),
|
||||
if (menuList.contains(3))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.brightness ==
|
||||
Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
backgroundColor:
|
||||
context.priamryContainer,
|
||||
title: isListened > 0
|
||||
? Text(s.markNotListened,
|
||||
style: TextStyle(
|
||||
|
@ -792,10 +839,8 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}),
|
||||
if (menuList.contains(4))
|
||||
FocusedMenuItem(
|
||||
backgroundColor: context.brightness ==
|
||||
Brightness.light
|
||||
? context.primaryColor
|
||||
: context.dialogBackgroundColor,
|
||||
backgroundColor:
|
||||
context.priamryContainer,
|
||||
title: isDownloaded
|
||||
? Text(s.downloaded,
|
||||
style: TextStyle(
|
||||
|
@ -812,30 +857,33 @@ class EpisodeGrid extends StatelessWidget {
|
|||
}),
|
||||
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,
|
||||
);
|
||||
}),
|
||||
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: action,
|
||||
child: _episodeCard(context,
|
||||
index: index,
|
||||
isLiked: isLiked,
|
||||
isDownloaded: isDownloaded,
|
||||
color: c,
|
||||
boo: boo),
|
||||
child: _episodeCard(
|
||||
context,
|
||||
index: index,
|
||||
isLiked: isLiked,
|
||||
isListened: isListened,
|
||||
isDownloaded: isDownloaded,
|
||||
cardColor:
|
||||
episodes![index].cardColor(context),
|
||||
color: c,
|
||||
boo: boo,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue