modified: lib/class/settingstate.dart

modified:   lib/episodes/episodedetail.dart
	modified:   lib/home/appbar/about.dart
modified:   lib/home/appbar/addpodcast.dart
	modified:   lib/home/appbar/importompl.dart
	modified:   lib/home/appbar/popupmenu.dart
	modified:   lib/home/audio_player.dart
	modified:   lib/home/audiopanel.dart
	modified:   lib/home/homescroll.dart
	modified:   lib/home/hometab.dart
	modified:   lib/local_storage/key_value_storage.dart
	modified:   lib/main.dart
	modified:   lib/podcasts/podcastdetail.dart
	modified:   lib/podcasts/podcastgroup.dart
	modified:   lib/podcasts/podcastlist.dart
	modified:   lib/podcasts/podcastmanage.dart
	modified:   lib/util/episodegrid.dart
This commit is contained in:
stonegate 2020-02-22 20:25:06 +08:00
parent 9d4bbc895a
commit 7ba0552717
17 changed files with 966 additions and 791 deletions

View File

@ -1,13 +1,30 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
//two types podcast update, backhome nedd to back to default grooup.
enum Update {backhome, justupdate}
class SettingState extends ChangeNotifier{
Update _subscribeupdate;
Update get subscribeupdate => _subscribeupdate;
set subscribeUpdate(Update s){
_subscribeupdate = s;
import 'package:tsacdop/local_storage/key_value_storage.dart';
class SettingState extends ChangeNotifier {
KeyValueStorage storage = KeyValueStorage('themes');
int _theme;
int get theme => _theme;
void setTheme(int theme) {
_theme = theme;
notifyListeners();
_saveTheme(theme);
}
@override
void addListener(VoidCallback listener) {
super.addListener(listener);
_getTheme();
}
_getTheme() async {
_theme = await storage.getTheme();
}
_saveTheme(theme) async {
await storage.saveTheme(theme);
}
}

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:url_launcher/url_launcher.dart';
@ -15,8 +16,7 @@ import 'episodedownload.dart';
class EpisodeDetail extends StatefulWidget {
final EpisodeBrief episodeItem;
final String heroTag;
EpisodeDetail({this.episodeItem, this.heroTag, Key key})
: super(key: key);
EpisodeDetail({this.episodeItem, this.heroTag, Key key}) : super(key: key);
@override
_EpisodeDetailState createState() => _EpisodeDetailState();
@ -53,119 +53,130 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text(widget.episodeItem.feedTitle),
elevation: 0.0,
centerTitle: true,
backgroundColor: Colors.grey[100],
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
statusBarColor: Theme.of(context).primaryColor,
),
body: Container(
color: Colors.grey[100],
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 12.0),
alignment: Alignment.topLeft,
child: Text(
widget.episodeItem.title,
style: Theme.of(context).textTheme.headline5,
child: SafeArea(
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
appBar: AppBar(
title: Text(widget.episodeItem.feedTitle),
centerTitle: true,
),
body: Container(
color: Theme.of(context).primaryColor,
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 12.0),
alignment: Alignment.topLeft,
child: Text(
widget.episodeItem.title,
style: Theme.of(context).textTheme.headline5,
),
),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 12.0),
height: 30.0,
child: Text(
'Published ' +
DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(
widget.episodeItem.pubDate)),
style: TextStyle(color: Colors.blue[500])),
),
Container(
padding: EdgeInsets.all(12.0),
height: 50.0,
child: Row(
children: <Widget>[
(widget.episodeItem.explicit == 1)
? Container(
decoration: BoxDecoration(
color: Colors.red[800],
shape: BoxShape.circle),
height: 25.0,
width: 25.0,
margin: EdgeInsets.only(right: 10.0),
alignment: Alignment.center,
child: Text('E',
style: TextStyle(color: Colors.white)))
: Center(),
Container(
decoration: BoxDecoration(
color: Colors.cyan[300],
borderRadius:
BorderRadius.all(Radius.circular(15.0))),
height: 30.0,
margin: EdgeInsets.only(right: 10.0),
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
(widget.episodeItem.duration).toString() +
'mins',
style: textstyle),
),
Container(
decoration: BoxDecoration(
color: Colors.lightBlue[300],
borderRadius:
BorderRadius.all(Radius.circular(15.0))),
height: 30.0,
margin: EdgeInsets.only(right: 10.0),
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
((widget.episodeItem.enclosureLength) ~/
1000000)
.toString() +
'MB',
style: textstyle),
),
],
),
),
],
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 12.0, right: 12.0, top: 5.0),
child: SingleChildScrollView(
child: _loaddes
? (widget.episodeItem.description.contains('<'))
? Html(
data: widget.episodeItem.description,
onLinkTap: (url) {
_launchUrl(url);
},
useRichText: true,
)
: Container(
alignment: Alignment.topLeft,
child: Text(widget.episodeItem.description))
: Center(),
),
),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 12.0),
height: 30.0,
child: Text(
'Published ' +
DateFormat.yMMMd().format( DateTime.fromMillisecondsSinceEpoch(widget.episodeItem.pubDate)),
style: TextStyle(color: Colors.blue[500])),
),
Container(
padding: EdgeInsets.all(12.0),
height: 50.0,
child: Row(
children: <Widget>[
(widget.episodeItem.explicit == 1)
? Container(
decoration: BoxDecoration(
color: Colors.red[800],
shape: BoxShape.circle),
height: 25.0,
width: 25.0,
margin: EdgeInsets.only(right: 10.0),
alignment: Alignment.center,
child: Text('E',
style: TextStyle(color: Colors.white)))
: Center(),
Container(
decoration: BoxDecoration(
color: Colors.cyan[300],
borderRadius:
BorderRadius.all(Radius.circular(15.0))),
height: 30.0,
margin: EdgeInsets.only(right: 10.0),
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
(widget.episodeItem.duration).toString() + 'mins',
style: textstyle),
),
Container(
decoration: BoxDecoration(
color: Colors.lightBlue[300],
borderRadius:
BorderRadius.all(Radius.circular(15.0))),
height: 30.0,
margin: EdgeInsets.only(right: 10.0),
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
((widget.episodeItem.enclosureLength) ~/ 1000000)
.toString() +
'MB',
style: textstyle),
),
],
),
),
],
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 12.0, right: 12.0, top: 5.0),
child: SingleChildScrollView(
child: _loaddes
? (widget.episodeItem.description.contains('<'))
? Html(
data: widget.episodeItem.description,
onLinkTap: (url) {
_launchUrl(url);
},
useRichText: true,
)
: Container(
alignment: Alignment.topLeft,
child: Text(widget.episodeItem.description))
: Center(),
),
),
),
MenuBar(
episodeItem: widget.episodeItem,
heroTag: widget.heroTag,
MenuBar(
episodeItem: widget.episodeItem,
heroTag: widget.heroTag,
),
],
],
),
),
),
),
);
@ -175,8 +186,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
class MenuBar extends StatefulWidget {
final EpisodeBrief episodeItem;
final String heroTag;
MenuBar({this.episodeItem, this.heroTag, Key key})
: super(key: key);
MenuBar({this.episodeItem, this.heroTag, Key key}) : super(key: key);
@override
_MenuBarState createState() => _MenuBarState();
}
@ -224,114 +234,113 @@ class _MenuBarState extends State<MenuBar> {
@override
Widget build(BuildContext context) {
final audioplay = Provider.of<AudioPlay>(context);
return Container(
height: 50.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
(widget.episodeItem.title == audioplay.episode?.title &&
audioplay.audioState == AudioState.play)
? ImageRotate(
title: widget.episodeItem.feedTitle,
path: widget.episodeItem.imagePath,
)
: Hero(
tag: widget.episodeItem.enclosureUrl + widget.heroTag,
child: Container(
padding: EdgeInsets.all(10.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
child: Container(
height: 30.0,
width: 30.0,
color: Colors.white,
child: Image.file(File(
"${widget.episodeItem.imagePath}")),
),
),
),
),
(_like == 0 && !_liked)
? _buttonOnMenu(
Icon(
Icons.favorite_border,
color: Colors.grey[700],
),
() => saveLiked(widget.episodeItem.enclosureUrl))
: Stack(
alignment: Alignment.center,
children: <Widget>[
LoveOpen(),
_buttonOnMenu(
Icon(
Icons.favorite,
color: Colors.red,
),
() => setUnliked(widget.episodeItem.enclosureUrl)),
],
),
DownloadButton(episodeBrief: widget.episodeItem),
_buttonOnMenu(Icon(Icons.playlist_add, color: Colors.grey[700]),
() {
Fluttertoast.showToast(
msg: 'Not support yet',
gravity: ToastGravity.BOTTOM,
);
/*TODO*/
}),
Spacer(),
(widget.episodeItem.title != audioplay.episode?.title)
? Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.only(
topRight: Radius.circular(5.0),
bottomRight: Radius.circular(5.0)),
onTap: () {
audioplay.episodeLoad = widget.episodeItem;
},
return Container(
height: 50.0,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
(widget.episodeItem.title == audioplay.episode?.title &&
audioplay.audioState == AudioState.play)
? ImageRotate(
title: widget.episodeItem.feedTitle,
path: widget.episodeItem.imagePath,
)
: Hero(
tag: widget.episodeItem.enclosureUrl + widget.heroTag,
child: Container(
padding: EdgeInsets.all(10.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
child: Container(
alignment: Alignment.center,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: <Widget>[
Text('Play Now',
style: TextStyle(
color: Colors.blue,
fontSize: 15,
fontWeight: FontWeight.bold,
)),
Icon(
Icons.play_arrow,
color: Colors.blue,
),
],
),
height: 30.0,
width: 30.0,
color: Theme.of(context).scaffoldBackgroundColor,
child:
Image.file(File("${widget.episodeItem.imagePath}")),
),
),
)
: (widget.episodeItem.title == audioplay.episode?.title &&
audioplay.audioState == AudioState.play)
? Container(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20, height: 15, child: WaveLoader()))
: Container(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20,
height: 15,
child: LineLoader(),
),
),
(_like == 0 && !_liked)
? _buttonOnMenu(
Icon(
Icons.favorite_border,
color: Colors.grey[700],
),
() => saveLiked(widget.episodeItem.enclosureUrl))
: Stack(
alignment: Alignment.center,
children: <Widget>[
LoveOpen(),
_buttonOnMenu(
Icon(
Icons.favorite,
color: Colors.red,
),
() => setUnliked(widget.episodeItem.enclosureUrl)),
],
),
DownloadButton(episodeBrief: widget.episodeItem),
_buttonOnMenu(Icon(Icons.playlist_add, color: Colors.grey[700]), () {
Fluttertoast.showToast(
msg: 'Not support yet',
gravity: ToastGravity.BOTTOM,
);
/*TODO*/
}),
Spacer(),
(widget.episodeItem.title != audioplay.episode?.title)
? Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.only(
topRight: Radius.circular(5.0),
bottomRight: Radius.circular(5.0)),
onTap: () {
audioplay.episodeLoad = widget.episodeItem;
},
child: Container(
alignment: Alignment.center,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: <Widget>[
Text('Play Now',
style: TextStyle(
color: Theme.of(context).accentColor,
fontSize: 15,
fontWeight: FontWeight.bold,
)),
Icon(
Icons.play_arrow,
color: Theme.of(context).accentColor,
),
],
),
],
),
),
),
)
: (widget.episodeItem.title == audioplay.episode?.title &&
audioplay.audioState == AudioState.play)
? Container(
padding: EdgeInsets.only(right: 30),
child:
SizedBox(width: 20, height: 15, child: WaveLoader()))
: Container(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20,
height: 15,
child: LineLoader(),
),
),
],
),
);
}
}

