mirror of
https://github.com/stonega/tsacdop
synced 2025-02-04 01:17:47 +01:00
new file: lib/class/podcast_group.dart
modified: lib/class/podcastlocal.dart modified: lib/class/settingstate.dart modified: lib/episodes/episodedetail.dart modified: lib/episodes/episodedownload.dart modified: lib/home/appbar/addpodcast.dart modified: lib/home/appbar/popupmenu.dart modified: lib/home/audio_player.dart modified: lib/home/home.dart modified: lib/home/homescroll.dart modified: lib/home/hometab.dart new file: lib/local_storage/key_value_storage.dart renamed: lib/class/sqflite_localpodcast.dart -> lib/local_storage/sqflite_localpodcast.dart modified: lib/main.dart modified: lib/podcasts/podcastdetail.dart new file: lib/podcasts/podcastgroup.dart modified: lib/podcasts/podcastlist.dart modified: lib/podcasts/podcastmanage.dart modified: lib/util/episodegrid.dart modified: lib/webfeed/domain/rss_item.dart modified: lib/webfeed/domain/rss_itunes.dart modified: pubspec.lock modified: pubspec.yaml Add podcast group support Manage group with provider
This commit is contained in:
parent
d3efce463c
commit
16567a7199
158
lib/class/podcast_group.dart
Normal file
158
lib/class/podcast_group.dart
Normal file
@ -0,0 +1,158 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:core';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
|
||||
class GroupEntity {
|
||||
final String name;
|
||||
final String id;
|
||||
final String color;
|
||||
final List<String> podcastList;
|
||||
|
||||
GroupEntity(this.name, this.id, this.color, this.podcastList);
|
||||
|
||||
Map<String, Object> toJson() {
|
||||
return {'name': name, 'id': id, 'color': color, 'podcastList': podcastList};
|
||||
}
|
||||
|
||||
static GroupEntity fromJson(Map<String, Object> json) {
|
||||
List<String> list = List.from(json['podcastList']);
|
||||
print(json['[podcastList']);
|
||||
return
|
||||
GroupEntity(
|
||||
json['name'] as String,
|
||||
json['id'] as String,
|
||||
json['color'] as String,
|
||||
list);
|
||||
}
|
||||
}
|
||||
|
||||
class PodcastGroup {
|
||||
final String name;
|
||||
final String id;
|
||||
final String color;
|
||||
final List<String> podcastList;
|
||||
|
||||
PodcastGroup(this.name,
|
||||
{this.color = '#000000', String id, List<String> podcastList})
|
||||
: id = id ?? Uuid().v4(),
|
||||
podcastList = podcastList ?? [];
|
||||
|
||||
Future getPodcasts() async {
|
||||
var dbHelper = DBHelper();
|
||||
if (podcastList != []) {
|
||||
_podcasts = await dbHelper.getPodcastLocal(podcastList, 0);
|
||||
}
|
||||
}
|
||||
|
||||
List<PodcastLocal> _podcasts;
|
||||
|
||||
List<PodcastLocal> get podcasts => _podcasts;
|
||||
|
||||
GroupEntity toEntity() {
|
||||
return GroupEntity(name, id, color, podcastList);
|
||||
}
|
||||
|
||||
static PodcastGroup fromEntity(GroupEntity entity) {
|
||||
return PodcastGroup(
|
||||
entity.name,
|
||||
id: entity.id,
|
||||
color: entity.color,
|
||||
podcastList: entity.podcastList,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GroupList extends ChangeNotifier {
|
||||
List<PodcastGroup> _groups;
|
||||
DBHelper dbHelper = DBHelper();
|
||||
UnmodifiableListView<PodcastGroup> get groups =>
|
||||
UnmodifiableListView(_groups);
|
||||
|
||||
KeyValueStorage storage = KeyValueStorage('groups');
|
||||
GroupList({List<PodcastGroup> groups}) : _groups = groups ?? [];
|
||||
|
||||
bool _isLoading = false;
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
@override
|
||||
void addListener(VoidCallback listener) {
|
||||
super.addListener(listener);
|
||||
loadGroups();
|
||||
}
|
||||
|
||||
Future loadGroups() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
storage.getGroups().then((loadgroups) async {
|
||||
_groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e)));
|
||||
await Future.forEach(_groups, (group) async {
|
||||
await group.getPodcasts();
|
||||
});
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
Future addGroup(PodcastGroup podcastGroup) async {
|
||||
_groups.add(podcastGroup);
|
||||
_saveGroup();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future delGroup(PodcastGroup podcastGroup) async {
|
||||
_groups.remove(podcastGroup);
|
||||
notifyListeners();
|
||||
_saveGroup();
|
||||
}
|
||||
|
||||
void updateGroup(PodcastGroup podcastGroup) {
|
||||
var oldGroup = _groups.firstWhere((it) => it.id == podcastGroup.id);
|
||||
var index = _groups.indexOf(oldGroup);
|
||||
_groups.replaceRange(index, index + 1, [podcastGroup]);
|
||||
notifyListeners();
|
||||
_saveGroup();
|
||||
}
|
||||
|
||||
void _saveGroup() {
|
||||
storage.saveGroup(_groups.map((it) => it.toEntity()).toList());
|
||||
}
|
||||
|
||||
Future subscribe(PodcastLocal podcastLocal) async {
|
||||
_groups[0].podcastList.add(podcastLocal.id);
|
||||
_saveGroup();
|
||||
await dbHelper.savePodcastLocal(podcastLocal);
|
||||
await _groups[0].getPodcasts();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<PodcastGroup> getPodcastGroup(String id) {
|
||||
List<PodcastGroup> result =[];
|
||||
_groups.forEach((group) {
|
||||
if (group.podcastList.contains(id)) {
|
||||
result.add(group);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
changeGroup(String id, List<String> l) {
|
||||
_groups.forEach((group) {
|
||||
if (group.podcastList.contains(id)) {
|
||||
group.podcastList.remove(id);
|
||||
}
|
||||
});
|
||||
l.forEach((s) {
|
||||
_groups.forEach((group) {
|
||||
if (group.name == s) group.podcastList.add(id);
|
||||
});
|
||||
});
|
||||
notifyListeners();
|
||||
_saveGroup();
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class PodcastLocal {
|
||||
final String title;
|
||||
final String imageUrl;
|
||||
@ -6,5 +8,7 @@ class PodcastLocal {
|
||||
final String author;
|
||||
String description;
|
||||
final String primaryColor;
|
||||
PodcastLocal(this.title, this.imageUrl, this.rssUrl, this.primaryColor, this.author);
|
||||
}
|
||||
final String id;
|
||||
PodcastLocal(this.title, this.imageUrl, this.rssUrl, this.primaryColor, this.author,{String id}) : id = id ?? Uuid().v4();
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,13 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
enum Setting {start, stop}
|
||||
//two types podcast update, backhome nedd to back to default grooup.
|
||||
enum Update {backhome, justupdate}
|
||||
class SettingState extends ChangeNotifier{
|
||||
Setting _subscribeupdate;
|
||||
Setting get subscribeupdate => _subscribeupdate;
|
||||
set subscribeUpdate(Setting s){
|
||||
Update _subscribeupdate;
|
||||
Update get subscribeupdate => _subscribeupdate;
|
||||
set subscribeUpdate(Update s){
|
||||
_subscribeupdate = s;
|
||||
notifyListeners();
|
||||
}
|
||||
Setting _themeupdate;
|
||||
Setting get themeUpdate => _themeupdate;
|
||||
set themeUpdate(Setting s){
|
||||
_themeupdate = s;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,22 +1,23 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:tsacdop/class/audiostate.dart';
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'episodedownload.dart';
|
||||
|
||||
enum DownloadState { stop, load, donwload, complete, error }
|
||||
|
||||
class EpisodeDetail extends StatefulWidget {
|
||||
final EpisodeBrief episodeItem;
|
||||
final String heroTag;
|
||||
EpisodeDetail({this.episodeItem, this.heroTag, Key key}) : super(key: key);
|
||||
final String path;
|
||||
EpisodeDetail({this.episodeItem, this.heroTag, this.path, Key key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_EpisodeDetailState createState() => _EpisodeDetailState();
|
||||
@ -26,7 +27,10 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||
final textstyle = TextStyle(fontSize: 15.0, color: Colors.black);
|
||||
double downloadProgress;
|
||||
bool _loaddes;
|
||||
String path;
|
||||
Future getSDescription(String title) async {
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
path = dir.path;
|
||||
var dbHelper = DBHelper();
|
||||
widget.episodeItem.description = await dbHelper.getDescription(title);
|
||||
if (mounted)
|
||||
@ -62,7 +66,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||
),
|
||||
body: Container(
|
||||
color: Colors.grey[100],
|
||||
padding: EdgeInsets.all(12.0),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -77,7 +81,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
widget.episodeItem.title,
|
||||
style: Theme.of(context).textTheme.title,
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
@ -144,22 +148,26 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 12.0, right: 12.0, top: 5.0),
|
||||
child: SingleChildScrollView(
|
||||
child: (widget.episodeItem.description != null && _loaddes)
|
||||
? Html(
|
||||
data: widget.episodeItem.description,
|
||||
onLinkTap: (url) {
|
||||
_launchUrl(url);
|
||||
},
|
||||
useRichText: true,
|
||||
)
|
||||
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,
|
||||
),
|
||||
episodeItem: widget.episodeItem,
|
||||
heroTag: widget.heroTag,
|
||||
path: widget.path),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -168,9 +176,11 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||
}
|
||||
|
||||
class MenuBar extends StatefulWidget {
|
||||
final String path;
|
||||
final EpisodeBrief episodeItem;
|
||||
final String heroTag;
|
||||
MenuBar({this.episodeItem, this.heroTag, Key key}) : super(key: key);
|
||||
MenuBar({this.episodeItem, this.heroTag, this.path, Key key})
|
||||
: super(key: key);
|
||||
@override
|
||||
_MenuBarState createState() => _MenuBarState();
|
||||
}
|
||||
@ -204,7 +214,7 @@ class _MenuBarState extends State<MenuBar> {
|
||||
_like = widget.episodeItem.liked;
|
||||
}
|
||||
|
||||
Widget _buttonOnMenu(Widget widget, Function() onTap) => Material(
|
||||
Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
@ -232,7 +242,8 @@ class _MenuBarState extends State<MenuBar> {
|
||||
(widget.episodeItem.title == urlChange.title &&
|
||||
urlChange.audioState == AudioState.play)
|
||||
? ImageRotate(
|
||||
url: widget.episodeItem.imageUrl,
|
||||
title: widget.episodeItem.feedTitle,
|
||||
path: widget.path,
|
||||
)
|
||||
: Hero(
|
||||
tag: widget.episodeItem.enclosureUrl + widget.heroTag,
|
||||
@ -244,9 +255,8 @@ class _MenuBarState extends State<MenuBar> {
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
color: Colors.white,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: widget.episodeItem.imageUrl,
|
||||
),
|
||||
child: Image.file(File(
|
||||
"${widget.path}/${widget.episodeItem.feedTitle}.png")),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -298,10 +308,18 @@ class _MenuBarState extends State<MenuBar> {
|
||||
alignment: Alignment.center,
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child:Row(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Text('Play Now', style: TextStyle(color: Colors.blue, fontSize: 15, fontWeight: FontWeight.bold,)),
|
||||
Icon(Icons.play_arrow, color: Colors.blue,),
|
||||
Text('Play Now',
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
Icon(
|
||||
Icons.play_arrow,
|
||||
color: Colors.blue,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -487,8 +505,9 @@ class _WaveLoaderState extends State<WaveLoader>
|
||||
}
|
||||
|
||||
class ImageRotate extends StatefulWidget {
|
||||
final String url;
|
||||
ImageRotate({this.url, Key key}) : super(key: key);
|
||||
final String title;
|
||||
final String path;
|
||||
ImageRotate({this.title, this.path, Key key}) : super(key: key);
|
||||
@override
|
||||
_ImageRotateState createState() => _ImageRotateState();
|
||||
}
|
||||
@ -541,9 +560,7 @@ class _ImageRotateState extends State<ImageRotate>
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
color: Colors.white,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: widget.url,
|
||||
),
|
||||
child: Image.file(File("${widget.path}/${widget.title}.png")),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -590,35 +607,26 @@ class LoveOpen extends StatefulWidget {
|
||||
class _LoveOpenState extends State<LoveOpen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
Animation _animationA;
|
||||
Animation _animationB;
|
||||
AnimationController _controller;
|
||||
|
||||
var rect = RelativeRect.fromLTRB(100, 100, 100, 100);
|
||||
var rectend = RelativeRect.fromLTRB(0, 0, 0, 0);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: Duration(milliseconds: 500),
|
||||
duration: Duration(milliseconds: 100),
|
||||
);
|
||||
|
||||
_animationA = Tween(begin: 0.0, end: 1.0).animate(_controller)
|
||||
..addListener(() {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
_animationB =
|
||||
RelativeRectTween(begin: rect, end: rectend).animate(_controller)
|
||||
..addListener(() {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
|
||||
_controller.forward();
|
||||
_controller.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
_controller.reset();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -635,8 +643,8 @@ class _LoveOpenState extends State<LoveOpen>
|
||||
scale: _animationA,
|
||||
alignment: Alignment.center,
|
||||
child: Transform.rotate(
|
||||
angle: angle,
|
||||
child: SizedBox(
|
||||
angle: angle,
|
||||
child: SizedBox(
|
||||
height: 5 * scale,
|
||||
width: 6 * scale,
|
||||
child: CustomPaint(
|
||||
@ -658,22 +666,22 @@ class _LoveOpenState extends State<LoveOpen>
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
_littleHeart(1.3, 10, -math.pi/6),
|
||||
_littleHeart(1.5, 3, 0),
|
||||
_littleHeart(0.5, 10, -math.pi / 6),
|
||||
_littleHeart(1.2, 3, 0),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
_littleHeart(0.8, 6, math.pi*1.5),
|
||||
_littleHeart(1.2, 24, math.pi/2),
|
||||
_littleHeart(0.8, 6, math.pi * 1.5),
|
||||
_littleHeart(0.9, 24, math.pi / 2),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_littleHeart(1, 8,-math.pi*0.7),
|
||||
_littleHeart(1.3, 8, math.pi),
|
||||
_littleHeart(1.1, 3, -math.pi*1.2)
|
||||
_littleHeart(1, 8, -math.pi * 0.7),
|
||||
_littleHeart(0.8, 8, math.pi),
|
||||
_littleHeart(0.6, 3, -math.pi * 1.2)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -8,8 +8,9 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:tsacdop/class/downloadstate.dart';
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
|
||||
class DownloadButton extends StatefulWidget {
|
||||
final EpisodeBrief episodeBrief;
|
||||
@ -150,9 +151,9 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||
final tasks = await FlutterDownloader.loadTasks();
|
||||
|
||||
_task = _TaskInfo(
|
||||
name: widget.episodeBrief.title,
|
||||
link: widget.episodeBrief.enclosureUrl);
|
||||
|
||||
name: widget.episodeBrief.title,
|
||||
link: widget.episodeBrief.enclosureUrl,
|
||||
);
|
||||
tasks?.forEach((task) {
|
||||
if (_task.link == task.url) {
|
||||
_task.taskId = task.taskId;
|
||||
@ -203,13 +204,28 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _downloadButton(_task);
|
||||
return _isLoading
|
||||
? Center()
|
||||
: Row(
|
||||
children: <Widget>[
|
||||
_downloadButton(_task),
|
||||
AnimatedContainer(
|
||||
duration: Duration(seconds: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.cyan[300],
|
||||
borderRadius: BorderRadius.all(Radius.circular(15.0))),
|
||||
height: 20.0,
|
||||
width:
|
||||
(_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
|
||||
alignment: Alignment.center,
|
||||
child: Text('${_task.progress}%',
|
||||
style: TextStyle(color: Colors.white))),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _downloadButton(_TaskInfo task) {
|
||||
if (_isLoading)
|
||||
return Center();
|
||||
else if (task.status == DownloadTaskStatus.undefined) {
|
||||
if (task.status == DownloadTaskStatus.undefined) {
|
||||
return _buttonOnMenu(
|
||||
Icon(
|
||||
Icons.arrow_downward,
|
||||
@ -217,33 +233,28 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||
),
|
||||
() => _requestDownload(task));
|
||||
} else if (task.status == DownloadTaskStatus.running) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_pauseDownload(task);
|
||||
},
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: SizedBox(
|
||||
height: 18,
|
||||
width: 18,
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: Colors.grey[500],
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
|
||||
value: task.progress / 100,
|
||||
),
|
||||
),
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
(task.progress > 0) ? _pauseDownload(task) : null;
|
||||
},
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: SizedBox(
|
||||
height: 18,
|
||||
width: 18,
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: Colors.grey[500],
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.cyan[300]),
|
||||
value: task.progress / 100,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text('${task.progress}%', style: TextStyle(color: Colors.blue,),),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else if (task.status == DownloadTaskStatus.paused) {
|
||||
return Material(
|
||||
|
@ -11,9 +11,11 @@ import 'package:image/image.dart' as img;
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
import 'package:tsacdop/class/importompl.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
import 'package:tsacdop/class/searchpodcast.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/class/settingstate.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/home/home.dart';
|
||||
import 'package:tsacdop/home/appbar/popupmenu.dart';
|
||||
import 'package:tsacdop/webfeed/webfeed.dart';
|
||||
@ -211,7 +213,8 @@ class _SearchResultState extends State<SearchResult> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context);
|
||||
|
||||
var groupList = Provider.of<GroupList>(context);
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
savePodcast(String rss) async {
|
||||
print(rss);
|
||||
if (mounted) setState(() => _adding = true);
|
||||
@ -240,17 +243,19 @@ class _SearchResultState extends State<SearchResult> {
|
||||
PodcastLocal podcastLocal = PodcastLocal(
|
||||
_p.title, _p.itunes.image.href, rss, _primaryColor, _p.author);
|
||||
podcastLocal.description = _p.description;
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.savePodcastLocal(podcastLocal);
|
||||
groupList.subscribe(podcastLocal);
|
||||
|
||||
importOmpl.importState = ImportState.parse;
|
||||
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.savePodcastRss(_p);
|
||||
|
||||
importOmpl.importState = ImportState.complete;
|
||||
importOmpl.importState = ImportState.stop;
|
||||
|
||||
_settingState.subscribeUpdate = Update.backhome;
|
||||
|
||||
print('fatch data');
|
||||
} catch (e) {
|
||||
importOmpl.importState = ImportState.error;
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Network error, Subscribe failed',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
|
@ -4,6 +4,7 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:provider/provider.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';
|
||||
@ -13,7 +14,7 @@ import 'package:image/image.dart' as img;
|
||||
|
||||
import 'about.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/class/importompl.dart';
|
||||
import 'package:tsacdop/webfeed/webfeed.dart';
|
||||
|
||||
@ -43,10 +44,11 @@ class PopupMenu extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context);
|
||||
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
_refreshAll() async {
|
||||
var dbHelper = DBHelper();
|
||||
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocal();
|
||||
List<PodcastLocal> podcastList =
|
||||
await dbHelper.getPodcastLocalAll();
|
||||
await Future.forEach(podcastList, (podcastLocal) async {
|
||||
importOmpl.rssTitle = podcastLocal.title;
|
||||
importOmpl.importState = ImportState.parse;
|
||||
@ -83,12 +85,16 @@ class PopupMenu extends StatelessWidget {
|
||||
_p.title, _p.itunes.image.href, rss, _primaryColor, _p.author);
|
||||
|
||||
podcastLocal.description = _p.description;
|
||||
print('_p.description');
|
||||
|
||||
await dbHelper.savePodcastLocal(podcastLocal);
|
||||
|
||||
importOmpl.importState = ImportState.parse;
|
||||
|
||||
await dbHelper.savePodcastRss(_p);
|
||||
|
||||
importOmpl.importState = ImportState.complete;
|
||||
|
||||
_settingState.subscribeUpdate = Update.backhome;
|
||||
} catch (e) {
|
||||
importOmpl.importState = ImportState.error;
|
||||
}
|
||||
@ -110,8 +116,6 @@ class PopupMenu extends StatelessWidget {
|
||||
print(total[i].text);
|
||||
}
|
||||
}
|
||||
importOmpl.importState = ImportState.complete;
|
||||
importOmpl.importState = ImportState.stop;
|
||||
print('Import fisnished');
|
||||
} catch (e) {
|
||||
importOmpl.importState = ImportState.error;
|
||||
|
@ -15,7 +15,7 @@ import 'package:tsacdop/class/audiostate.dart';
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/episodes/episodedetail.dart';
|
||||
import 'package:tsacdop/home/audiopanel.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/util/pageroute.dart';
|
||||
|
||||
final Logger _logger = Logger('audiofileplayer');
|
||||
@ -84,7 +84,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
_remoteAudioLoading = true;
|
||||
Provider.of<Urlchange>(context, listen: false).audioState = AudioState.load;
|
||||
|
||||
if (_backgroundAudioPlaying == true) _backgroundAudio?.pause();
|
||||
if (_backgroundAudioPlaying == true)
|
||||
{ _backgroundAudio?.pause();
|
||||
AudioSystem.instance.stopBackgroundDisplay();}
|
||||
_backgroundAudio?.dispose();
|
||||
_backgroundAudio = Audio.loadFromRemoteUrl(url,
|
||||
onDuration: (double durationSeconds) {
|
||||
@ -135,7 +137,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
_remoteAudioLoading = true;
|
||||
ByteData audio = getAudio(path);
|
||||
Provider.of<Urlchange>(context, listen: false).audioState = AudioState.load;
|
||||
if (_backgroundAudioPlaying == true) _backgroundAudio?.pause();
|
||||
if (_backgroundAudioPlaying == true)
|
||||
{_backgroundAudio?.pause();
|
||||
AudioSystem.instance.stopBackgroundDisplay();}
|
||||
_backgroundAudio?.dispose();
|
||||
_backgroundAudio = Audio.loadFromByteData(audio,
|
||||
onDuration: (double durationSeconds) {
|
||||
|
@ -1,13 +1,10 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:tsacdop/podcasts/podcastlist.dart';
|
||||
import 'hometab.dart';
|
||||
import 'package:tsacdop/home/appbar/importompl.dart';
|
||||
import 'package:tsacdop/home/audio_player.dart';
|
||||
import 'homescroll.dart';
|
||||
import 'package:tsacdop/util/pageroute.dart';
|
||||
import 'package:tsacdop/podcasts/podcastmanage.dart';
|
||||
|
||||
class Home extends StatelessWidget {
|
||||
|
||||
@ -19,28 +16,6 @@ class Home extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Import(),
|
||||
Container(
|
||||
height: 30,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
alignment: Alignment.bottomRight,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(page: PodcastManage()),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 30,
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Text('See All',
|
||||
style: TextStyle(
|
||||
color: Colors.red[300],
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(child: ScrollPodcasts()),
|
||||
Expanded(
|
||||
child: MainTab(),
|
||||
|
@ -6,15 +6,17 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/importompl.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
|
||||
import 'package:tsacdop/episodes/episodedetail.dart';
|
||||
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||
import 'package:tsacdop/podcasts/podcastlist.dart';
|
||||
import 'package:tsacdop/podcasts/podcastmanage.dart';
|
||||
import 'package:tsacdop/util/pageroute.dart';
|
||||
import 'package:tsacdop/class/settingstate.dart';
|
||||
|
||||
@ -25,101 +27,184 @@ class ScrollPodcasts extends StatefulWidget {
|
||||
|
||||
class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
||||
var dir;
|
||||
bool _loading;
|
||||
List<PodcastLocal> podcastList;
|
||||
|
||||
getPodcastLocal() async {
|
||||
var dbHelper = DBHelper();
|
||||
podcastList = await dbHelper.getPodcastLocal();
|
||||
dir = await getApplicationDocumentsDirectory();
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
}
|
||||
int _groupIndex;
|
||||
bool _loaded;
|
||||
|
||||
ImportState importState;
|
||||
Setting subscribeUpdate;
|
||||
Update subscribeUpdate;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final importState = Provider.of<ImportOmpl>(context).importState;
|
||||
final subscribeUpdate = Provider.of<SettingState>(context).subscribeupdate;
|
||||
if (importState == ImportState.complete ||
|
||||
subscribeUpdate == Setting.start) {
|
||||
subscribeUpdate = Provider.of<SettingState>(context).subscribeupdate;
|
||||
if (subscribeUpdate == Update.backhome) {
|
||||
setState(() {
|
||||
getPodcastLocal();
|
||||
_groupIndex = 0;
|
||||
});
|
||||
} else if (subscribeUpdate == Update.justupdate) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loading = false;
|
||||
getPodcastLocal();
|
||||
_loaded = false;
|
||||
_groupIndex = 0;
|
||||
getApplicationDocumentsDirectory().then((value) {
|
||||
dir = value.path;
|
||||
setState(() => _loaded = true);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double _width = MediaQuery.of(context).size.width;
|
||||
return !_loading
|
||||
? Container(
|
||||
height: (_width - 20) / 3 + 110,
|
||||
)
|
||||
: DefaultTabController(
|
||||
length: podcastList.length,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 70,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
labelPadding: EdgeInsets.only(
|
||||
top: 5.0, bottom: 10.0, left: 6.0, right: 6.0),
|
||||
indicator:
|
||||
CircleTabIndicator(color: Colors.blue, radius: 3),
|
||||
isScrollable: true,
|
||||
tabs: podcastList.map<Tab>((PodcastLocal podcastLocal) {
|
||||
return Tab(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25.0)),
|
||||
child: LimitedBox(
|
||||
maxHeight: 50,
|
||||
maxWidth: 50,
|
||||
child: Image.file(
|
||||
File("${dir.path}/${podcastLocal.title}.png")),
|
||||
return Consumer<GroupList>(builder: (_, groupList, __) {
|
||||
var groups = groupList.groups;
|
||||
bool isLoading = groupList.isLoading;
|
||||
return isLoading
|
||||
? Container(
|
||||
height: (_width - 20) / 3 + 110,
|
||||
child: SizedBox(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
: DefaultTabController(
|
||||
length: groups[_groupIndex].podcastList.length,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onVerticalDragEnd: (event) {
|
||||
if (event.primaryVelocity > 200) {
|
||||
if (groups.length == 1) {
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Add some groups',
|
||||
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: 'Add some groups',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
setState(() {
|
||||
(_groupIndex < groups.length - 1)
|
||||
? _groupIndex++
|
||||
: _groupIndex = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 15.0),
|
||||
child: Text(
|
||||
groups[_groupIndex].name,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red[300]),
|
||||
)),
|
||||
Spacer(),
|
||||
Container(
|
||||
height: 30,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
alignment: Alignment.bottomRight,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(page: PodcastManage()),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 30,
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Text('See All',
|
||||
style: TextStyle(
|
||||
color: Colors.red[300],
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: (_width - 20) / 3 + 40,
|
||||
margin: EdgeInsets.only(left: 10, right: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
),
|
||||
child: TabBarView(
|
||||
children:
|
||||
podcastList.map<Widget>((PodcastLocal podcastLocal) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: Colors.grey[100]),
|
||||
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: PodcastPreview(
|
||||
podcastLocal: podcastLocal,
|
||||
Container(
|
||||
color: Colors.white10,
|
||||
height: 70,
|
||||
width: _width,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
labelPadding: EdgeInsets.only(
|
||||
top: 5.0, bottom: 10.0, left: 6.0, right: 6.0),
|
||||
indicator: CircleTabIndicator(
|
||||
color: Colors.blue, radius: 3),
|
||||
isScrollable: true,
|
||||
tabs: groups[_groupIndex].podcasts
|
||||
.map<Tab>((PodcastLocal podcastLocal) {
|
||||
return Tab(
|
||||
child: ClipRRect(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(25.0)),
|
||||
child: LimitedBox(
|
||||
maxHeight: 50,
|
||||
maxWidth: 50,
|
||||
child: !_loaded
|
||||
? CircularProgressIndicator()
|
||||
: Image.file(File(
|
||||
"$dir/${podcastLocal.title}.png")),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
Container(
|
||||
height: (_width - 20) / 3 + 40,
|
||||
margin: EdgeInsets.only(left: 10, right: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
),
|
||||
child: TabBarView(
|
||||
children: groups[_groupIndex].podcasts
|
||||
.map<Widget>((PodcastLocal podcastLocal) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: Colors.grey[100]),
|
||||
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: PodcastPreview(
|
||||
podcastLocal: podcastLocal,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,6 +335,7 @@ class ShowEpisode extends StatelessWidget {
|
||||
episodeItem: podcast[index],
|
||||
heroTag: 'scroll',
|
||||
//unique hero tag
|
||||
path: path,
|
||||
)),
|
||||
);
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/util/episodegrid.dart';
|
||||
|
||||
class MainTab extends StatefulWidget {
|
||||
|
34
lib/local_storage/key_value_storage.dart
Normal file
34
lib/local_storage/key_value_storage.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
|
||||
class KeyValueStorage {
|
||||
final String key;
|
||||
KeyValueStorage(this.key);
|
||||
|
||||
Future<List<GroupEntity>> getGroups() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
if (prefs.getString(key) == null) {
|
||||
PodcastGroup home = PodcastGroup('Home');
|
||||
await prefs.setString(
|
||||
key,
|
||||
json.encode({
|
||||
'groups': [home.toEntity().toJson()]
|
||||
}));}
|
||||
print(prefs.getString(key));
|
||||
return json
|
||||
.decode(prefs.getString(key))['groups']
|
||||
.cast<Map<String, Object>>()
|
||||
.map<GroupEntity>(GroupEntity.fromJson)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
Future<bool> saveGroup(List<GroupEntity> groupList) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
return prefs.setString(
|
||||
key,
|
||||
json.encode(
|
||||
{'groups': groupList.map((group) => group.toJson()).toList()}));
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@ import 'package:path/path.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'podcastlocal.dart';
|
||||
import 'episodebrief.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/webfeed/webfeed.dart';
|
||||
|
||||
class DBHelper {
|
||||
@ -25,72 +25,72 @@ class DBHelper {
|
||||
}
|
||||
|
||||
void _onCreate(Database db, int version) async {
|
||||
await db.execute(
|
||||
"""CREATE TABLE PodcastLocal(id INTEGER PRIMARY KEY,title TEXT,
|
||||
await db
|
||||
.execute("""CREATE TABLE PodcastLocal(id TEXT PRIMARY KEY,title TEXT,
|
||||
imageUrl TEXT,rssUrl TEXT UNIQUE,primaryColor TEXT,author TEXT,
|
||||
description TEXT, add_date INTEGER, order_id INTEGER default 0)""");
|
||||
description TEXT, add_date INTEGER, order_id INTEGER DEFAULT 0)""");
|
||||
await db
|
||||
.execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT,
|
||||
enclosure_url TEXT UNIQUE, enclosure_length INTEGER, pubDate TEXT,
|
||||
description TEXT, feed_title TEXT, feed_link TEXT, milliseconds INTEGER,
|
||||
duration INTEGER DEFAULT 0, explicit INTEGER DEFAULT 0, liked INTEGER DEFAULT 0,
|
||||
downloaded TEXT DEFAULT 'ND', download_date INTEGER DEFAULT 0)""");
|
||||
await db.execute(
|
||||
"""CREATE TABLE Setting(id INTEGER PRIMARY KEY, setting TEXT, setting_value INTEGER DEFAULT 0)""");
|
||||
await db
|
||||
.execute("""INSERT INTO Setting (setting) VALUES('podcasts_order') """);
|
||||
}
|
||||
|
||||
Future<List<PodcastLocal>> getPodcastLocal() async {
|
||||
Future<List<PodcastLocal>> getPodcastLocal(
|
||||
List<String> podcasts, int podcastsOrder) async {
|
||||
var dbClient = await database;
|
||||
//query podcasts order setting
|
||||
List<Map> setting = await dbClient.rawQuery("SELECT setting_value FROM Setting WHERE setting = 'podcasts_order'");
|
||||
int podcastsOrder = setting.first['setting_value'];
|
||||
List<Map> list;
|
||||
if (podcastsOrder == 0)
|
||||
{ list = await dbClient.rawQuery(
|
||||
List<PodcastLocal> podcastLocal = List();
|
||||
|
||||
await Future.forEach(podcasts, (s) async {
|
||||
List<Map> list;
|
||||
if (podcastsOrder == 0) {
|
||||
list = await dbClient.rawQuery(
|
||||
'SELECT id, title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal WHERE id = ? ORDER BY add_date DESC',
|
||||
[s]);
|
||||
} else if (podcastsOrder == 1) {
|
||||
list = await dbClient.rawQuery(
|
||||
'SELECT id, title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal WHERE id = ? ORDER BY add_date',
|
||||
[s]);
|
||||
} else if (podcastsOrder == 2) {
|
||||
list = await dbClient.rawQuery(
|
||||
'SELECT id, title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal WHERE id = ? ORDER BY order_id , add_date DESC',
|
||||
[s]);
|
||||
print('Get podcasts list Ordered by 2');
|
||||
}
|
||||
podcastLocal.add(PodcastLocal(
|
||||
list.first['title'],
|
||||
list.first['imageUrl'],
|
||||
list.first['rssUrl'],
|
||||
list.first['primaryColor'],
|
||||
list.first['author'],
|
||||
id: list.first['id']));
|
||||
});
|
||||
return podcastLocal;
|
||||
}
|
||||
|
||||
Future<List<PodcastLocal>> getPodcastLocalAll() async {
|
||||
var dbClient = await database;
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
'SELECT title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal ORDER BY add_date DESC');
|
||||
print('Get podcasts list Ordered by 0');}
|
||||
else if (podcastsOrder == 1)
|
||||
{ list = await dbClient.rawQuery(
|
||||
'SELECT title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal ORDER BY add_date');}
|
||||
else if (podcastsOrder ==2)
|
||||
{ list = await dbClient.rawQuery(
|
||||
'SELECT title, imageUrl, rssUrl, primaryColor, author FROM PodcastLocal ORDER BY order_id');
|
||||
print('Get podcasts list Ordered by 2');}
|
||||
|
||||
List<PodcastLocal> podcastLocal = List();
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
podcastLocal.add(PodcastLocal(
|
||||
list[i]['title'],
|
||||
list[i]['imageUrl'],
|
||||
list[i]['rssUrl'],
|
||||
list[i]['primaryColor'],
|
||||
list[i]['author'],
|
||||
));
|
||||
podcastLocal.add(PodcastLocal(list[i]['title'], list[i]['imageUrl'],
|
||||
list[i]['rssUrl'], list[i]['primaryColor'], list[i]['author'],
|
||||
id: list[i]['id']));
|
||||
}
|
||||
return podcastLocal;
|
||||
}
|
||||
|
||||
//save podcast order adter user save
|
||||
//save podcast order adter user save
|
||||
saveOrder(List<PodcastLocal> podcastList) async {
|
||||
var dbClient = await database;
|
||||
for (int i = 0; i < podcastList.length; i++){
|
||||
for (int i = 0; i < podcastList.length; i++) {
|
||||
await dbClient.rawUpdate(
|
||||
"UPDATE OR IGNORE PodcastLocal SET order_id = ? WHERE title = ?",
|
||||
[i, podcastList[i].title]);
|
||||
print(podcastList[i].title);
|
||||
}
|
||||
await dbClient.rawUpdate(
|
||||
"UPDATE OR IGNORE Setting SET setting_value = 2 WHERE setting = 'podcasts_order' ");
|
||||
print('Changed order');
|
||||
}
|
||||
|
||||
updateOrderSetting(int value) async{
|
||||
var dbClient = await database;
|
||||
await dbClient.rawUpdate(
|
||||
"UPDATE OR IGNORE Setting SET setting_value = ? WHERE setting = 'podcasts_order'",[value]);
|
||||
|
||||
}
|
||||
|
||||
Future savePodcastLocal(PodcastLocal podcastLocal) async {
|
||||
@ -99,9 +99,10 @@ class DBHelper {
|
||||
var dbClient = await database;
|
||||
await dbClient.transaction((txn) async {
|
||||
return await txn.rawInsert(
|
||||
"""INSERT OR IGNORE INTO PodcastLocal (title, imageUrl, rssUrl,
|
||||
primaryColor, author, description, add_date) VALUES(?, ?, ?, ?, ?, ?, ?)""",
|
||||
"""INSERT OR IGNORE INTO PodcastLocal (id, title, imageUrl, rssUrl,
|
||||
primaryColor, author, description, add_date) VALUES(?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
[
|
||||
podcastLocal.id,
|
||||
podcastLocal.title,
|
||||
podcastLocal.imageUrl,
|
||||
podcastLocal.rssUrl,
|
||||
@ -188,22 +189,22 @@ class DBHelper {
|
||||
} else {
|
||||
for (int i = 0; i < (_result - _count); i++) {
|
||||
print(_p.items[i].title);
|
||||
_p.items[i].itunes.title != null
|
||||
? _title = _p.items[i].itunes.title
|
||||
: _title = _p.items[i].title;
|
||||
_p.items[i].itunes.summary != null
|
||||
_title = _p.items[i].itunes.title ?? _p.items[i].title;
|
||||
_p.items[i].itunes.summary.contains('<')
|
||||
? _description = _p.items[i].itunes.summary
|
||||
: _description = _p.items[i].description;
|
||||
|
||||
isXimalaya(_p.items[i].enclosure.url)
|
||||
? _url = _p.items[i].enclosure.url.split('=').last
|
||||
: _url = _p.items[i].enclosure.url;
|
||||
|
||||
final _length = _p.items[i].enclosure.length;
|
||||
final _pubDate = _p.items[i].pubDate;
|
||||
final _date = _parsePubDate(_pubDate);
|
||||
final _milliseconds = _date.millisecondsSinceEpoch;
|
||||
(_p.items[i].itunes.duration != null)
|
||||
? _duration = _p.items[i].itunes.duration.inMinutes
|
||||
: _duration = 0;
|
||||
|
||||
_duration = _p.items[i].itunes.duration?.inMinutes ?? 0;
|
||||
|
||||
final _explicit = getExplicit(_p.items[i].itunes.explicit);
|
||||
if (_p.items[i].enclosure.url != null) {
|
||||
await dbClient.transaction((txn) {
|
||||
@ -384,7 +385,6 @@ class DBHelper {
|
||||
int count = await dbClient.rawUpdate(
|
||||
"UPDATE Episodes SET downloaded = ?, download_date = ? WHERE enclosure_url = ?",
|
||||
[id, _milliseconds, url]);
|
||||
print('Downloaded ' + url);
|
||||
return count;
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
import 'package:tsacdop/home/appbar/addpodcast.dart';
|
||||
import 'package:tsacdop/class/audiostate.dart';
|
||||
import 'package:tsacdop/class/importompl.dart';
|
||||
@ -15,6 +19,7 @@ void main() async {
|
||||
ChangeNotifierProvider(create: (context) => Urlchange()),
|
||||
ChangeNotifierProvider(create: (context) => ImportOmpl()),
|
||||
ChangeNotifierProvider(create: (context) => SettingState()),
|
||||
ChangeNotifierProvider(create: (context) => GroupList()),
|
||||
],
|
||||
child: MyApp(),
|
||||
),
|
||||
@ -23,22 +28,13 @@ void main() async {
|
||||
await FlutterDownloader.initialize();
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
await FlutterStatusbarcolor.setStatusBarColor(Colors.grey[100]);
|
||||
if (useWhiteForeground(Colors.grey[100])) {
|
||||
FlutterStatusbarcolor.setStatusBarWhiteForeground(true);
|
||||
} else {
|
||||
FlutterStatusbarcolor.setStatusBarWhiteForeground(false);
|
||||
}
|
||||
await FlutterStatusbarcolor.setNavigationBarColor(Colors.grey[100]);
|
||||
if (useWhiteForeground(Colors.grey[100])) {
|
||||
FlutterStatusbarcolor.setNavigationBarWhiteForeground(true);
|
||||
} else {
|
||||
FlutterStatusbarcolor.setNavigationBarWhiteForeground(false);
|
||||
}
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'TsacDop',
|
||||
|
@ -5,7 +5,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/episodebrief.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/util/episodegrid.dart';
|
||||
import 'package:tsacdop/webfeed/webfeed.dart';
|
||||
|
||||
|
398
lib/podcasts/podcastgroup.dart
Normal file
398
lib/podcasts/podcastgroup.dart
Normal file
@ -0,0 +1,398 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/class/settingstate.dart';
|
||||
|
||||
class PodcastGroupList extends StatefulWidget {
|
||||
final PodcastGroup group;
|
||||
PodcastGroupList({this.group, Key key}) : super(key: key);
|
||||
@override
|
||||
_PodcastGroupListState createState() => _PodcastGroupListState();
|
||||
}
|
||||
|
||||
class _PodcastGroupListState extends State<PodcastGroupList> {
|
||||
bool _loading;
|
||||
bool _loadSave;
|
||||
String dir;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loading = false;
|
||||
_loadSave = false;
|
||||
getApplicationDocumentsDirectory().then((value) {
|
||||
dir = value.path;
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Widget _saveButton(BuildContext context) {
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
_saveOrder(List<PodcastLocal> podcastList) async {
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.saveOrder(podcastList);
|
||||
}
|
||||
|
||||
var podcastList = widget.group.podcasts;
|
||||
return Container(
|
||||
child: InkWell(
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 800),
|
||||
width: _loadSave ? 70 : 0,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.all(Radius.circular(5.0))),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Save',
|
||||
style: TextStyle(color: Colors.white),
|
||||
maxLines: 1,
|
||||
)),
|
||||
onTap: () async {
|
||||
await _saveOrder(podcastList);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Setting Saved',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
_settingState.subscribeUpdate = Update.justupdate;
|
||||
setState(() {
|
||||
_loadSave = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.group.podcastList.length == 0
|
||||
? Center()
|
||||
: Container(
|
||||
color: Colors.grey[100],
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
ReorderableListView(
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (newIndex > oldIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final PodcastLocal podcast =
|
||||
widget.group.podcasts.removeAt(oldIndex);
|
||||
widget.group.podcasts.insert(newIndex, podcast);
|
||||
_loadSave = true;
|
||||
});
|
||||
},
|
||||
children:
|
||||
widget.group.podcasts.map<Widget>((PodcastLocal podcastLocal) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: Colors.grey[100]),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: !_loading
|
||||
? CircularProgressIndicator()
|
||||
: PodcastCard(
|
||||
path: dir,
|
||||
podcastLocal: podcastLocal,
|
||||
group: widget.group.name,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
AnimatedPositioned(
|
||||
duration: Duration(seconds: 1),
|
||||
bottom: 30,
|
||||
right: _loadSave ? 50 : 0,
|
||||
child: Center(),
|
||||
//_saveButton(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PodcastCard extends StatefulWidget {
|
||||
final PodcastLocal podcastLocal;
|
||||
final String path;
|
||||
final String group;
|
||||
PodcastCard({this.podcastLocal, this.path, this.group, Key key})
|
||||
: super(key: key);
|
||||
@override
|
||||
_PodcastCardState createState() => _PodcastCardState();
|
||||
}
|
||||
|
||||
class _PodcastCardState extends State<PodcastCard> {
|
||||
bool _loadMenu;
|
||||
bool _remove;
|
||||
bool _addGroup;
|
||||
bool _loadGroup;
|
||||
List<String> _selectedGroups;
|
||||
List<String> _belongGroups;
|
||||
Color _c;
|
||||
|
||||
_unSubscribe(String title) async {
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.delPodcastLocal(title);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadMenu = false;
|
||||
_remove = false;
|
||||
_addGroup = false;
|
||||
_loadGroup = false;
|
||||
}
|
||||
|
||||
Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0),
|
||||
child: widget),
|
||||
),
|
||||
);
|
||||
|
||||
@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);
|
||||
double _width = MediaQuery.of(context).size.width;
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
var _groupList = Provider.of<GroupList>(context);
|
||||
_selectedGroups = _groupList.groups.map((e) => e.name).toList();
|
||||
_belongGroups = _groupList
|
||||
.getPodcastGroup(widget.podcastLocal.id)
|
||||
.map((e) => e.name)
|
||||
.toList();
|
||||
return _remove
|
||||
? Center()
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () => setState(() => _loadMenu = !_loadMenu),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
height: 100,
|
||||
child: Row(children: <Widget>[
|
||||
Container(
|
||||
child: Icon(
|
||||
Icons.unfold_more,
|
||||
color: _c,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 60,
|
||||
child: Image.file(File(
|
||||
"${widget.path}/${widget.podcastLocal.title}.png")),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: _width / 2,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
widget.podcastLocal.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.fade,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 15),
|
||||
),
|
||||
),
|
||||
!_loadGroup
|
||||
? Center()
|
||||
: Row(
|
||||
children: _belongGroups.map((group) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
child: Text(group));
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Spacer(),
|
||||
Icon(_loadMenu
|
||||
? Icons.keyboard_arrow_up
|
||||
: Icons.keyboard_arrow_down),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
),
|
||||
OutlineButton(
|
||||
child: Text('Remove'),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
child: AlertDialog(
|
||||
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: () {
|
||||
_unSubscribe(widget.podcastLocal.title);
|
||||
_settingState.subscribeUpdate =
|
||||
Update.justupdate;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'CONFIRM',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
!_loadMenu
|
||||
? Center()
|
||||
: Container(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Colors.grey[300]),
|
||||
top: BorderSide(color: Colors.grey[300]))),
|
||||
height: 50,
|
||||
child: _addGroup
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Consumer<GroupList>(
|
||||
builder: (_, groupList, __) => Row(
|
||||
children: groupList.groups
|
||||
.map<Widget>(
|
||||
(PodcastGroup group) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 5.0),
|
||||
child: FilterChip(
|
||||
key: ValueKey<String>(group.name),
|
||||
label: Text(group.name),
|
||||
selected: _belongGroups
|
||||
.contains(group.name) &&
|
||||
_selectedGroups
|
||||
.contains(group.name),
|
||||
onSelected: (bool value) {
|
||||
setState(() {
|
||||
if (!value) {
|
||||
_selectedGroups
|
||||
.remove(group.name);
|
||||
} else {
|
||||
_selectedGroups
|
||||
.add(group.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () => setState(() {
|
||||
_addGroup = false;
|
||||
}),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
print(_selectedGroups);
|
||||
if (_selectedGroups.length > 0) {
|
||||
setState(() {
|
||||
_addGroup = false;
|
||||
});
|
||||
await _groupList.changeGroup(
|
||||
widget.podcastLocal.id,
|
||||
_selectedGroups,
|
||||
);
|
||||
_settingState.subscribeUpdate =
|
||||
Update.justupdate;
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Setting Saved',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
if (!_selectedGroups
|
||||
.contains(widget.group)) {
|
||||
print(widget.group);
|
||||
setState(() {
|
||||
_remove = true;
|
||||
});
|
||||
}
|
||||
} else
|
||||
Fluttertoast.showToast(
|
||||
msg:
|
||||
'At least select one group',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
},
|
||||
icon: Icon(Icons.done),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
_buttonOnMenu(Icon(Icons.fullscreen), () {}),
|
||||
_buttonOnMenu(Icon(Icons.add), () {
|
||||
setState(() {
|
||||
_addGroup = true;
|
||||
});
|
||||
}),
|
||||
_buttonOnMenu(
|
||||
Icon(Icons.notifications), () {})
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -6,9 +6,11 @@ import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/class/settingstate.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||
import 'package:tsacdop/util/pageroute.dart';
|
||||
|
||||
@ -48,12 +50,14 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
return AlertDialog(
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
onPressed: () {
|
||||
_unSubscribe(widget.podcastLocal.title);
|
||||
_settingState.subscribeUpdate = Update.justupdate;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
color: Colors.grey[200],
|
||||
@ -88,95 +92,104 @@ class PodcastList extends StatefulWidget {
|
||||
|
||||
class _PodcastListState extends State<PodcastList> {
|
||||
var dir;
|
||||
|
||||
Future<List<PodcastLocal>> getPodcastLocal() async {
|
||||
dir = await getApplicationDocumentsDirectory();
|
||||
var dbHelper = DBHelper();
|
||||
var podcastList = await dbHelper.getPodcastLocal();
|
||||
var podcastList = await dbHelper.getPodcastLocalAll();
|
||||
return podcastList;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double _width = MediaQuery.of(context).size.width;
|
||||
return 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],
|
||||
)),
|
||||
);
|
||||
},
|
||||
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(
|
||||
"${dir.path}/${snapshot.data[index].title}.png")),
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Podcasts'),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.grey[100],
|
||||
elevation: 0,
|
||||
),
|
||||
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],
|
||||
)),
|
||||
);
|
||||
},
|
||||
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,
|
||||
),
|
||||
),
|
||||
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),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(_width / 8)),
|
||||
child: Container(
|
||||
height: _width / 4,
|
||||
width: _width / 4,
|
||||
child: Image.file(File(
|
||||
"${dir.path}/${snapshot.data[index].title}.png")),
|
||||
),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
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');
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/sqflite_localpodcast.dart';
|
||||
import 'package:tsacdop/class/settingstate.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
import 'package:tsacdop/podcasts/podcastgroup.dart';
|
||||
import 'package:tsacdop/podcasts/podcastlist.dart';
|
||||
import 'package:tsacdop/util/pageroute.dart';
|
||||
|
||||
class PodcastManage extends StatefulWidget {
|
||||
@override
|
||||
@ -15,152 +11,208 @@ class PodcastManage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PodcastManageState extends State<PodcastManage> {
|
||||
var dir;
|
||||
bool _loading;
|
||||
bool _loadSave;
|
||||
Color _c;
|
||||
double _width;
|
||||
List<PodcastLocal> podcastList;
|
||||
getPodcastLocal() async {
|
||||
dir = await getApplicationDocumentsDirectory();
|
||||
var dbHelper = DBHelper();
|
||||
podcastList = await dbHelper.getPodcastLocal();
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
}
|
||||
|
||||
_unSubscribe(String title) async {
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.delPodcastLocal(title);
|
||||
print('Unsubscribe');
|
||||
Decoration getIndicator() {
|
||||
return const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: Colors.red, width: 2),
|
||||
insets: EdgeInsets.only(
|
||||
top: 10.0,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loading = false;
|
||||
_loadSave = false;
|
||||
getPodcastLocal();
|
||||
}
|
||||
|
||||
void _onReorder(int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (newIndex > oldIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final PodcastLocal podcast = podcastList.removeAt(oldIndex);
|
||||
podcastList.insert(newIndex, podcast);
|
||||
_loadSave = true;
|
||||
});
|
||||
}
|
||||
|
||||
_saveOrder(List<PodcastLocal> podcastList) async {
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.saveOrder(podcastList);
|
||||
}
|
||||
|
||||
Widget _podcastCard(BuildContext context, PodcastLocal podcastLocal) {
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
height: 100,
|
||||
child: Row(children: <Widget>[
|
||||
Container(
|
||||
child: Icon(
|
||||
Icons.unfold_more,
|
||||
color: _c,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 60,
|
||||
child: Image.file(File("${dir.path}/${podcastLocal.title}.png")),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: _width / 2,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Text(
|
||||
podcastLocal.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.fade,
|
||||
)),
|
||||
Spacer(),
|
||||
OutlineButton(
|
||||
child: Text('Unsubscribe'),
|
||||
onPressed: () {
|
||||
_unSubscribe(podcastLocal.title);
|
||||
_settingState.subscribeUpdate = Setting.start;
|
||||
setState(() {
|
||||
getPodcastLocal();
|
||||
});
|
||||
},
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_width = MediaQuery.of(context).size.width;
|
||||
var _settingState = Provider.of<SettingState>(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Podcasts'),
|
||||
backgroundColor: Colors.grey[100],
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.grey[100],
|
||||
title: Text('Groups'),
|
||||
actions: <Widget>[
|
||||
!_loadSave
|
||||
? Center()
|
||||
: InkWell(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
alignment: Alignment.center,
|
||||
child: Text('Save')),
|
||||
onTap: () async{
|
||||
await _saveOrder(podcastList);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Saved',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
_settingState.subscribeUpdate = Setting.start;
|
||||
setState(() {
|
||||
_loadSave = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () {},
|
||||
),
|
||||
OrderMenu(),
|
||||
],
|
||||
),
|
||||
body: Container(
|
||||
color: Colors.grey[100],
|
||||
child: !_loading
|
||||
? CircularProgressIndicator()
|
||||
: ReorderableListView(
|
||||
onReorder: _onReorder,
|
||||
children: podcastList.map<Widget>((PodcastLocal podcastLocal) {
|
||||
var color = json.decode(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);
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: Colors.grey[100]),
|
||||
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: _podcastCard(context, podcastLocal),
|
||||
);
|
||||
}).toList(),
|
||||
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(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 50,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
labelPadding: EdgeInsets.only(
|
||||
top: 5.0,
|
||||
bottom: 6.0,
|
||||
left: 10.0,
|
||||
right: 10.0),
|
||||
indicator: getIndicator(),
|
||||
isScrollable: true,
|
||||
tabs: _groups.map<Tab>((group) {
|
||||
return Tab(
|
||||
child: Text(group.name),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: FlatButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) =>
|
||||
AddGroup()),
|
||||
child: Icon(Icons.add)),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: TabBarView(
|
||||
children: _groups.map<Widget>((group) {
|
||||
return Container(
|
||||
key: ObjectKey(group),
|
||||
child: PodcastGroupList(group: group));
|
||||
}).toList(),),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OrderMenu extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton(
|
||||
elevation: 3,
|
||||
tooltip: 'Menu',
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Text('All Podcasts'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Text('New group'),
|
||||
)
|
||||
],
|
||||
onSelected: (value) {
|
||||
if (value == 1) {
|
||||
Navigator.push(context, ScaleRoute(page: PodcastList()));
|
||||
}
|
||||
if (value == 2) {
|
||||
showDialog(
|
||||
context: context, builder: (BuildContext context) => AddGroup());
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddGroup extends StatefulWidget {
|
||||
@override
|
||||
_AddGroupState createState() => _AddGroupState();
|
||||
}
|
||||
|
||||
class _AddGroupState extends State<AddGroup> {
|
||||
TextEditingController _controller;
|
||||
String _newGroup;
|
||||
int _error;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_error = 0;
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
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),
|
||||
),
|
||||
),
|
||||
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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ class EpisodeGrid extends StatelessWidget {
|
||||
this.showNumber,
|
||||
this.heroTag})
|
||||
: super(key: key);
|
||||
double _width;
|
||||
Future<String> _getPath() async {
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
return dir.path;
|
||||
@ -33,7 +32,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_width = MediaQuery.of(context).size.width;
|
||||
double _width = MediaQuery.of(context).size.width;
|
||||
return FutureBuilder(
|
||||
future: _getPath(),
|
||||
builder: (context, snapshot) {
|
||||
@ -67,7 +66,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||
ScaleRoute(
|
||||
page: EpisodeDetail(
|
||||
episodeItem: podcast[index],
|
||||
heroTag: heroTag)),
|
||||
heroTag: heroTag,
|
||||
path: snapshot.data,)),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
|
@ -46,14 +46,14 @@ class RssItem {
|
||||
factory RssItem.parse(XmlElement element) {
|
||||
return RssItem(
|
||||
title: findElementOrNull(element, "title")?.text,
|
||||
description: findElementOrNull(element, "description")?.text,
|
||||
link: findElementOrNull(element, "link")?.text,
|
||||
description: findElementOrNull(element, "description")?.text?.trim() ?? 'No shownote provided for this episode',
|
||||
link: findElementOrNull(element, "link")?.text?.trim(),
|
||||
categories: element.findElements("category").map((element) {
|
||||
return RssCategory.parse(element);
|
||||
}).toList(),
|
||||
guid: findElementOrNull(element, "guid")?.text,
|
||||
pubDate: findElementOrNull(element, "pubDate")?.text,
|
||||
author: findElementOrNull(element, "author")?.text,
|
||||
//guid: findElementOrNull(element, "guid")?.text,
|
||||
pubDate: findElementOrNull(element, "pubDate")?.text?.trim(),
|
||||
author: findElementOrNull(element, "author")?.text?.trim(),
|
||||
// comments: findElementOrNull(element, "comments")?.text,
|
||||
// source: RssSource.parse(findElementOrNull(element, "source")),
|
||||
// content: RssContent.parse(findElementOrNull(element, "content:encoded")),
|
||||
|
@ -45,7 +45,7 @@ class RssItunes {
|
||||
}
|
||||
return RssItunes(
|
||||
author: findElementOrNull(element, "itunes:author")?.text?.trim(),
|
||||
summary: findElementOrNull(element, "itunes:summary")?.text?.trim(),
|
||||
summary: findElementOrNull(element, "itunes:summary")?.text?.trim() ?? '',
|
||||
explicit: parseBoolLiteral(element, "itunes:explicit"),
|
||||
title: findElementOrNull(element, "itunes:title")?.text?.trim(),
|
||||
// subtitle: findElementOrNull(element, "itunes:subtitle")?.text?.trim(),
|
||||
|
53
pubspec.lock
53
pubspec.lock
@ -36,13 +36,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
cached_network_image:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: cached_network_image
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -111,13 +104,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
flutter_downloader:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@ -240,13 +226,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
network_image_to_byte:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: network_image_to_byte
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -317,6 +296,34 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
shared_preferences:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: shared_preferences
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.5.6+1"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_macos
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.1+5"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.2+3"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -414,7 +421,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.flutter-io.cn"
|
||||
@ -443,4 +450,4 @@ packages:
|
||||
version: "3.5.0"
|
||||
sdks:
|
||||
dart: ">=2.6.0 <3.0.0"
|
||||
flutter: ">=1.12.13 <2.0.0"
|
||||
flutter: ">=1.12.13+hotfix.4 <2.0.0"
|
||||
|
@ -29,16 +29,14 @@ dev_dependencies:
|
||||
sdk: flutter
|
||||
flutter_statusbarcolor: ^0.2.3
|
||||
json_annotation: any
|
||||
cached_network_image: any
|
||||
sqflite: any
|
||||
flutter_html: any
|
||||
flutter_html: ^0.11.1
|
||||
webfeed: any
|
||||
path_provider: any
|
||||
color_thief_flutter: ^1.0.1
|
||||
provider: ^4.0.1
|
||||
google_fonts: ^0.3.2
|
||||
dio: ^3.0.8
|
||||
network_image_to_byte: ^0.0.1
|
||||
file_picker: ^1.2.0
|
||||
xml: ^3.5.0
|
||||
marquee: ^1.3.1
|
||||
@ -49,6 +47,10 @@ dev_dependencies:
|
||||
intl: ^0.16.1
|
||||
url_launcher: ^5.4.1
|
||||
image: ^2.1.4
|
||||
shared_preferences: ^0.5.6+1
|
||||
uuid: ^2.0.4
|
||||
|
||||
|
||||
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
|
Loading…
x
Reference in New Issue
Block a user