View File

@ -1,31 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AboutApp extends StatelessWidget {
TextSpan buildTextSpan() {
return TextSpan(children: [
TextSpan(text: 'Tsacdop\n',style: TextStyle(fontSize: 20)),
TextSpan(text: 'Tsacdop\n', style: TextStyle(fontSize: 20)),
TextSpan(
text:
'Tsacdop is a podcast client developed by flutter, is a simple, easy-use player.\n'),
TextSpan(
text:
'Github https://github.com/stonga/tsacdop .\n'),
TextSpan(text: 'Github https://github.com/stonga/tsacdop .\n'),
]);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey[100],
title: Text('Tsacdop'),
centerTitle: true,
elevation: 0,
),
body: Container(
padding: EdgeInsets.all(20),
alignment: Alignment.topLeft,
child: Text.rich(buildTextSpan()),
));
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
statusBarColor: Theme.of(context).primaryColor,
),
child: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Tsacdop'),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.all(20),
alignment: Alignment.topLeft,
child: Text.rich(buildTextSpan()),
)),
),
);
}
}

View File

@ -34,35 +34,35 @@ class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor: Colors.grey[100],
statusBarColor: Colors.green,
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
statusBarColor: Theme.of(context).primaryColor,
),
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
elevation: 0,
centerTitle: true,
backgroundColor: Colors.grey[100],
brightness: Brightness.light,
leading: IconButton(
tooltip: 'Add',
icon: const Icon(Icons.add_circle_outline),
onPressed: () async {
await showSearch<int>(
context: context,
delegate: _delegate,
);
},
child: SafeArea(
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: true,
leading: IconButton(
tooltip: 'Add',
icon: const Icon(Icons.add_circle_outline),
onPressed: () async {
await showSearch<int>(
context: context,
delegate: _delegate,
);
},
),
title: Image(
image: AssetImage('assets/text.png'),
height: 30,
),
actions: <Widget>[
PopupMenu(),
],
),
title: Image(
image: AssetImage('assets/text.png'),
height: 30,
),
actions: <Widget>[
PopupMenu(),
],
body: Home(),
),
body: Home(),
),
);
}
@ -83,6 +83,9 @@ class _MyHomePageDelegate extends SearchDelegate<int> {
var searchResult = SearchPodcast.fromJson(searchResultMap);
return searchResult.results;
}
@override
ThemeData appBarTheme(BuildContext context) => Theme.of(context);
@override
Widget buildLeading(BuildContext context) {
@ -327,7 +330,7 @@ class _SearchResultState extends State<SearchResult> {
? !_adding
? OutlineButton(
child: Text('Subscribe',
style: TextStyle(color: Colors.blue)),
style: TextStyle(color: Theme.of(context).accentColor)),
onPressed: () {
importOmpl.rssTitle = widget.onlinePodcast.title;
savePodcast(widget.onlinePodcast.rss);
@ -351,7 +354,7 @@ class _SearchResultState extends State<SearchResult> {
? Container(
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: Colors.grey[300],
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.only(
topRight: Radius.circular(15.0),
bottomLeft: Radius.circular(15.0),

View File

@ -7,7 +7,7 @@ class Import extends StatelessWidget {
Widget build(BuildContext context) {
return Consumer<ImportOmpl>(
builder: (context, importOmpl, _) => Container(
color: Colors.grey[300],
color: Theme.of(context).primaryColorDark,
child: importOmpl.importState == ImportState.start
? Column(
mainAxisAlignment: MainAxisAlignment.start,

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/class/podcast_group.dart';
import 'package:tsacdop/class/settingstate.dart';
import 'package:xml/xml.dart' as xml;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart';
@ -47,6 +48,7 @@ class PopupMenu extends StatelessWidget {
Widget build(BuildContext context) {
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context, listen: false);
GroupList groupList = Provider.of<GroupList>(context, listen: false);
SettingState setting = Provider.of<SettingState>(context);
_refreshAll() async {
var dbHelper = DBHelper();
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
@ -113,16 +115,15 @@ class PopupMenu extends StatelessWidget {
await Future.delayed(Duration(seconds: 5));
importOmpl.importState = ImportState.stop;
}
}
else {
} else {
importOmpl.importState = ImportState.error;
Fluttertoast.showToast(
msg: 'Network error, Subscribe failed',
gravity: ToastGravity.TOP,
);
await Future.delayed(Duration(seconds: 5));
importOmpl.importState = ImportState.stop;
Fluttertoast.showToast(
msg: 'Network error, Subscribe failed',
gravity: ToastGravity.TOP,
);
await Future.delayed(Duration(seconds: 5));
importOmpl.importState = ImportState.stop;
}
}
@ -144,10 +145,11 @@ class PopupMenu extends StatelessWidget {
for (int i = 0; i < total.length; i++) {
if (total[i].xmlUrl != null) {
importOmpl.rssTitle = total[i].text;
try{await saveOmpl(total[i].xmlUrl);}
catch (e){
print(e.toString());
}
try {
await saveOmpl(total[i].xmlUrl);
} catch (e) {
print(e.toString());
}
print(total[i].text);
}
}
@ -183,17 +185,23 @@ class PopupMenu extends StatelessWidget {
),
PopupMenuItem(
value: 3,
child: setting.theme != 2 ? Text('Night Mode') : Text('Light Mode'),
),
PopupMenuItem(
value: 4,
child: Text('About'),
),
],
onSelected: (value) {
if (value == 3) {
if (value == 4) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AboutApp()));
} else if (value == 2) {
_getFilePath();
} else if (value == 1) {
_refreshAll();
} else if (value == 3) {
setting.theme != 2 ? setting.setTheme(2) : setting.setTheme(1);
}
},
);

View File

@ -65,9 +65,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
_remoteAudioLoading = true;
Provider.of<AudioPlay>(context, listen: false).audioState = AudioState.load;
if (_backgroundAudioPlaying == true)
{ _backgroundAudio?.pause();
AudioSystem.instance.stopBackgroundDisplay();}
if (_backgroundAudioPlaying == true) {
_backgroundAudio?.pause();
AudioSystem.instance.stopBackgroundDisplay();
}
_backgroundAudio?.dispose();
_backgroundAudio = Audio.loadFromRemoteUrl(url,
onDuration: (double durationSeconds) {
@ -118,9 +119,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
_remoteAudioLoading = true;
ByteData audio = getAudio(path);
Provider.of<AudioPlay>(context, listen: false).audioState = AudioState.load;
if (_backgroundAudioPlaying == true)
{_backgroundAudio?.pause();
AudioSystem.instance.stopBackgroundDisplay();}
if (_backgroundAudioPlaying == true) {
_backgroundAudio?.pause();
AudioSystem.instance.stopBackgroundDisplay();
}
_backgroundAudio?.dispose();
_backgroundAudio = Audio.loadFromByteData(audio,
onDuration: (double durationSeconds) {
@ -192,11 +194,6 @@ class _PlayerWidgetState extends State<PlayerWidget> {
setState(() {
this.url = url;
episode = Provider.of<AudioPlay>(context).episode;
var color = json.decode(episode?.primaryColor);
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
_backgroundAudioPlaying = true;
_isLoading = true;
_getFile(url).then((result) {
@ -270,11 +267,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
'playnow', likeButtonId, 'ic_stat_play_circle_filled');
Future<void> _setNotification() async {
final Uint8List imageBytes =
File('${episode.imagePath}').readAsBytesSync();
final Uint8List imageBytes = File('${episode.imagePath}').readAsBytesSync();
AudioSystem.instance.setMetadata(AudioMetadata(
title: episode.title,
artist:episode.feedTitle,
artist: episode.feedTitle,
album: episode.feedTitle,
genre: "Podcast",
durationSeconds: _backgroundAudioDurationSeconds,
@ -309,8 +305,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
Provider.of<AudioPlay>(context, listen: false).audioState =
AudioState.play;
});
final Uint8List imageBytes =
File('${episode.imagePath}').readAsBytesSync();
final Uint8List imageBytes = File('${episode.imagePath}').readAsBytesSync();
AudioSystem.instance.setMetadata(AudioMetadata(
title: episode.title,
artist: episode.feedTitle,
@ -383,9 +378,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
AudioSystem.instance.setPlaybackState(true, forwardposition);
}
Widget _expandedPanel() => Container(
Widget _expandedPanel(BuildContext context) => Container(
height: 300,
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@ -459,7 +454,8 @@ class _PlayerWidgetState extends State<PlayerWidget> {
color: const Color(0xFFFF0000)))
: Text(
_remoteAudioLoading ? 'Buffring...' : '',
style: TextStyle(color: Colors.blue),
style: TextStyle(
color: Theme.of(context).accentColor),
),
),
),
@ -474,6 +470,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
height: 100,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
color: Colors.transparent,
@ -484,7 +481,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
: null,
iconSize: 32.0,
icon: Icon(Icons.replay_10),
color: Colors.black),
color: Theme.of(context).tabBarTheme.labelColor),
),
_backgroundAudioPlaying
? Material(
@ -498,7 +495,8 @@ class _PlayerWidgetState extends State<PlayerWidget> {
: null,
iconSize: 40.0,
icon: Icon(Icons.pause_circle_filled),
color: Colors.black),
color:
Theme.of(context).tabBarTheme.labelColor),
)
: Material(
color: Colors.transparent,
@ -511,7 +509,8 @@ class _PlayerWidgetState extends State<PlayerWidget> {
},
iconSize: 40.0,
icon: Icon(Icons.play_circle_filled),
color: Colors.black),
color:
Theme.of(context).tabBarTheme.labelColor),
),
Material(
color: Colors.transparent,
@ -522,7 +521,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
: null,
iconSize: 32.0,
icon: Icon(Icons.forward_30),
color: Colors.black),
color: Theme.of(context).tabBarTheme.labelColor),
),
],
),
@ -533,7 +532,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
margin: EdgeInsets.symmetric(vertical: 10.0),
padding: EdgeInsets.symmetric(horizontal: 10.0),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
@ -545,12 +544,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
child: Container(
height: 30.0,
width: 30.0,
color: Colors.white,
child: Image.file(
File("${episode.imagePath}"))
),
height: 30.0,
width: 30.0,
child: Image.file(File("${episode.imagePath}"))),
),
),
Spacer(),
@ -565,138 +561,158 @@ class _PlayerWidgetState extends State<PlayerWidget> {
))
]),
);
Widget _miniPanel(double width) => Container(
height: 60,
color: Colors.grey[100],
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 2,
child: LinearProgressIndicator(
value: _seekSliderValue,
backgroundColor: Colors.grey[100],
valueColor: AlwaysStoppedAnimation<Color>(_c),
)),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 15, right: 10),
alignment: Alignment.center,
Widget _miniPanel(double width, BuildContext context) {
var color = json.decode(episode?.primaryColor);
if (color) {
if (Theme.of(context).brightness == Brightness.light) {
_c = (color[0] > 200 && color[1] > 200 && color[2] > 200)
? Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: Color.fromRGBO(color[0], color[1], color[2], 1.0);
} else {
_c = (color[0] < 50 && color[1] < 50 && color[2] < 50)
? Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: Color.fromRGBO(color[0], color[1], color[2], 1.0);
}
}
return Container(
height: 60,
color: Theme.of(context).primaryColor,
child:
Column(mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[
SizedBox(
height: 2,
child: LinearProgressIndicator(
value: _seekSliderValue,
backgroundColor: Theme.of(context).primaryColor,
valueColor: AlwaysStoppedAnimation<Color>(_c),
)),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 15, right: 10),
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 4,
child: Container(
padding: EdgeInsets.symmetric(vertical: 5),
alignment: Alignment.centerLeft,
child: (episode.title.length > 55)
? Marquee(
text: episode.title,
style: TextStyle(fontWeight: FontWeight.bold),
scrollAxis: Axis.vertical,
crossAxisAlignment: CrossAxisAlignment.start,
blankSpace: 30.0,
velocity: 50.0,
pauseAfterRound: Duration(seconds: 1),
startPadding: 30.0,
accelerationDuration: Duration(seconds: 1),
accelerationCurve: Curves.linear,
decelerationDuration: Duration(milliseconds: 500),
decelerationCurve: Curves.easeOut,
)
: Text(
episode.title,
style: TextStyle(fontWeight: FontWeight.bold),
maxLines: 2,
),
),
),
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: _remoteAudioLoading
? Text(
'Buffring...',
style:
TextStyle(color: Theme.of(context).accentColor),
)
: Row(
children: <Widget>[
Text(
_stringForSeconds(
_backgroundAudioDurationSeconds -
_backgroundAudioPositionSeconds) ??
'',
style: TextStyle(color: _c),
),
Text(
' Left',
style: TextStyle(color: _c),
),
],
),
),
),
Expanded(
flex: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 4,
child: Container(
padding: EdgeInsets.symmetric(vertical: 5),
alignment: Alignment.centerLeft,
child: (episode.title.length > 55)
? Marquee(
text: episode.title,
style: TextStyle(fontWeight: FontWeight.bold),
scrollAxis: Axis.vertical,
crossAxisAlignment: CrossAxisAlignment.start,
blankSpace: 30.0,
velocity: 50.0,
pauseAfterRound: Duration(seconds: 1),
startPadding: 30.0,
accelerationDuration: Duration(seconds: 1),
accelerationCurve: Curves.linear,
decelerationDuration:
Duration(milliseconds: 500),
decelerationCurve: Curves.easeOut,
)
: Text(
episode.title,
style: TextStyle(fontWeight: FontWeight.bold),
maxLines: 2,
),
),
),
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: _remoteAudioLoading
? Text(
'Buffring...',
style: TextStyle(color: Colors.blue),
)
: Row(
children: <Widget>[
Text(
_stringForSeconds(
_backgroundAudioDurationSeconds -
_backgroundAudioPositionSeconds) ??
'',
style: TextStyle(color: _c),
),
Text(
' Left',
style: TextStyle(color: _c),
),
],
),
),
),
Expanded(
flex: 2,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_backgroundAudioPlaying
? Material(
color: Colors.transparent,
child: IconButton(
onPressed: _backgroundAudioPlaying
? () {
_pauseBackgroundAudio();
}
: null,
iconSize: 25.0,
icon: Icon(Icons.pause_circle_filled),
color: Colors.black),
)
: Material(
color: Colors.transparent,
child: IconButton(
onPressed: _backgroundAudioPlaying
? null
: () {
_resumeBackgroundAudio();
},
iconSize: 25.0,
icon: Icon(Icons.play_circle_filled),
color: Colors.black),
),
Material(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_backgroundAudioPlaying
? Material(
color: Colors.transparent,
child: IconButton(
onPressed: _backgroundAudioPlaying
? () => _forwardBackgroundAudio(30)
? () {
_pauseBackgroundAudio();
}
: null,
iconSize: 25.0,
icon: Icon(Icons.forward_30),
color: Colors.black),
icon: Icon(Icons.pause_circle_filled),
color:
Theme.of(context).tabBarTheme.labelColor),
)
: Material(
color: Colors.transparent,
child: IconButton(
onPressed: _backgroundAudioPlaying
? null
: () {
_resumeBackgroundAudio();
},
iconSize: 25.0,
icon: Icon(Icons.play_circle_filled),
color:
Theme.of(context).tabBarTheme.labelColor),
),
],
),
Material(
color: Colors.transparent,
child: IconButton(
onPressed: _backgroundAudioPlaying
? () => _forwardBackgroundAudio(30)
: null,
iconSize: 25.0,
icon: Icon(Icons.forward_30),
color: Theme.of(context).tabBarTheme.labelColor),
),
],
),
),
),
]),
);
],
),
),
),
]),
);
}
@override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
return !_isLoading
? Center()
: AudioPanel(
miniPanel: _miniPanel(_width), expandedPanel: _expandedPanel());
miniPanel: _miniPanel(_width, context),
expandedPanel: _expandedPanel(context));
}
}

View File

@ -46,7 +46,7 @@ class _AudioPanelState extends State<AudioPanel>
child: GestureDetector(
onTap: () => _backToMini(),
child: Container(
color: Colors.white.withOpacity(0.5),
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
),
),
)
@ -64,7 +64,7 @@ class _AudioPanelState extends State<AudioPanel>
: (_animation.value <= minSize) ? minSize : _animation.value,
child: _animation.value < minSize + 30
? Container(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
child: Opacity(
opacity: _animation.value > minSize
? (minSize + 30 - _animation.value) / 40
@ -75,7 +75,7 @@ class _AudioPanelState extends State<AudioPanel>
),
)
: Container(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
child: SingleChildScrollView(
child: Opacity(
opacity: _animation.value < (maxSize - 50)

View File

@ -87,9 +87,10 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
EdgeInsets.symmetric(horizontal: 15.0),
child: Text(
groups[_groupIndex].name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red[300]),
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(color: Colors.red[300]),
)),
Spacer(),
Container(
@ -104,21 +105,24 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
);
},
child: Container(
height: 30,
padding: EdgeInsets.all(5.0),
child: Text('See All',
style: TextStyle(
color: Colors.red[300],
fontWeight: FontWeight.bold,
)),
),
height: 30,
padding: EdgeInsets.all(5.0),
child: Text(
'See All',
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(
color: Theme.of(context)
.accentColor),
)),
),
),
],
),
),
Container(
color: Colors.white10,
// color: Colors.white10,
height: 70,
width: _width,
alignment: Alignment.centerLeft,
@ -153,14 +157,15 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
height: (_width - 20) / 3 + 40,
margin: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Colors.white,
color: Theme.of(context).scaffoldBackgroundColor,
),
child: TabBarView(
children: groups[_groupIndex]
.podcasts
.map<Widget>((PodcastLocal podcastLocal) {
return Container(
decoration: BoxDecoration(color: Colors.grey[100]),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor),
margin: EdgeInsets.symmetric(horizontal: 5.0),
key: ObjectKey(podcastLocal.title),
child: PodcastPreview(
@ -193,18 +198,21 @@ class _PodcastPreviewState extends State<PodcastPreview> {
}
Color _c;
@override
void initState() {
super.initState();
var color = json.decode(widget.podcastLocal.primaryColor);
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
}
@override
Widget build(BuildContext context) {
var color = json.decode(widget.podcastLocal.primaryColor);
if (Theme.of(context).brightness == Brightness.light) {
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
} else {
(color[0] < 50 && color[1] < 50 && color[2] < 50)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
}
return Column(
children: <Widget>[
Expanded(
@ -294,10 +302,18 @@ class ShowEpisode extends StatelessWidget {
(BuildContext context, int index) {
Color _c;
var color = json.decode(podcast[index].primaryColor);
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
if (Theme.of(context).brightness == Brightness.light) {
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
} else {
(color[0] < 50 && color[1] < 50 && color[2] < 50)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
}
return InkWell(
onTap: () {
Navigator.push(
@ -315,12 +331,12 @@ class ShowEpisode extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.of(context).scaffoldBackgroundColor,
border: Border.all(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
width: 3.0,
),
boxShadow: [
BoxShadow(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
blurRadius: 1.0,
spreadRadius: 0.5,
),

View File

@ -12,11 +12,16 @@ class MainTab extends StatefulWidget {
class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
TabController _controller;
Decoration getIndicator() {
return const UnderlineTabIndicator(
borderSide: BorderSide(color: Colors.red, width: 2),
insets: EdgeInsets.only(left:10.0,right: 10.0, top:10.0,)
);}
Decoration getIndicator(BuildContext context) {
return UnderlineTabIndicator(
borderSide: BorderSide(color: Theme.of(context).accentColor, width: 2),
insets: EdgeInsets.only(
left: 10.0,
right: 10.0,
top: 10.0,
));
}
@override
void initState() {
super.initState();
@ -41,17 +46,23 @@ class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
alignment: Alignment.centerLeft,
child: TabBar(
isScrollable: true,
labelPadding:
EdgeInsets.all(10.0),
labelPadding: EdgeInsets.all(10.0),
controller: _controller,
labelColor: Colors.red,
unselectedLabelColor: Colors.black,
indicator: getIndicator(),
indicator: getIndicator(context),
tabs: <Widget>[
Text('Recent Update',style: TextStyle(fontWeight: FontWeight.bold),),
Text('Favorites',style: TextStyle(fontWeight: FontWeight.bold),),
Text('Downloads',style: TextStyle(fontWeight: FontWeight.bold),),
],
Text(
'Recent Update',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
'Favorites',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
'Downloads',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
Expanded(
@ -96,7 +107,13 @@ class _RecentUpdateState extends State<RecentUpdate> {
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return (snapshot.hasData)
? EpisodeGrid(podcast: snapshot.data, showDownload: false, showFavorite: false, showNumber: false, heroTag: 'recent',)
? EpisodeGrid(
podcast: snapshot.data,
showDownload: false,
showFavorite: false,
showNumber: false,
heroTag: 'recent',
)
: Center(child: CircularProgressIndicator());
},
);
@ -111,7 +128,7 @@ class MyFavorite extends StatefulWidget {
class _MyFavoriteState extends State<MyFavorite> {
Future<List<EpisodeBrief>> _getLikedRssItem() async {
var dbHelper = DBHelper();
List<EpisodeBrief> episodes =await dbHelper.getLikedRssItem();
List<EpisodeBrief> episodes = await dbHelper.getLikedRssItem();
return episodes;
}
@ -122,26 +139,31 @@ class _MyFavoriteState extends State<MyFavorite> {
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return (snapshot.hasData)
? EpisodeGrid(podcast: snapshot.data, showDownload: false, showFavorite: false, showNumber: false, heroTag: 'favorite',)
? EpisodeGrid(
podcast: snapshot.data,
showDownload: false,
showFavorite: false,
showNumber: false,
heroTag: 'favorite',
)
: Center(child: CircularProgressIndicator());
},
);
}
}
class MyDownload extends StatefulWidget {
class MyDownload extends StatefulWidget {
@override
_MyDownloadState createState() => _MyDownloadState();
}
class _MyDownloadState extends State<MyDownload> {
Future<List<EpisodeBrief>> _getDownloadedRssItem() async {
var dbHelper = DBHelper();
List<EpisodeBrief> episodes =await dbHelper.getDownloadedRssItem();
List<EpisodeBrief> episodes = await dbHelper.getDownloadedRssItem();
return episodes;
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<EpisodeBrief>>(
@ -149,10 +171,15 @@ class _MyDownloadState extends State<MyDownload> {
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return (snapshot.hasData)
? EpisodeGrid(podcast: snapshot.data, showDownload: true, showFavorite: false, showNumber: false, heroTag: 'download',)
? EpisodeGrid(
podcast: snapshot.data,
showDownload: true,
showFavorite: false,
showNumber: false,
heroTag: 'download',
)
: Center(child: CircularProgressIndicator());
},
);
}
}

View File

@ -31,4 +31,15 @@ class KeyValueStorage {
json.encode(
{'groups': groupList.map((group) => group.toJson()).toList()}));
}
Future<bool> saveTheme(int setting) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.setInt(key, setting);
}
Future<int> getTheme() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
if(prefs.getInt(key) == null) await prefs.setInt(key, 0);
return prefs.getInt(key);
}
}

View File

@ -28,14 +28,35 @@ void main() async {
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.grey[100]));
var theme = Provider.of<SettingState>(context).theme;
return MaterialApp(
themeMode: theme == 0
? ThemeMode.system
: theme == 1 ? ThemeMode.dark : ThemeMode.light,
debugShowCheckedModeBanner: false,
title: 'TsacDop',
theme: ThemeData(
primaryColor: Colors.white,
accentColorBrightness: Brightness.dark,
primaryColor: Colors.grey[100],
accentColor: Colors.blue[400],
primaryColorLight: Colors.white,
primaryColorDark: Colors.grey[300],
dialogBackgroundColor: Colors.white,
backgroundColor: Colors.grey[100],
appBarTheme: AppBarTheme(
color: Colors.grey[100],
elevation: 0,
),
textTheme: TextTheme(
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
bodyText2: TextStyle(fontSize: 15.0, fontWeight: FontWeight.normal),
),
tabBarTheme: TabBarTheme(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey[400],
),
),
darkTheme: ThemeData.dark(),
home: MyHomePage(),
);
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tsacdop/class/podcastlocal.dart';
@ -21,48 +22,62 @@ class _PodcastDetailState extends State<PodcastDetail> {
Future _updateRssItem(PodcastLocal podcastLocal) async {
var dbHelper = DBHelper();
final result = await dbHelper.updatePodcastRss(podcastLocal);
result == 0 ?
Fluttertoast.showToast(
msg: 'No Update',
gravity: ToastGravity.TOP,
)
: Fluttertoast.showToast(
msg: 'Updated $result Episodes',
gravity: ToastGravity.TOP,
);
if(mounted) setState(() {});
result == 0
? Fluttertoast.showToast(
msg: 'No Update',
gravity: ToastGravity.TOP,
)
: Fluttertoast.showToast(
msg: 'Updated $result Episodes',
gravity: ToastGravity.TOP,
);
if (mounted) setState(() {});
}
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal) async {
print(podcastLocal.id);
var dbHelper = DBHelper();
List<EpisodeBrief> episodes = await
dbHelper.getRssItem(podcastLocal.id);
List<EpisodeBrief> episodes = await dbHelper.getRssItem(podcastLocal.id);
return episodes;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.podcastLocal.title,),
elevation: 0.0,
backgroundColor: Colors.grey[100],
centerTitle: true,
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
statusBarColor: Theme.of(context).primaryColor,
),
child: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
widget.podcastLocal.title,
),
centerTitle: true,
),
body: RefreshIndicator(
key: _refreshIndicatorKey,
color: Colors.blue[500],
onRefresh: () => _updateRssItem(widget.podcastLocal),
child: FutureBuilder<List<EpisodeBrief>>(
future: _getRssItem(widget.podcastLocal),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return (snapshot.hasData)
? EpisodeGrid(
podcast: snapshot.data,
showDownload: true,
showFavorite: true,
showNumber: true,
heroTag: 'podcast',
)
: Center(child: CircularProgressIndicator());
},
)),
),
body: RefreshIndicator(
key: _refreshIndicatorKey,
color: Colors.blue[500],
onRefresh: () => _updateRssItem(widget.podcastLocal),
child: FutureBuilder<List<EpisodeBrief>>(
future: _getRssItem(widget.podcastLocal),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return (snapshot.hasData)
? EpisodeGrid(podcast: snapshot.data, showDownload: true, showFavorite: true, showNumber: true, heroTag: 'podcast',)
: Center(child: CircularProgressIndicator());
},
)),
),
);
}
}

View File

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/class/podcast_group.dart';
@ -36,14 +37,15 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
width: _loadSave ? 70 : 0,
height: 60,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
boxShadow: [BoxShadow(
color: Colors.grey[700],
blurRadius: 5,
offset: Offset(1, 1),
),]
),
color: Colors.blue,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey[700],
blurRadius: 5,
offset: Offset(1, 1),
),
]),
alignment: Alignment.center,
child: Text(
'Save',
@ -68,10 +70,10 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
Widget build(BuildContext context) {
return widget.group.podcastList.length == 0
? Container(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
)
: Container(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
child: Stack(
children: <Widget>[
ReorderableListView(
@ -89,7 +91,8 @@ class _PodcastGroupListState extends State<PodcastGroupList> {
children: widget.group.podcasts
.map<Widget>((PodcastLocal podcastLocal) {
return Container(
decoration: BoxDecoration(color: Colors.grey[100]),
decoration:
BoxDecoration(color: Theme.of(context).primaryColor),
key: ObjectKey(podcastLocal.title),
child: PodcastCard(
podcastLocal: podcastLocal,
@ -147,10 +150,17 @@ class _PodcastCardState extends State<PodcastCard> {
@override
Widget build(BuildContext context) {
var color = json.decode(widget.podcastLocal.primaryColor);
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
if (Theme.of(context).brightness == Brightness.light) {
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
} else {
(color[0] < 50 && color[1] < 50 && color[2] < 50)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
}
double _width = MediaQuery.of(context).size.width;
var _groupList = Provider.of<GroupList>(context);
_belongGroups = _groupList.getPodcastGroup(widget.podcastLocal.id);
@ -219,27 +229,35 @@ class _PodcastCardState extends State<PodcastCard> {
onPressed: () {
showDialog(
context: context,
child: AlertDialog(
elevation: 2.0,
title: Text('Remove confirm'),
content: Text(
'${widget.podcastLocal.title} will be removed from device.'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('CANCEL'),
),
FlatButton(
onPressed: () {
_groupList.removePodcast(widget.podcastLocal.id);
Navigator.of(context).pop();
},
child: Text(
'CONFIRM',
style: TextStyle(color: Colors.red),
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor:
Colors.black.withOpacity(0.5),
statusBarColor: Colors.red,
),
child: AlertDialog(
elevation: 2.0,
title: Text('Remove confirm'),
content: Text(
'${widget.podcastLocal.title} will be removed from device.'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('CANCEL'),
),
)
],
FlatButton(
onPressed: () {
_groupList
.removePodcast(widget.podcastLocal.id);
Navigator.of(context).pop();
},
child: Text(
'CONFIRM',
style: TextStyle(color: Colors.red),
),
)
],
),
));
},
),
@ -251,10 +269,12 @@ class _PodcastCardState extends State<PodcastCard> {
: Container(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[100],
color: Theme.of(context).primaryColor,
border: Border(
bottom: BorderSide(color: Colors.grey[300]),
top: BorderSide(color: Colors.grey[300]))),
bottom: BorderSide(
color: Theme.of(context).primaryColorDark),
top: BorderSide(
color: Theme.of(context).primaryColorDark))),
height: 50,
child: _addGroup
? Row(

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/class/podcast_group.dart';
@ -52,7 +53,6 @@ class _AboutPodcastState extends State<AboutPodcast> {
_groupList.removePodcast(widget.podcastLocal.id);
Navigator.of(context).pop();
},
color: Colors.grey[200],
textColor: Colors.red,
child: Text(
'UNSUBSCRIBE',
@ -83,7 +83,6 @@ class PodcastList extends StatefulWidget {
}
class _PodcastListState extends State<PodcastList> {
Future<List<PodcastLocal>> getPodcastLocal() async {
var dbHelper = DBHelper();
var podcastList = await dbHelper.getPodcastLocalAll();
@ -93,109 +92,100 @@ class _PodcastListState extends State<PodcastList> {
@override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('Podcasts'),
centerTitle: true,
backgroundColor: Colors.grey[100],
elevation: 0,
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
statusBarColor: Theme.of(context).primaryColor,
),
body: Container(
color: Colors.grey[100],
child: FutureBuilder<List<PodcastLocal>>(
future: getPodcastLocal(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return CustomScrollView(
primary: false,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(10.0),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 0.8,
crossAxisCount: 3,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return InkWell(
onTap: () {
Navigator.push(
context,
ScaleRoute(
page: PodcastDetail(
podcastLocal: snapshot.data[index],
)),
child: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Podcasts'),
centerTitle: true,
),
body: Container(
color: Theme.of(context).primaryColor,
child: FutureBuilder<List<PodcastLocal>>(
future: getPodcastLocal(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return CustomScrollView(
primary: false,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(10.0),
sliver: SliverGrid(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 0.8,
crossAxisCount: 3,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return InkWell(
onTap: () {
Navigator.push(
context,
ScaleRoute(
page: PodcastDetail(
podcastLocal: snapshot.data[index],
)),
);
},
onLongPress: () {
showDialog(
context: context,
builder: (BuildContext context) =>
AboutPodcast(
podcastLocal: snapshot.data[index]),
).then((_) => setState(() {}));
},
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: 10.0,
),
ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(_width / 8)),
child: Container(
height: _width / 4,
width: _width / 4,
child: Image.file(File(
"${snapshot.data[index].imagePath}")),
),
),
Container(
padding: EdgeInsets.all(4.0),
child: Text(
snapshot.data[index].title,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyText1,
maxLines: 2,
),
),
],
),
),
);
},
onLongPress: () {
showDialog(
context: context,
builder: (BuildContext context) => AboutPodcast(
podcastLocal: snapshot.data[index]),
).then((_) => setState(() {}));
},
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: 10.0,
),
ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(_width / 8)),
child: Container(
height: _width / 4,
width: _width / 4,
child: Image.file(File(
"${snapshot.data[index].imagePath}")),
),
),
Container(
padding: EdgeInsets.all(4.0),
child: Text(
snapshot.data[index].title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
color: Colors.black.withOpacity(0.5),
),
maxLines: 2,
),
),
],
),
),
);
},
childCount: snapshot.data.length,
childCount: snapshot.data.length,
),
),
),
),
),
],
);
}
return Text('NoData');
},
],
);
}
return Text('NoData');
},
),
),
),
),
);
}
}
class Podcast extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey[100],
elevation: 0,
centerTitle: true,
title: Text('Podcasts'),
),
body: Container(child: PodcastList()),
);
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/class/podcast_group.dart';
import 'package:tsacdop/podcasts/podcastgroup.dart';
@ -26,86 +27,85 @@ class _PodcastManageState extends State<PodcastManage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: true,
backgroundColor: Colors.grey[100],
title: Text('Groups'),
actions: <Widget>[
OrderMenu(),
],
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
statusBarColor: Theme.of(context).primaryColor,
),
body: Consumer<GroupList>(builder: (_, groupList, __) {
bool _isLoading = groupList.isLoading;
List<PodcastGroup> _groups = groupList.groups;
return _isLoading
? Center()
: DefaultTabController(
length: _groups.length,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
child: SafeArea(
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Groups'),
actions: <Widget>[
IconButton(
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) => AddGroup()),
icon: Icon(Icons.add)),
OrderMenu(),
],
),
body: Consumer<GroupList>(builder: (_, groupList, __) {
bool _isLoading = groupList.isLoading;
List<PodcastGroup> _groups = groupList.groups;
return _isLoading
? Center()
: DefaultTabController(
length: _groups.length,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
flex: 4,
child: Container(
height: 50,
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
child: TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey[500],
labelPadding: EdgeInsets.all(5.0),
indicator: getIndicator(),
isScrollable: true,
tabs: _groups.map<Tab>((group) {
return Tab(
child: Container(
height: 30.0,
padding: EdgeInsets.symmetric(
horizontal: 10.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.all(
Radius.circular(15)),
),
child: Text(
group.name,
)),
);
}).toList(),
),
Container(
height: 50,
padding: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
child: TabBar(
// labelColor: Colors.black,
// unselectedLabelColor: Colors.grey[500],
labelPadding: EdgeInsets.all(5.0),
indicator: getIndicator(),
isScrollable: true,
tabs: _groups.map<Tab>((group) {
return Tab(
child: Container(
height: 30.0,
padding:
EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).brightness ==
Brightness.light
? Theme.of(context).primaryColorDark
: Colors.grey[800],
borderRadius:
BorderRadius.all(Radius.circular(15)),
),
child: Text(
group.name,
)),
);
}).toList(),
),
),
Expanded(
flex: 1,
child: IconButton(
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) =>
AddGroup()),
icon: Icon(Icons.add)),
),
child: Container(
child: TabBarView(
children: _groups.map<Widget>((group) {
return Container(
key: ObjectKey(group),
child: PodcastGroupList(group: group));
}).toList(),
),
),
)
],
),
Expanded(
child: Container(
child: TabBarView(
children: _groups.map<Widget>((group) {
return Container(
key: ObjectKey(group),
child: PodcastGroupList(group: group));
}).toList(),
),
),
)
],
));
}),
));
}),
),
),
);
}
}
@ -166,66 +166,72 @@ class _AddGroupState extends State<AddGroup> {
Widget build(BuildContext context) {
var groupList = Provider.of<GroupList>(context);
List list = groupList.groups.map((e) => e.name).toList();
return AlertDialog(
elevation: 1,
contentPadding: EdgeInsets.symmetric(horizontal: 20),
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
actionsPadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'CANCEL',
style: TextStyle(color: Colors.grey[700]),
),
),
FlatButton(
onPressed: () async {
if (list.contains(_newGroup)) {
setState(() => _error = 1);
} else {
groupList.addGroup(PodcastGroup(_newGroup));
Navigator.of(context).pop();
}
},
child: Text('DONE'),
)
],
title: Text('Create new group'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 10),
hintText: 'New Group',
hintStyle: TextStyle(fontSize: 18),
filled: true,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white, width: 2.0),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2.0),
),
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor: Colors.black.withOpacity(0.5),
statusBarColor: Colors.red,
),
child: AlertDialog(
elevation: 1,
contentPadding: EdgeInsets.symmetric(horizontal: 20),
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
actionsPadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'CANCEL',
style: TextStyle(color: Colors.grey[600]),
),
cursorRadius: Radius.circular(2),
autofocus: true,
maxLines: 1,
controller: _controller,
onChanged: (value) {
_newGroup = value;
),
FlatButton(
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(
'Group existed',
style: TextStyle(color: Colors.red[400]),
)
: Center(),
),
child: Text('DONE'),
)
],
title: Text('Create new group'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 10),
hintText: 'New Group',
hintStyle: TextStyle(fontSize: 18),
filled: true,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2.0),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue, 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(
'Group existed',
style: TextStyle(color: Colors.red[400]),
)
: Center(),
),
],
),
),
);
}

View File

@ -45,10 +45,17 @@ class EpisodeGrid extends StatelessWidget {
(BuildContext context, int index) {
Color _c;
var color = json.decode(podcast[index].primaryColor);
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
if (Theme.of(context).brightness == Brightness.light) {
(color[0] > 200 && color[1] > 200 && color[2] > 200)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
} else {
(color[0] < 50 && color[1] < 50 && color[2] < 50)
? _c = Color.fromRGBO(
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
}
return Material(
color: Colors.transparent,
child: InkWell(
@ -67,13 +74,16 @@ class EpisodeGrid extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(5.0)),
color: Theme.of(context).scaffoldBackgroundColor,
border: Border.all(
color: Colors.grey[100],
color:
Theme.of(context).brightness == Brightness.light
? Theme.of(context).primaryColor
: Theme.of(context).scaffoldBackgroundColor,
width: 3.0,
),
boxShadow: [
BoxShadow(
color: Colors.grey[100],
blurRadius: 1.0,
color: Theme.of(context).primaryColor,
blurRadius: 0.5,
spreadRadius: 0.5,
),
]),