feat: intgrate material you design

This commit is contained in:
xijieyin 2022-06-03 22:03:21 +08:00
parent 49a70fb833
commit bb6f57a6a3
43 changed files with 1736 additions and 1641 deletions

View File

@ -3,23 +3,20 @@ import 'dart:developer' as developer;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
import 'package:linkify/linkify.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/menu_bar.dart';
import 'package:tsacdop/episodes/shownote.dart';
import 'package:tsacdop/util/helpers.dart';
import 'package:tuple/tuple.dart';
import '../home/audioplayer.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../state/setting_state.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../util/extension_helper.dart';
import '../widgets/audiopanel.dart';
import '../widgets/custom_widget.dart';
import 'episode_download.dart';
class EpisodeDetail extends StatefulWidget {
final EpisodeBrief? episodeItem;
@ -48,23 +45,23 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
return await dbHelper.getPosition(episode);
}
ScrollController? _controller;
late ScrollController _controller;
_scrollListener() {
if (_controller!.position.userScrollDirection == ScrollDirection.reverse) {
if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
if (_showMenu && mounted) {
setState(() {
_showMenu = false;
});
}
}
if (_controller!.position.userScrollDirection == ScrollDirection.forward) {
if (_controller.position.userScrollDirection == ScrollDirection.forward) {
if (!_showMenu && mounted) {
setState(() {
_showMenu = true;
});
}
}
if (_controller!.offset > context.textTheme.headline5!.fontSize!) {
if (_controller.offset > context.textTheme.headline5!.fontSize!) {
if (!_showTitle) setState(() => _showTitle = true);
} else if (_showTitle) setState(() => _showTitle = false);
}
@ -75,623 +72,256 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
_showMenu = true;
_showTitle = false;
_controller = ScrollController();
_controller!.addListener(_scrollListener);
_controller.addListener(_scrollListener);
}
@override
void dispose() {
_controller!.dispose();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
final s = context.s;
final audio = context.watch<AudioPlayerNotifier>();
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: context.priamryContainer,
systemNavigationBarColor: context.priamryContainer,
systemNavigationBarContrastEnforced: false,
systemNavigationBarIconBrightness: context.iconBrightness,
statusBarBrightness: context.brightness,
statusBarIconBrightness: context.iconBrightness),
);
final s = context.s!;
final audio = context.watch<AudioPlayerNotifier>();
return WillPopScope(
onWillPop: () async {
if (_playerKey.currentState != null &&
_playerKey.currentState!.initSize! > 100) {
_playerKey.currentState!.backToMini();
return false;
} else {
return true;
}
},
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: SafeArea(
child: Stack(
children: <Widget>[
ScrollConfiguration(
behavior: NoGrowBehavior(),
child: NestedScrollView(
scrollDirection: Axis.vertical,
controller: _controller,
headerSliverBuilder: (context, innerBoxScrolled) {
return <Widget>[
SliverAppBar(
floating: true,
pinned: true,
title: _showTitle
? Text(
widget.episodeItem?.title ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
: Text(
widget.episodeItem!.feedTitle!,
maxLines: 1,
style: TextStyle(
fontSize: 15,
color: context.textColor!.withOpacity(0.7)),
child: WillPopScope(
onWillPop: () async {
if (_playerKey.currentState != null &&
_playerKey.currentState!.initSize! > 100) {
_playerKey.currentState!.backToMini();
return false;
} else {
return true;
}
},
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: SafeArea(
child: Stack(
children: <Widget>[
StretchingOverscrollIndicator(
axisDirection: AxisDirection.down,
child: NestedScrollView(
scrollDirection: Axis.vertical,
controller: _controller,
headerSliverBuilder: (context, innerBoxScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: context.priamryContainer,
floating: true,
pinned: true,
title: _showTitle
? Text(
widget.episodeItem?.title ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
: Text(
widget.episodeItem!.feedTitle!,
maxLines: 1,
style: TextStyle(
fontSize: 15,
color:
context.textColor.withOpacity(0.7)),
),
leading: CustomBackButton(),
elevation: 0,
),
];
},
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
widget.episodeItem!.title!,
textAlign: TextAlign.left,
style:
Theme.of(context).textTheme.headlineSmall,
),
leading: CustomBackButton(),
elevation: _showTitle ? 1 : 0,
),
];
},
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
widget.episodeItem!.title!,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.headline5,
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
child: Row(
children: [
Text(
s.published(DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(
widget.episodeItem!.pubDate!))),
style: TextStyle(color: context.accentColor)),
SizedBox(width: 10),
if (widget.episodeItem!.explicit == 1)
Text('E',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red)),
Spacer(),
],
Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
child: Row(
children: [
Text(
s.published(formateDate(
widget.episodeItem!.pubDate!)),
style:
TextStyle(color: context.accentColor)),
SizedBox(width: 10),
if (widget.episodeItem!.explicit == 1)
Text('E',
style: TextStyle(
fontWeight: FontWeight.bold,
color: context.error)),
Spacer(),
],
),
),
),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Row(
children: <Widget>[
if (widget.episodeItem!.duration != 0)
Container(
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 5),
child: Row(
children: <Widget>[
if (widget.episodeItem!.duration != 0)
Container(
decoration: BoxDecoration(
color: context.secondary,
borderRadius: BorderRadius.all(
Radius.circular(16.0))),
height: 30.0,
margin: EdgeInsets.only(right: 12.0),
padding: EdgeInsets.symmetric(
horizontal: 10.0),
alignment: Alignment.center,
child: Text(
s.minsCount(
widget.episodeItem!.duration! ~/ 60,
),
style:
TextStyle(color: context.onPrimary),
)),
if (widget.episodeItem!.enclosureLength !=
null &&
widget.episodeItem!.enclosureLength != 0)
Container(
decoration: BoxDecoration(
color: Colors.cyan[300],
color: context.tertiary,
borderRadius: BorderRadius.all(
Radius.circular(16.0))),
height: 28.0,
margin: EdgeInsets.only(right: 10.0),
height: 30.0,
margin: EdgeInsets.only(right: 12.0),
padding:
EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
s.minsCount(
widget.episodeItem!.duration! ~/ 60,
),
style: TextStyle(color: Colors.black),
)),
if (widget.episodeItem!.enclosureLength != null &&
widget.episodeItem!.enclosureLength != 0)
Container(
decoration: BoxDecoration(
color: Colors.lightBlue[300],
borderRadius: BorderRadius.all(
Radius.circular(16.0))),
height: 28.0,
margin: EdgeInsets.only(right: 10.0),
padding:
EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
'${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
style: TextStyle(color: Colors.black),
'${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
style:
TextStyle(color: context.onPrimary),
),
),
),
FutureBuilder<PlayHistory>(
future: _getPosition(widget.episodeItem!),
builder: (context, snapshot) {
if (snapshot.hasError) {
developer.log(snapshot.error as String);
}
if (snapshot.hasData &&
snapshot.data!.seekValue! < 0.9 &&
snapshot.data!.seconds! > 10) {
return ButtonTheme(
height: 28,
padding:
EdgeInsets.symmetric(horizontal: 0),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
100.0),
side: BorderSide(
color:
context.accentColor)),
),
onPressed: () => audio.episodeLoad(
widget.episodeItem,
startPosition:
(snapshot.data!.seconds! *
1000)
.toInt()),
child: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CustomPaint(
painter: ListenedPainter(
context.textColor,
stroke: 2.0),
FutureBuilder<PlayHistory>(
future: _getPosition(widget.episodeItem!),
builder: (context, snapshot) {
if (snapshot.hasError) {
developer.log(snapshot.error as String);
}
if (snapshot.hasData &&
snapshot.data!.seekValue! < 0.9 &&
snapshot.data!.seconds! > 10) {
return ButtonTheme(
height: 28,
padding: EdgeInsets.symmetric(
horizontal: 0),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
100.0),
side: BorderSide(
color:
context.accentColor)),
),
onPressed: () => audio.episodeLoad(
widget.episodeItem,
startPosition:
(snapshot.data!.seconds! *
1000)
.toInt()),
child: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CustomPaint(
painter: ListenedPainter(
context.textColor,
stroke: 2.0),
),
),
),
SizedBox(width: 5),
Text(
snapshot.data!.seconds!.toTime,
),
],
SizedBox(width: 5),
Text(
snapshot
.data!.seconds!.toTime,
),
],
),
),
),
);
} else {
return Center();
}
}),
],
);
} else {
return Center();
}
}),
],
),
),
),
ShowNote(episode: widget.episodeItem),
Selector<AudioPlayerNotifier,
Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) =>
Tuple2(audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
var height = kMinPlayerHeight[data.item2!.index];
return SizedBox(
height: data.item1 ? height : 0,
);
}),
],
ShowNote(episode: widget.episodeItem),
Selector<AudioPlayerNotifier,
Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) => Tuple2(
audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
final height =
kMinPlayerHeight[data.item2!.index];
return SizedBox(
height: data.item1 ? height : 0,
);
}),
],
),
),
),
),
),
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) =>
Tuple2(audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
var height = kMinPlayerHeight[data.item2!.index];
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(bottom: data.item1 ? height : 0),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: _showMenu ? 50 : 0,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: _MenuBar(
episodeItem: widget.episodeItem,
heroTag: widget.heroTag,
hide: widget.hide),
),
),
);
}),
Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, data, __) => Container(
child: PlayerWidget(
playerKey: _playerKey,
isPlayingPage: data == widget.episodeItem))),
],
),
),
),
);
}
}
class _MenuBar extends StatefulWidget {
final EpisodeBrief? episodeItem;
final String? heroTag;
final bool? hide;
_MenuBar({this.episodeItem, this.heroTag, this.hide, Key? key})
: super(key: key);
@override
__MenuBarState createState() => __MenuBarState();
}
class __MenuBarState extends State<_MenuBar> {
Future<int> _isListened(EpisodeBrief episode) async {
var dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
Future<void> _saveLiked(String url) async {
var dbHelper = DBHelper();
await dbHelper.setLiked(url);
if (mounted) setState(() {});
}
Future<void> _setUnliked(String url) async {
var dbHelper = DBHelper();
await dbHelper.setUniked(url);
if (mounted) setState(() {});
}
Future<void> _markListened(EpisodeBrief episode) async {
var dbHelper = DBHelper();
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
if (mounted) setState(() {});
}
Future<void> _markNotListened(String url) async {
var dbHelper = DBHelper();
await dbHelper.markNotListened(url);
if (mounted) setState(() {});
}
Future<bool> _isLiked(EpisodeBrief episode) async {
var dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
Widget _buttonOnMenu({Widget? child, VoidCallback? onTap}) => Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: SizedBox(
height: 50,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0), child: child),
),
),
);
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject() as RenderBox;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (constext) => Positioned(
left: offset.dx + 50,
top: offset.dy - 60,
child: Container(
width: 70,
height: 100,
//color: Colors.grey[200],
child: HeartOpen(width: 50, height: 80)),
),
);
}
@override
Widget build(BuildContext context) {
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
final s = context.s;
return Container(
height: 50.0,
decoration: BoxDecoration(
color: context.scaffoldBackgroundColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Hero(
tag: widget.episodeItem!.enclosureUrl + widget.heroTag!,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: 30.0,
width: 30.0,
color: context.scaffoldBackgroundColor,
child: widget.hide!
? Center()
: CircleAvatar(
radius: 15,
backgroundImage:
widget.episodeItem!.avatarImage),
),
),
),
FutureBuilder<bool>(
future: _isLiked(widget.episodeItem!),
initialData: false,
builder: (context, snapshot) {
return (!snapshot.data!)
? _buttonOnMenu(
child: Icon(
Icons.favorite_border,
color: Colors.grey[700],
),
onTap: () async {
await _saveLiked(
widget.episodeItem!.enclosureUrl);
OverlayEntry _overlayEntry;
_overlayEntry = _createOverlayEntry();
Overlay.of(context)!.insert(_overlayEntry);
await Future.delayed(Duration(seconds: 2));
_overlayEntry.remove();
})
: _buttonOnMenu(
child: Icon(
Icons.favorite,
color: Colors.red,
),
onTap: () => _setUnliked(
widget.episodeItem!.enclosureUrl));
},
),
DownloadButton(episode: widget.episodeItem),
Selector<AudioPlayerNotifier, List<EpisodeBrief?>>(
selector: (_, audio) => audio.queue.episodes,
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) =>
Tuple2(audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
final inPlaylist = data.contains(widget.episodeItem);
return inPlaylist
? _buttonOnMenu(
child: Icon(Icons.playlist_add_check,
color: context.accentColor),
onTap: () {
audio.delFromPlaylist(widget.episodeItem!);
Fluttertoast.showToast(
msg: s!.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Icon(Icons.playlist_add,
color: Colors.grey[700]),
onTap: () {
audio.addToPlaylist(widget.episodeItem!);
Fluttertoast.showToast(
msg: s!.toastAddPlaylist,
gravity: ToastGravity.BOTTOM,
);
});
},
),
FutureBuilder<int>(
future: _isListened(widget.episodeItem!),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data == 0
? _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(Colors.grey[700],
stroke: 2.0),
),
),
onTap: () {
_markListened(widget.episodeItem!);
Fluttertoast.showToast(
msg: s!.markListened,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(
context.accentColor,
stroke: 2.0),
),
),
onTap: () {
_markNotListened(
widget.episodeItem!.enclosureUrl);
Fluttertoast.showToast(
msg: s!.markNotListened,
gravity: ToastGravity.BOTTOM,
);
});
},
),
],
),
final height = kMinPlayerHeight[data.item2!.index];
return Container(
alignment: Alignment.bottomCenter,
padding:
EdgeInsets.only(bottom: data.item1 ? height : 0),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: _showMenu ? 50 : 0,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: MenuBar(
episodeItem: widget.episodeItem,
heroTag: widget.heroTag,
hide: widget.hide),
),
),
);
}),
Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, data, __) => Container(
child: PlayerWidget(
playerKey: _playerKey,
isPlayingPage: data == widget.episodeItem))),
],
),
),
Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief?, bool>>(
selector: (_, audio) => Tuple2(audio.episode, audio.playerRunning),
builder: (_, data, __) {
return (widget.episodeItem == data.item1 && data.item2)
? Padding(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20,
height: 15,
child: WaveLoader(color: context.accentColor)))
: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
audio.episodeLoad(widget.episodeItem);
},
child: Container(
alignment: Alignment.center,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: <Widget>[
Text(s!.play.toUpperCase(),
style: TextStyle(
color: context.accentColor,
fontSize: 15,
fontWeight: FontWeight.bold,
)),
Icon(
Icons.play_arrow,
color: context.accentColor,
),
],
),
),
),
);
},
),
],
),
),
);
}
}
class ShowNote extends StatelessWidget {
final EpisodeBrief? episode;
const ShowNote({this.episode, Key? key}) : super(key: key);
int? _getTimeStamp(String url) {
final time = url.substring(3).trim();
final data = time.split(':');
var seconds;
if (data.length == 3) {
seconds = int.tryParse(data[0])! * 3600 +
int.tryParse(data[1])! * 60 +
int.tryParse(data[2])!;
} else if (data.length == 2) {
seconds = int.tryParse(data[0])! * 60 + int.tryParse(data[1])!;
}
return seconds;
}
Future<String?> _getSDescription(String url) async {
var description;
var dbHelper = DBHelper();
description = (await dbHelper.getDescription(url))!
.replaceAll(RegExp(r'\s?<p>(<br>)?</p>\s?'), '')
.replaceAll('\r', '')
.trim();
if (!description.contains('<')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [UrlLinkifier(), EmailLinkifier()]);
for (var element in linkList) {
if (element is UrlElement) {
description = description.replaceAll(element.url,
'<a rel="nofollow" href = ${element.url}>${element.text}</a>');
}
if (element is EmailElement) {
final address = element.emailAddress;
description = description.replaceAll(address,
'<a rel="nofollow" href = "mailto:$address">$address</a>');
}
}
await dbHelper.saveEpisodeDes(url, description: description);
}
return description;
}
@override
Widget build(BuildContext context) {
var audio = context.watch<AudioPlayerNotifier>();
final s = context.s;
return FutureBuilder<String?>(
future: _getSDescription(episode!.enclosureUrl),
builder: (context, snapshot) {
if (snapshot.hasData) {
var description = snapshot.data;
if (description == null) return Center();
return description.length > 0
? Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, playEpisode, __) {
if (playEpisode == episode &&
!description!.contains('#t=')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [TimeStampLinkifier()]);
for (var element in linkList) {
if (element is TimeStampElement) {
final time = element.timeStamp;
description = description!.replaceFirst(time!,
'<a rel="nofollow" href = "#t=$time">$time</a>');
}
}
}
return Selector<SettingState, TextStyle>(
selector: (_, settings) => settings.showNoteFontStyle,
builder: (_, data, __) => Html(
style: {
'a': Style(
color: context.accentColor,
),
},
data: description,
onLinkTap: (url, _, __, ___) {
if (url!.substring(0, 3) == '#t=') {
final seconds = _getTimeStamp(url);
if (playEpisode == episode) {
audio.seekTo(seconds! * 1000);
}
} else {
url.launchUrl;
}
},
),
);
})
: Container(
height: context.width,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(
image: AssetImage('assets/shownote.png'),
height: 100.0,
),
Padding(padding: EdgeInsets.all(5.0)),
Text(s!.noShownote,
textAlign: TextAlign.center,
style: TextStyle(
color: context.textColor!.withOpacity(0.5))),
],
),
);
} else {
return Center();
}
},
);
}
}

View File

@ -45,7 +45,7 @@ class _DownloadButtonState extends State<DownloadButton> {
void _deleteDownload(EpisodeBrief episode) async {
Provider.of<DownloadState>(context, listen: false).delTask(episode);
Fluttertoast.showToast(
msg: context.s!.downloadRemovedToast,
msg: context.s.downloadRemovedToast,
gravity: ToastGravity.BOTTOM,
);
}
@ -78,22 +78,20 @@ class _DownloadButtonState extends State<DownloadButton> {
Future<bool> _useDataConfirm() async {
var ifUseData = false;
final s = context.s!;
final s = context.s;
await generalDialog(
context,
title: Text(s.cellularConfirm),
content: Text(s.cellularConfirmDes),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(
s.cancel,
style: TextStyle(color: Colors.grey[600]),
),
),
FlatButton(
TextButton(
onPressed: () {
ifUseData = true;
Navigator.of(context).pop();
@ -130,7 +128,7 @@ class _DownloadButtonState extends State<DownloadButton> {
AnimatedContainer(
duration: Duration(seconds: 1),
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
color: context.accentColor,
borderRadius: BorderRadius.all(Radius.circular(15.0))),
height: 20.0,
width: (_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
@ -222,7 +220,6 @@ class _DownloadButtonState extends State<DownloadButton> {
),
),
);
break;
case 3:
Provider.of<AudioPlayerNotifier>(context, listen: false)
.updateMediaItem(task.episode!);
@ -251,11 +248,9 @@ class _DownloadButtonState extends State<DownloadButton> {
),
),
);
break;
case 4:
return _buttonOnMenu(Icon(Icons.refresh, color: Colors.red),
() => _retryDownload(task.episode!));
break;
default:
return Center();
}

271
lib/episodes/menu_bar.dart Normal file
View File

@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
import 'package:tsacdop/type/play_histroy.dart';
import 'package:tuple/tuple.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/episode_download.dart';
import 'package:tsacdop/state/audio_state.dart';
import 'package:tsacdop/type/episodebrief.dart';
import 'package:tsacdop/util/extension_helper.dart';
import 'package:tsacdop/widgets/custom_widget.dart';
class MenuBar extends StatefulWidget {
final EpisodeBrief? episodeItem;
final String? heroTag;
final bool? hide;
MenuBar({this.episodeItem, this.heroTag, this.hide, Key? key})
: super(key: key);
@override
MenuBarState createState() => MenuBarState();
}
class MenuBarState extends State<MenuBar> {
@override
Widget build(BuildContext context) {
final audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
final s = context.s;
return Container(
height: 50.0,
decoration: BoxDecoration(
color: context.priamryContainer,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Hero(
tag: widget.episodeItem!.enclosureUrl + widget.heroTag!,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: 30.0,
width: 30.0,
color: context.priamryContainer,
child: widget.hide!
? Center()
: CircleAvatar(
radius: 15,
backgroundImage:
widget.episodeItem!.avatarImage),
),
),
),
FutureBuilder<bool>(
future: _isLiked(widget.episodeItem!),
initialData: false,
builder: (context, snapshot) {
return (!snapshot.data!)
? _buttonOnMenu(
child: Icon(
Icons.favorite_border,
color: Colors.grey[700],
),
onTap: () async {
await _saveLiked(
widget.episodeItem!.enclosureUrl);
OverlayEntry _overlayEntry;
_overlayEntry = _createOverlayEntry();
Overlay.of(context)!.insert(_overlayEntry);
await Future.delayed(Duration(seconds: 2));
_overlayEntry.remove();
})
: _buttonOnMenu(
child: Icon(
Icons.favorite,
color: Colors.red,
),
onTap: () => _setUnliked(
widget.episodeItem!.enclosureUrl));
},
),
DownloadButton(episode: widget.episodeItem),
Selector<AudioPlayerNotifier, List<EpisodeBrief?>>(
selector: (_, audio) => audio.queue.episodes,
builder: (_, data, __) {
final inPlaylist = data.contains(widget.episodeItem);
return inPlaylist
? _buttonOnMenu(
child: Icon(Icons.playlist_add_check,
color: context.accentColor),
onTap: () {
audio.delFromPlaylist(widget.episodeItem!);
Fluttertoast.showToast(
msg: s.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Icon(Icons.playlist_add,
color: Colors.grey[700]),
onTap: () {
audio.addToPlaylist(widget.episodeItem!);
Fluttertoast.showToast(
msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM,
);
});
},
),
FutureBuilder<int>(
future: _isListened(widget.episodeItem!),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data == 0
? _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(Colors.grey[700],
stroke: 2.0),
),
),
onTap: () {
_markListened(widget.episodeItem!);
Fluttertoast.showToast(
msg: s.markListened,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(
context.accentColor,
stroke: 2.0),
),
),
onTap: () {
_markNotListened(
widget.episodeItem!.enclosureUrl);
Fluttertoast.showToast(
msg: s.markNotListened,
gravity: ToastGravity.BOTTOM,
);
},
);
},
),
],
),
),
),
Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief?, bool>>(
selector: (_, audio) => Tuple2(audio.episode, audio.playerRunning),
builder: (_, data, __) {
return (widget.episodeItem == data.item1 && data.item2)
? Padding(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20,
height: 15,
child: WaveLoader(color: context.accentColor)))
: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
audio.episodeLoad(widget.episodeItem);
},
child: Container(
alignment: Alignment.center,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: <Widget>[
Text(
s.play.toUpperCase(),
style: TextStyle(
color: context.accentColor,
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
Icon(
Icons.play_arrow,
color: context.accentColor,
),
],
),
),
),
);
},
),
],
),
);
}
Future<int> _isListened(EpisodeBrief episode) async {
final dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
Future<void> _saveLiked(String url) async {
final dbHelper = DBHelper();
await dbHelper.setLiked(url);
if (mounted) setState(() {});
}
Future<void> _setUnliked(String url) async {
final dbHelper = DBHelper();
await dbHelper.setUniked(url);
if (mounted) setState(() {});
}
Future<void> _markListened(EpisodeBrief episode) async {
final dbHelper = DBHelper();
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
if (mounted) setState(() {});
}
Future<void> _markNotListened(String url) async {
final dbHelper = DBHelper();
await dbHelper.markNotListened(url);
if (mounted) setState(() {});
}
Future<bool> _isLiked(EpisodeBrief episode) async {
final dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
Widget _buttonOnMenu({Widget? child, VoidCallback? onTap}) => Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: SizedBox(
height: 50,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0), child: child),
),
),
);
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject() as RenderBox;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (constext) => Positioned(
left: offset.dx + 50,
top: offset.dy - 60,
child: Container(
width: 70,
height: 100,
//color: Colors.grey[200],
child: HeartOpen(width: 50, height: 80)),
),
);
}
}

136
lib/episodes/shownote.dart Normal file
View File

@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:linkify/linkify.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
import 'package:tsacdop/state/audio_state.dart';
import 'package:tsacdop/state/setting_state.dart';
import 'package:tsacdop/type/episodebrief.dart';
import 'package:tsacdop/util/extension_helper.dart';
class ShowNote extends StatelessWidget {
final EpisodeBrief? episode;
const ShowNote({this.episode, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final audio = context.watch<AudioPlayerNotifier>();
final s = context.s;
return FutureBuilder<String?>(
future: _getSDescription(episode!.enclosureUrl),
builder: (context, snapshot) {
if (snapshot.hasData) {
var description = snapshot.data;
if (description == null) return Center();
if (description.length > 0) {
return Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, playEpisode, __) {
if (playEpisode == episode && !description!.contains('#t=')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [TimeStampLinkifier()]);
for (final element in linkList) {
if (element is TimeStampElement) {
final time = element.timeStamp;
description = description!.replaceFirst(time!,
'<a rel="nofollow" href = "#t=$time">$time</a>');
}
}
}
return Selector<SettingState, TextStyle>(
selector: (_, settings) => settings.showNoteFontStyle,
builder: (_, data, __) => Html(
style: {
'html': Style.fromTextStyle(data.copyWith(fontSize: 14))
.copyWith(
padding: const EdgeInsets.symmetric(horizontal: 12),
),
'a': Style(
color: context.accentColor,
textDecoration: TextDecoration.none,
),
},
data: description,
onLinkTap: (url, _, __, ___) {
if (url!.substring(0, 3) == '#t=') {
final seconds = _getTimeStamp(url);
if (playEpisode == episode) {
audio.seekTo(seconds! * 1000);
}
} else {
url.launchUrl;
}
},
),
);
},
);
} else {
return Container(
height: context.width,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(
image: AssetImage('assets/shownote.png'),
height: 100.0,
),
Padding(padding: EdgeInsets.all(5.0)),
Text(s.noShownote,
textAlign: TextAlign.center,
style:
TextStyle(color: context.textColor.withOpacity(0.5))),
],
),
);
}
} else {
return Center();
}
},
);
}
int? _getTimeStamp(String url) {
final time = url.substring(3).trim();
final data = time.split(':');
int? seconds;
if (data.length == 3) {
seconds = int.tryParse(data[0])! * 3600 +
int.tryParse(data[1])! * 60 +
int.tryParse(data[2])!;
} else if (data.length == 2) {
seconds = int.tryParse(data[0])! * 60 + int.tryParse(data[1])!;
}
return seconds;
}
Future<String?> _getSDescription(String url) async {
final dbHelper = DBHelper();
String description;
description = (await dbHelper.getDescription(url))!
.replaceAll(RegExp(r'\s?<p>(<br>)?</p>\s?'), '')
.replaceAll('\r', '')
.trim();
if (!description.contains('<')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [UrlLinkifier(), EmailLinkifier()]);
for (var element in linkList) {
if (element is UrlElement) {
description = description.replaceAll(element.url!,
'<a rel="nofollow" href = ${element.url}>${element.text}</a>');
}
if (element is EmailElement) {
final address = element.emailAddress;
description = description.replaceAll(address,
'<a rel="nofollow" href = "mailto:$address">$address</a>');
}
}
await dbHelper.saveEpisodeDes(url, description: description);
}
return description;
}
}

View File

@ -57,7 +57,8 @@ class _AboutAppState extends State<AboutApp> {
),
);
Widget _translatorInfo(BuildContext context, {required String name, String? flag}) =>
Widget _translatorInfo(BuildContext context,
{required String name, String? flag}) =>
Container(
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
@ -114,7 +115,7 @@ class _AboutAppState extends State<AboutApp> {
);
}
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -208,7 +209,10 @@ class _AboutAppState extends State<AboutApp> {
children: <Widget>[
_listItem(context, 'Twitter @tsacdop',
LineIcons.twitter, 'https://twitter.com/tsacdop'),
_listItem(context, 'GitHub', LineIcons.alternateGithub,
_listItem(
context,
'GitHub',
LineIcons.alternateGithub,
'https://github.com/stonega/tsacdop'),
_listItem(context, 'Telegram', LineIcons.telegram,
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
@ -281,7 +285,7 @@ class _AboutAppState extends State<AboutApp> {
name: 'Bruno Pinheiro', flag: 'pt'),
_translatorInfo(context,
name: 'Edoardo Maria Elidoro', flag: 'it'),
_translatorInfo(context,
_translatorInfo(context,
name: 'Murat T. Akyuz', flag: 'tr'),
],
),

View File

@ -11,6 +11,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:line_icons/line_icons.dart';
import 'package:marquee/marquee.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/shownote.dart';
import 'package:tuple/tuple.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
@ -499,8 +500,8 @@ class _PlaylistWidgetState extends State<PlaylistWidget> {
children: <Widget>[
Text(
data.item1!.name == 'Queue'
? context.s!.queue
: '${context.s!.homeMenuPlaylist}${'-${data.item1!.name}'}',
? context.s.queue
: '${context.s.homeMenuPlaylist}${'-${data.item1!.name}'}',
overflow: TextOverflow.fade,
style: TextStyle(
color: context.accentColor,
@ -826,7 +827,7 @@ class SleepModeState extends State<SleepMode>
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: [
Text(context.s!.sleepTimer,
Text(context.s.sleepTimer,
style: TextStyle(
color: context.accentColor,
fontWeight: FontWeight.bold,
@ -935,10 +936,9 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
padding: EdgeInsets.symmetric(horizontal: 0),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
side: BorderSide(color: context.accentColor)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
side: BorderSide(color: context.accentColor)),
),
// highlightedBorderColor: Colors.green[700],
onPressed: () {
@ -1088,7 +1088,7 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
child: Row(
children: <Widget>[
Text(
context.s!.homeToprightMenuAbout,
context.s.homeToprightMenuAbout,
overflow: TextOverflow.fade,
style: TextStyle(
color: context.accentColor,
@ -1332,7 +1332,7 @@ class _ControlPanelState extends State<ControlPanel>
.buffering ||
data.audioState ==
AudioProcessingState.loading
? context.s!.buffering
? context.s.buffering
: '',
style: TextStyle(
color: context.accentColor),

View File

@ -64,7 +64,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
_controller = TabController(length: 3, vsync: this);
// FeatureDiscovery.hasPreviouslyCompleted(context, addFeature).then((value) {
// if (!value) {
SchedulerBinding.instance!.addPostFrameCallback((_) {
SchedulerBinding.instance.addPostFrameCallback((_) {
FeatureDiscovery.discoverFeatures(
context,
const <String>{
@ -90,15 +90,15 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
double top = 0;
@override
Widget build(BuildContext context) {
var height = (context.width - 20) / 3 + 140;
var settings = Provider.of<SettingState>(context, listen: false);
final height = (context.width - 20) / 3 + 140;
final settings = Provider.of<SettingState>(context, listen: false);
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarIconBrightness:
context.brightness,
systemNavigationBarIconBrightness: context.brightness,
statusBarIconBrightness: context.iconBrightness,
systemNavigationBarColor: context.primaryColor,
systemNavigationBarColor: context.onPrimary,
statusBarColor: context.onPrimary,
),
child: WillPopScope(
onWillPop: () async {
@ -115,12 +115,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
},
child: Scaffold(
key: _scaffoldKey,
backgroundColor: context.onPrimary,
body: Stack(
children: <Widget>[
SafeArea(
bottom: false,
child: ScrollConfiguration(
behavior: NoGrowBehavior(),
child: StretchingOverscrollIndicator(
axisDirection: AxisDirection.down,
child: NestedScrollView(
innerScrollPositionKeyBuilder: () {
return Key('tab${_controller!.index}');
@ -141,7 +142,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
context,
featureId: addFeature,
tapTarget: Icon(Icons.add_circle_outline),
title: s!.featureDiscoverySearch,
title: s.featureDiscoverySearch,
backgroundColor: Colors.cyan[600],
buttonColor: Colors.cyan[500],
description: s.featureDiscoverySearchDes,
@ -240,14 +241,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
),
),
Selector<AudioPlayerNotifier, bool>(
selector: (_, audio) =>
audio.playerRunning,
builder: (_, data, __) {
return Padding(
padding:
EdgeInsets.only(bottom: data ? 60.0 : 0),
);
}),
selector: (_, audio) => audio.playerRunning,
builder: (_, data, __) {
return Padding(
padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
);
},
),
],
),
),
@ -274,9 +274,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final s = context.s!;
final s = context.s;
return Container(
color: context.scaffoldBackgroundColor,
color: context.background,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
@ -335,7 +335,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(100),
@ -406,19 +406,23 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Padding(
padding: EdgeInsets.symmetric(vertical: 2),
),
Container(
SizedBox(
height: 70,
width: 140,
child: Column(
children: <Widget>[
Text(
(data.item3 ~/ 1000).toTime,
style:
TextStyle(color: context.textColor),
),
Text(
data.item2!.title!,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style:
TextStyle(color: context.textColor),
// style: TextStyle(color: Colors.white),
),
],
@ -443,7 +447,10 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.0),
),
Text(s.homeMenuPlaylist),
Text(
s.homeMenuPlaylist,
style: TextStyle(color: context.textColor),
),
],
),
),
@ -475,7 +482,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PlaylistHome(),
builder: (_) => PlaylistHome(),
),
);
}
@ -530,7 +537,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
refreshWorker.start(_group);
await Future.delayed(Duration(seconds: 1));
Fluttertoast.showToast(
msg: context.s!.refreshStarted,
msg: context.s.refreshStarted,
gravity: ToastGravity.BOTTOM,
);
}
@ -593,14 +600,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
builder: (context, groupList, child) => PopupMenuButton<String>(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1,
tooltip: context.s!.groupFilter,
tooltip: context.s.groupFilter,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20),
height: 50,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(_groupName == 'All' ? context.s!.all : _groupName!),
Text(_groupName == 'All' ? context.s.all : _groupName!),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
),
@ -613,7 +620,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
itemBuilder: (context) => [
PopupMenuItem(
child: Row(children: [
Text(context.s!.all),
Text(context.s.all),
Spacer(),
if (_groupName == 'All') DotIndicator()
]),
@ -806,7 +813,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
Material(
color: Colors.transparent,
child: IconButton(
tooltip: context.s!.refresh,
tooltip: context.s.refresh,
icon: Icon(
LineIcons.alternateRedo,
size: 16),

File diff suppressed because it is too large Load Diff

View File

@ -23,72 +23,10 @@ class PopupMenu extends StatefulWidget {
}
class _PopupMenuState extends State<PopupMenu> {
Future<String> _getRefreshDate(BuildContext context) async {
int? refreshDate;
var refreshstorage = KeyValueStorage('refreshdate');
var i = await refreshstorage.getInt();
if (i == 0) {
var refreshstorage = KeyValueStorage('refreshdate');
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
refreshDate = DateTime.now().millisecondsSinceEpoch;
} else {
refreshDate = i;
}
return refreshDate.toDate(context);
}
void _saveOmpl(String path) async {
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
var rssExp = RegExp(r'^(https?):\/\/(.*)');
final s = context.s;
var file = File(path);
try {
final opml = file.readAsStringSync();
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOPML(opml);
for (var entry in data.entries) {
var title = entry.key;
var list = entry.value.reversed;
for (var rss in list) {
var rssLink = rssExp.stringMatch(rss.xmlUrl!);
if (rssLink != null) {
var item = SubscribeItem(rssLink, rss.text, group: title);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 200));
}
}
}
} catch (e) {
developer.log(e.toString(), name: 'OMPL parse error');
Fluttertoast.showToast(
msg: s!.toastFileError,
gravity: ToastGravity.TOP,
);
}
}
void _getFilePath() async {
final s = context.s;
try {
var filePickResult =
await FilePicker.platform.pickFiles(type: FileType.any);
if (filePickResult == null) {
return;
}
Fluttertoast.showToast(
msg: s!.toastReadFile,
gravity: ToastGravity.TOP,
);
final filePath = filePickResult.files.first.path!;
_saveOmpl(filePath);
} on PlatformException catch (e) {
developer.log(e.toString(), name: 'Get OMPL file');
}
}
@override
Widget build(BuildContext context) {
var refreshWorker = Provider.of<RefreshWorker>(context, listen: false);
final s = context.s!;
final s = context.s;
return Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(100),
@ -98,10 +36,11 @@ class _PopupMenuState extends State<PopupMenu> {
width: 40,
child: PopupMenuButton<int>(
icon: Icon(Icons.more_vert),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1,
tooltip: s.menu,
color: context.priamryContainer,
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
@ -126,8 +65,8 @@ class _PopupMenuState extends State<PopupMenu> {
if (snapshot.hasData) {
return Text(
snapshot.data!,
style:
TextStyle(color: Colors.red, fontSize: 12),
style: TextStyle(
color: Colors.red, fontSize: 12),
);
} else {
return Center();
@ -141,7 +80,7 @@ class _PopupMenuState extends State<PopupMenu> {
),
PopupMenuItem(
value: 2,
child: Container(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Row(
children: <Widget>[
@ -204,4 +143,66 @@ class _PopupMenuState extends State<PopupMenu> {
),
);
}
Future<String> _getRefreshDate(BuildContext context) async {
int? refreshDate;
final refreshstorage = KeyValueStorage('refreshdate');
final i = await refreshstorage.getInt();
if (i == 0) {
final refreshstorage = KeyValueStorage('refreshdate');
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
refreshDate = DateTime.now().millisecondsSinceEpoch;
} else {
refreshDate = i;
}
return refreshDate.toDate(context);
}
void _saveOmpl(String path) async {
final subscribeWorker = Provider.of<GroupList>(context, listen: false);
final rssExp = RegExp(r'^(https?):\/\/(.*)');
final s = context.s;
final file = File(path);
try {
final opml = file.readAsStringSync();
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOPML(opml);
for (final entry in data.entries) {
var title = entry.key;
var list = entry.value.reversed;
for (var rss in list) {
var rssLink = rssExp.stringMatch(rss.xmlUrl!);
if (rssLink != null) {
var item = SubscribeItem(rssLink, rss.text, group: title);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 200));
}
}
}
} catch (e) {
developer.log(e.toString(), name: 'OMPL parse error');
Fluttertoast.showToast(
msg: s.toastFileError,
gravity: ToastGravity.TOP,
);
}
}
void _getFilePath() async {
final s = context.s;
try {
var filePickResult =
await FilePicker.platform.pickFiles(type: FileType.any);
if (filePickResult == null) {
return;
}
Fluttertoast.showToast(
msg: s.toastReadFile,
gravity: ToastGravity.TOP,
);
final filePath = filePickResult.files.first.path!;
_saveOmpl(filePath);
} on PlatformException catch (e) {
developer.log(e.toString(), name: 'Get OMPL file');
}
}
}

View File

@ -112,7 +112,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
// disabledTextColor: Colors.grey[500],
// disabledBorderColor: Colors.grey[500],
),
child: Text(context.s!.subscribe),
child: Text(context.s.subscribe),
onPressed: () {}),
),
),
@ -277,7 +277,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
padding: EdgeInsets.fromLTRB(50, 20, 50, 20),
child: Center(
child: Text(
context.s!.searchHelper,
context.s.searchHelper,
textAlign: TextAlign.center,
style: context.textTheme.headline6!
.copyWith(color: Colors.grey[400]),
@ -477,7 +477,7 @@ class __TopPodcastListState extends State<_TopPodcastList> {
child: CircularProgressIndicator(
strokeWidth: 2,
))
: Text(context.s!.loadMore),
: Text(context.s.loadMore),
onPressed: () => _loading
? null
: setState(

View File

@ -52,7 +52,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
Widget _invalidRss(BuildContext context) => Container(
padding: EdgeInsets.only(top: 200),
alignment: Alignment.topCenter,
child: Text(context.s!.searchInvalidRss,
child: Text(context.s.searchInvalidRss,
style: context.textTheme.headline6!.copyWith(color: Colors.red)),
);
@ -86,7 +86,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
return false;
},
child: IconButton(
tooltip: context.s!.back,
tooltip: context.s.back,
splashRadius: 20,
icon: Icon(_getIconData(Theme.of(context).platform)),
onPressed: () {
@ -111,7 +111,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
return <Widget>[
if (query.isNotEmpty)
IconButton(
tooltip: context.s!.clear,
tooltip: context.s.clear,
splashRadius: 20,
icon: const Icon(Icons.clear),
onPressed: () {
@ -216,7 +216,7 @@ class _RssResultState extends State<RssResult> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
var items = widget.rssFeed!.items!;
return DefaultTabController(
length: 2,
@ -339,7 +339,7 @@ class _RssResultState extends State<RssResult> {
borderRadius:
BorderRadius.all(Radius.circular(100))),
),
child: Text(context.s!.loadMore),
child: Text(context.s.loadMore),
onPressed: () => setState(
() => _loadItems += 10,
),
@ -560,7 +560,7 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
: CircularProgressIndicator(
strokeWidth: 2,
))
: Text(context.s!.loadMore),
: Text(context.s.loadMore),
onPressed: () => _loading
? null
: setState(
@ -722,7 +722,7 @@ class __PodcastIndexSearchState extends State<_PodcastIndexSearch> {
child: CircularProgressIndicator(
strokeWidth: 2,
))
: Text(context.s!.loadMore),
: Text(context.s.loadMore),
onPressed: () => _loading
? null
: setState(
@ -1011,7 +1011,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
child: CircularProgressIndicator(
strokeWidth: 2,
))
: Text(context.s!.loadMore),
: Text(context.s.loadMore),
onPressed: () {
if (widget.searchEngine ==
SearchEngine.listenNotes &&
@ -1057,12 +1057,11 @@ class _SearchResultDetailState extends State<SearchResultDetail>
foregroundColor: MaterialStateProperty.all<Color>(
context.accentColor),
overlayColor: MaterialStateProperty.all<Color>(
context.scaffoldBackgroundColor
.withOpacity(0.3)),
context.background.withOpacity(0.3)),
padding:
MaterialStateProperty.all<EdgeInsetsGeometry>(
EdgeInsets.symmetric(horizontal: 2))),
child: Text(context.s!.play.toUpperCase()),
child: Text(context.s.play.toUpperCase()),
onPressed: () {
context.read<AudioPlayerNotifier>().episodeLoad(
content[index].toEpisode,
@ -1215,7 +1214,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
),
Expanded(
child: Container(
color: context.scaffoldBackgroundColor,
color: context.background,
child: TabBarView(children: [
ListView(
physics: _animation.value != widget.maxHeight
@ -1327,7 +1326,7 @@ class PodcastSlideup extends StatelessWidget {
child: GestureDetector(
onTap: searchState.clearSelect,
child: Container(
color: context.scaffoldBackgroundColor.withOpacity(0.9),
color: context.background.withOpacity(0.9),
),
),
),

View File

@ -142,7 +142,7 @@ class _SlideIntroState extends State<SlideIntro> {
height: 40,
width: 80,
child: Center(
child: Text(context.s!.next,
child: Text(context.s.next,
style: TextStyle(
color: Colors.black,
fontWeight:
@ -164,7 +164,7 @@ class _SlideIntroState extends State<SlideIntro> {
height: 40,
width: 80,
child: Center(
child: Text(context.s!.done,
child: Text(context.s.done,
style: TextStyle(
color: Colors.black,
fontWeight:

View File

@ -24,7 +24,7 @@ class _FourthPageState extends State<FourthPage> {
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
child: Text(
context.s!.introFourthPage,
context.s.introFourthPage,
style: TextStyle(fontSize: 30, color: Colors.white),
),
),

View File

@ -24,7 +24,7 @@ class _SecondPageState extends State<SecondPage> {
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
child: Text(
context.s!.introSecondPage,
context.s.introSecondPage,
style: TextStyle(fontSize: 30, color: Colors.white),
),
),

View File

@ -24,7 +24,7 @@ class _ThirdPageState extends State<ThirdPage> {
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
child: Text(
context.s!.introThirdPage,
context.s.introThirdPage,
style: TextStyle(fontSize: 30, color: Colors.white),
),
),

View File

@ -60,7 +60,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
Color? color}) {
return OutlinedButton.icon(
style: OutlinedButton.styleFrom(
side: BorderSide(color: context.scaffoldBackgroundColor),
side: BorderSide(color: context.background),
primary: color,
backgroundColor:
isSelected ? context.primaryColorDark : Colors.transparent,
@ -73,7 +73,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarIconBrightness:
@ -100,7 +100,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
return Text(data?.title ?? '', maxLines: 1);
},
),
backgroundColor: context.scaffoldBackgroundColor,
backgroundColor: context.background,
),
body: Column(
children: [
@ -994,7 +994,7 @@ class __NewPlaylistState extends State<_NewPlaylist> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

@ -36,7 +36,7 @@ class _PlaylistDetailState extends State<PlaylistDetail> {
leading: IconButton(
splashRadius: 20,
icon: Icon(Icons.close),
tooltip: context.s!.back,
tooltip: context.s.back,
onPressed: () {
Navigator.maybePop(context);
},
@ -110,7 +110,8 @@ class _PlaylistDetailState extends State<PlaylistDetail> {
scrollDirection: Axis.vertical,
children: episodes.map<Widget>((episode) {
return _PlaylistItem(episode,
key: ValueKey(episode!.enclosureUrl), onSelect: (episode) {
key: ValueKey(episode!.enclosureUrl),
onSelect: (episode) {
_selectedEpisodes.add(episode);
setState(() {});
}, onRemove: (episode) {
@ -341,8 +342,8 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
onPressed: () => setState(() {
_clearConfirm = false;
}),
child:
Text(s!.cancel, style: TextStyle(color: Colors.grey[600])),
child: Text(s!.cancel,
style: TextStyle(color: Colors.grey[600])),
),
FlatButton(
splashColor: Colors.red.withAlpha(70),
@ -384,8 +385,8 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
onPressed: () => setState(() {
_removeConfirm = false;
}),
child:
Text(s!.cancel, style: TextStyle(color: Colors.grey[600])),
child: Text(s!.cancel,
style: TextStyle(color: Colors.grey[600])),
),
FlatButton(
splashColor: Colors.red.withAlpha(70),

View File

@ -117,7 +117,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
final result = await _dbHelper.updatePodcastRss(podcastLocal);
if (result >= 0) {
Fluttertoast.showToast(
msg: context.s!.updateEpisodesCount(result),
msg: context.s.updateEpisodesCount(result),
gravity: ToastGravity.TOP,
);
}
@ -148,7 +148,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
}
} else if (result != 0) {
Fluttertoast.showToast(
msg: context.s!.updateFailed,
msg: context.s.updateFailed,
gravity: ToastGravity.TOP,
);
}
@ -359,7 +359,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
Container(
padding: EdgeInsets.fromLTRB(15, 10, 15, 10),
alignment: Alignment.topLeft,
color: context.scaffoldBackgroundColor,
color: context.background,
child: AboutPodcast(podcastLocal: widget.podcastLocal),
),
],
@ -389,7 +389,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
);
Widget _actionBar(BuildContext context) {
final s = context.s!;
final s = context.s;
return SizedBox(
height: 30,
child: Row(
@ -668,7 +668,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
@override
Widget build(BuildContext context) {
final color = widget.podcastLocal!.primaryColor!.colorizedark();
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark,
@ -1138,7 +1138,7 @@ class _SearchEpisodeState extends State<SearchEpisode> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

@ -141,7 +141,10 @@ class __PodcastCardState extends State<_PodcastCard>
});
}
Widget _buttonOnMenu({required Widget icon, VoidCallback? onTap, required String tooltip}) =>
Widget _buttonOnMenu(
{required Widget icon,
VoidCallback? onTap,
required String tooltip}) =>
Material(
color: Colors.transparent,
child: InkWell(
@ -166,7 +169,7 @@ class __PodcastCardState extends State<_PodcastCard>
@override
Widget build(BuildContext context) {
final c = widget.podcastLocal!.backgroudColor(context);
final s = context.s!;
final s = context.s;
var groupList = context.watch<GroupList>();
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id);
return Column(
@ -243,7 +246,7 @@ class __PodcastCardState extends State<_PodcastCard>
: Container(
child: Container(
decoration: BoxDecoration(
color: context.scaffoldBackgroundColor,
color: context.background,
),
// border: Border(
// bottom: BorderSide(
@ -365,7 +368,8 @@ class __PodcastCardState extends State<_PodcastCard>
tooltip: s.autoDownload,
onTap: () async {
await _setAutoDownload(
widget.podcastLocal!.id, !snapshot.data!);
widget.podcastLocal!.id,
!snapshot.data!);
setState(() {});
},
);
@ -504,7 +508,7 @@ class _RenameGroupState extends State<RenameGroup> {
Widget build(BuildContext context) {
var groupList = Provider.of<GroupList>(context, listen: false);
List list = groupList.groups.map((e) => e!.name).toList();
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

@ -113,9 +113,10 @@ class _PodcastManageState extends State<PodcastManage>
}
} else {
groupList.saveOrder(groupList.groups[_index!]);
groupList.drlFromOrderChanged(groupList.groups[_index!]!.name);
groupList
.drlFromOrderChanged(groupList.groups[_index!]!.name);
Fluttertoast.showToast(
msg: context.s!.toastSettingSaved,
msg: context.s.toastSettingSaved,
gravity: ToastGravity.BOTTOM,
);
_controller.reverse();
@ -155,7 +156,7 @@ class _PodcastManageState extends State<PodcastManage>
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -166,7 +167,7 @@ class _PodcastManageState extends State<PodcastManage>
),
child: Scaffold(
appBar: AppBar(
title: Text(context.s!.groups(2)),
title: Text(context.s.groups(2)),
leading: CustomBackButton(),
actions: <Widget>[
featureDiscoveryOverlay(
@ -218,7 +219,7 @@ class _PodcastManageState extends State<PodcastManage>
: Stack(
children: <Widget>[
Container(
color: context.scaffoldBackgroundColor,
color: context.background,
child: CustomTabView(
itemCount: _groups.length,
tabBuilder: (context, index) => Tab(
@ -265,10 +266,8 @@ class _PodcastManageState extends State<PodcastManage>
}
},
child: Container(
color: context.scaffoldBackgroundColor
.withOpacity(0.8 *
math.min(
_menuController.value * 2, 1.0)),
color: context.background.withOpacity(0.8 *
math.min(_menuController.value * 2, 1.0)),
),
),
),
@ -332,7 +331,7 @@ class _PodcastManageState extends State<PodcastManage>
padding: EdgeInsets.symmetric(
horizontal: 5.0),
),
Text(context.s!.editGroupName,
Text(context.s.editGroupName,
style: TextStyle(
color: Colors.white)),
],
@ -366,7 +365,7 @@ class _PodcastManageState extends State<PodcastManage>
Navigator.of(context)
.pop(),
child: Text(
context.s!.cancel,
context.s.cancel,
style: TextStyle(
color:
Colors.grey[600]),
@ -385,7 +384,8 @@ class _PodcastManageState extends State<PodcastManage>
_index = _index! - 1;
});
groupList.delGroup(
_groups[_index! + 1]!);
_groups[
_index! + 1]!);
} else {
groupList.delGroup(
_groups[_index!]!);
@ -393,7 +393,7 @@ class _PodcastManageState extends State<PodcastManage>
Navigator.of(context).pop();
},
child: Text(
context.s!.confirm,
context.s.confirm,
style: TextStyle(
color: Colors.red),
),
@ -441,7 +441,7 @@ class _PodcastManageState extends State<PodcastManage>
class _OrderMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return PopupMenuButton(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1,
@ -494,7 +494,7 @@ class _AddGroupState extends State<AddGroup> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
var groupList = Provider.of<GroupList>(context, listen: false);
List list = groupList.groups.map((e) => e!.name).toList();
return AnnotatedRegion<SystemUiOverlayStyle>(

View File

@ -19,6 +19,7 @@ import '../widgets/custom_widget.dart';
import '../widgets/duraiton_picker.dart';
enum MarkStatus { start, complete, none }
enum RefreshCoverStatus { start, complete, error, none }
class PodcastSetting extends StatefulWidget {
@ -200,7 +201,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
final groupList = context.watch<GroupList>();
final textStyle = context.textTheme.bodyText2!;
return Column(
@ -233,8 +234,8 @@ class _PodcastSettingState extends State<PodcastSetting> {
),
trailing: Transform.scale(
scale: 0.8,
child:
Switch(value: snapshot.data!, onChanged: _setAutoDownload),
child: Switch(
value: snapshot.data!, onChanged: _setAutoDownload),
),
);
}),
@ -464,7 +465,7 @@ class _TimePicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return Container(
color: context.primaryColorDark,
child: Column(

View File

@ -51,7 +51,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
@override
Widget build(BuildContext context) {
var _groupList = Provider.of<GroupList>(context, listen: false);
final s = context.s!;
final s = context.s;
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
titlePadding: EdgeInsets.only(
@ -116,7 +116,7 @@ class _PodcastListState extends State<PodcastList> {
),
child: Scaffold(
appBar: AppBar(
title: Text(context.s!.podcast(2)),
title: Text(context.s.podcast(2)),
leading: CustomBackButton(),
actions: [
Selector<SettingState, bool?>(

View File

@ -79,7 +79,7 @@ class _DataBackupState extends State<DataBackup> {
}
Future<void> _importSetting(String path, BuildContext context) async {
final s = context.s!;
final s = context.s;
var settings = context.read<SettingState>();
var file = File(path);
try {
@ -178,7 +178,7 @@ class _DataBackupState extends State<DataBackup> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: context.iconBrightness,
@ -634,14 +634,14 @@ class __LoginGpodderState extends State<_LoginGpodder> {
} else {
if (mounted) setState(() => _loginStatus = LoginStatus.error);
Fluttertoast.showToast(
msg: context.s!.loginFailed,
msg: context.s.loginFailed,
gravity: ToastGravity.BOTTOM,
);
}
} else {
if (mounted) setState(() => _loginStatus = LoginStatus.error);
Fluttertoast.showToast(
msg: context.s!.loginFailed,
msg: context.s.loginFailed,
gravity: ToastGravity.BOTTOM,
);
}
@ -680,11 +680,11 @@ class __LoginGpodderState extends State<_LoginGpodder> {
String? _validateName(String? value) {
if (value!.isEmpty) {
return context.s!.usernameRequired;
return context.s.usernameRequired;
}
final nameExp = RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value)) {
return context.s!.invalidName;
return context.s.invalidName;
}
return null;
}
@ -692,7 +692,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
String? _validatePassword(String? value) {
final passwordField = _passwordFieldKey.currentState!;
if (passwordField.value == null || passwordField.value!.isEmpty) {
return context.s!.passwdRequired;
return context.s.passwdRequired;
}
return null;
}
@ -701,13 +701,13 @@ class __LoginGpodderState extends State<_LoginGpodder> {
switch (_loginStatus) {
case LoginStatus.none:
return Text(
context.s!.login,
context.s.login,
style: TextStyle(color: Colors.white),
);
break;
case LoginStatus.syncing:
return Text(
context.s!.settingsSyncing,
context.s.settingsSyncing,
style: TextStyle(color: Colors.white),
);
break;
@ -722,7 +722,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
);
default:
return Text(
context.s!.login,
context.s.login,
style: TextStyle(color: Colors.white),
);
break;
@ -731,7 +731,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark,

View File

@ -117,7 +117,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -219,11 +219,9 @@ class _DownloadsManageState extends State<DownloadsManage> {
),
Icon(
_mode == 0
? LineIcons
.hourglassStart
? LineIcons.hourglassStart
: _mode == 1
? LineIcons
.hourglassHalf
? LineIcons.hourglassHalf
: LineIcons.save,
size: 18,
)
@ -304,7 +302,8 @@ class _DownloadsManageState extends State<DownloadsManage> {
future: _isListened(_episodes[index]),
initialData: 0,
builder: (context, snapshot) {
return (_onlyListened! && snapshot.data == 0)
return (_onlyListened! &&
snapshot.data == 0)
? Center()
: Column(
children: <Widget>[

View File

@ -76,7 +76,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
Future recoverSub(BuildContext context, String url) async {
Fluttertoast.showToast(
msg: context.s!.toastPodcastRecovering,
msg: context.s.toastPodcastRecovering,
gravity: ToastGravity.BOTTOM,
);
var subscribeWorker = context.watch<GroupList>();
@ -99,7 +99,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
} catch (e) {
developer.log(e.toString(), name: 'Recover podcast error');
Fluttertoast.showToast(
msg: context.s!.toastRecoverFailed,
msg: context.s.toastRecoverFailed,
gravity: ToastGravity.BOTTOM,
);
}
@ -220,7 +220,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
return Container(
padding:
const EdgeInsets.symmetric(vertical: 5),
color: context.scaffoldBackgroundColor,
color: context.background,
child: Column(
children: <Widget>[
ListTile(
@ -234,7 +234,8 @@ class _PlayedHistoryState extends State<PlayedHistory>
DateFormat.yMd()
.add_jm()
.format(snapshot
.data![index].playdate!),
.data![index]
.playdate!),
style: TextStyle(
color: context.textColor!
.withOpacity(0.8),
@ -318,7 +319,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
itemBuilder: (context, index) {
var _status = snapshot.data![index].status;
return Container(
color: context.scaffoldBackgroundColor,
color: context.background,
child: Column(
children: <Widget>[
ListTile(
@ -362,7 +363,8 @@ class _PlayedHistoryState extends State<PlayedHistory>
.alternativeTrashRestore),
onPressed: () => recoverSub(
context,
snapshot.data![index].rssUrl!),
snapshot
.data![index].rssUrl!),
),
)
: null,
@ -476,11 +478,11 @@ class HistoryChart extends StatelessWidget {
lineTouchData: LineTouchData(
enabled: true,
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: context.scaffoldBackgroundColor,
tooltipBgColor: context.background,
fitInsideHorizontally: true,
getTooltipItems: (touchedBarSpots) {
return touchedBarSpots.map((barSpot) {
return LineTooltipItem(context.s!.minsCount(barSpot.y.toInt()),
return LineTooltipItem(context.s.minsCount(barSpot.y.toInt()),
context.textTheme.subtitle1!);
}).toList();
},

View File

@ -41,7 +41,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
}
} else {
await localeStorage
.saveStringList([locale!.languageCode, locale.countryCode??'']);
.saveStringList([locale!.languageCode, locale.countryCode ?? '']);
await S.load(locale);
if (mounted) {
setState(() {});
@ -66,7 +66,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
@override
Widget build(BuildContext context) {
final textStyle = context.textTheme.bodyText2!;
final s = context.s!;
final s = context.s;
return Column(
children: [
ListTile(

View File

@ -184,7 +184,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(

View File

@ -26,7 +26,7 @@ class Libries extends StatelessWidget {
),
child: Scaffold(
appBar: AppBar(
title: Text(context.s!.settingsLibraries),
title: Text(context.s.settingsLibraries),
leading: CustomBackButton(),
elevation: 0,
backgroundColor: Theme.of(context).primaryColor,
@ -68,7 +68,7 @@ class Libries extends StatelessWidget {
height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft,
child: Text(context.s!.fonts,
child: Text(context.s.fonts,
style: Theme.of(context)
.textTheme
.bodyText1!
@ -90,7 +90,7 @@ class Libries extends StatelessWidget {
height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft,
child: Text(context.s!.plugins,
child: Text(context.s.plugins,
style: Theme.of(context)
.textTheme
.bodyText1!

View File

@ -89,7 +89,7 @@ class _PlaySettingState extends State<PlaySetting> {
topLeft: Radius.circular(5)),
),
padding: const EdgeInsets.all(8.0),
child: Text(context.s!.endOfEpisode,
child: Text(context.s.endOfEpisode,
style: TextStyle(
color: data.item1 == 0 ? Colors.white : null)),
),
@ -113,7 +113,7 @@ class _PlaySettingState extends State<PlaySetting> {
topRight: Radius.circular(5)),
),
padding: const EdgeInsets.all(8.0),
child: Text(context.s!.minsCount(data.item2!),
child: Text(context.s.minsCount(data.item2!),
style: TextStyle(
color: data.item1 == 1 ? Colors.white : null)),
),
@ -224,7 +224,7 @@ class _PlaySettingState extends State<PlaySetting> {
Widget build(BuildContext context) {
var settings = context.watch<SettingState>();
var audio = context.watch<AudioPlayerNotifier>();
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -298,7 +298,8 @@ class _PlaySettingState extends State<PlaySetting> {
trailing: Transform.scale(
scale: 0.9,
child: Switch(
value: snapshot.data!, onChanged: _saveMarkListenedSkip),
value: snapshot.data!,
onChanged: _saveMarkListenedSkip),
),
),
),
@ -350,7 +351,8 @@ class _PlaySettingState extends State<PlaySetting> {
displayItemCount: 5,
isDense: true,
value: data,
onChanged: (dynamic value) => settings.setRewindSeconds = value,
onChanged: (dynamic value) =>
settings.setRewindSeconds = value,
items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) {
return DropdownMenuItem<int>(
value: e, child: Text(s.secCount(e)));
@ -524,8 +526,8 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_notificationIcon(
Icon(Icons.pause_circle_filled), '${s!.play}| ${s.pause}'),
_notificationIcon(Icon(Icons.pause_circle_filled),
'${s!.play}| ${s.pause}'),
_notificationIcon(Icon(Icons.fast_forward), s.fastForward),
_notificationIcon(Icon(Icons.skip_next), s.skipToNext),
_notificationIcon(Icon(Icons.close), s.stop),
@ -600,7 +602,7 @@ class __SpeedListState extends State<_SpeedList> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return ListTile(
contentPadding:
EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10),

View File

@ -68,7 +68,7 @@ class _SettingsState extends State<Settings> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -107,8 +107,7 @@ class _SettingsState extends State<Settings> {
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => ThemeSetting())),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading:
Icon(LineIcons.adjust, color: context.accentColor),
leading: Icon(LineIcons.adjust, color: context.accentColor),
title: Text(s.settingsAppearance),
subtitle: Text(s.settingsAppearanceDes),
),
@ -117,8 +116,7 @@ class _SettingsState extends State<Settings> {
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => LayoutSetting())),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.stopCircle,
color: Colors.blueAccent),
leading: Icon(LineIcons.stopCircle, color: Colors.blueAccent),
title: Text(s.settingsLayout),
subtitle: Text(s.settingsLayoutDes),
),
@ -168,8 +166,7 @@ class _SettingsState extends State<Settings> {
title: s.settingsLanguages, child: LanguagesSetting())
.then((value) => setState(() {})),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.language,
color: Colors.purpleAccent),
leading: Icon(LineIcons.language, color: Colors.purpleAccent),
title: Text(s.settingsLanguages),
subtitle: Text(s.settingsLanguagesDes),
),
@ -181,8 +178,8 @@ class _SettingsState extends State<Settings> {
MaterialPageRoute(builder: (context) => DataBackup()));
},
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.codeFile,
color: Colors.lightGreen[700]),
leading:
Icon(LineIcons.codeFile, color: Colors.lightGreen[700]),
title: Text(s.settingsBackup),
subtitle: Text(s.settingsBackupDes),
),
@ -204,8 +201,7 @@ class _SettingsState extends State<Settings> {
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => Libries())),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.bookOpen,
color: Colors.purple[700]),
leading: Icon(LineIcons.bookOpen, color: Colors.purple[700]),
title: Text(s.settingsLibraries),
subtitle: Text(s.settingsLibrariesDes),
),
@ -257,8 +253,7 @@ class _SettingsState extends State<Settings> {
);
},
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading:
Icon(LineIcons.capsules, color: Colors.pinkAccent),
leading: Icon(LineIcons.capsules, color: Colors.pinkAccent),
title: Text(s.settingsDiscovery),
),
Divider(height: 1),
@ -269,8 +264,7 @@ class _SettingsState extends State<Settings> {
builder: (context) =>
SlideIntro(goto: Goto.settings))),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading:
Icon(LineIcons.columns, color: Colors.blueGrey),
leading: Icon(LineIcons.columns, color: Colors.blueGrey),
title: Text(s.settingsAppIntro),
),
Divider(height: 1),

View File

@ -110,7 +110,7 @@ class _StorageSettingState extends State<StorageSetting>
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
var settings = Provider.of<SettingState>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(

View File

@ -11,7 +11,7 @@ import '../widgets/custom_widget.dart';
class SyncingSetting extends StatelessWidget {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
var settings = Provider.of<SettingState>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(

View File

@ -10,7 +10,7 @@ import '../widgets/general_dialog.dart';
class ThemeSetting extends StatelessWidget {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
var settings = Provider.of<SettingState>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(

View File

@ -65,9 +65,10 @@ final showNotesFontStyles = <TextStyle>[
height: 1.8,
)),
GoogleFonts.bitter(
textStyle: TextStyle(
height: 1.8,
)),
textStyle: TextStyle(
height: 1.8,
),
),
];
class SettingState extends ChangeNotifier {
@ -133,15 +134,13 @@ class SettingState extends ChangeNotifier {
/// Load locale.
Locale? get locale => _locale;
/// Spp thememode. default auto.
/// Set thememode. default auto.
ThemeMode? _theme;
ThemeMode? get theme => _theme;
ThemeData get lightTheme => ThemeData().copyWith(
colorScheme: ThemeData()
.colorScheme
.copyWith(brightness: Brightness.light, secondary: _accentSetColor),
brightness: Brightness.dark,
ThemeData get lightTheme => ThemeData.light().copyWith(
colorScheme: _colors(Brightness.light, _accentSetColor!),
brightness: Brightness.light,
primaryColor: Colors.grey[100],
primaryColorLight: Colors.white,
primaryColorDark: Colors.grey[300],
@ -154,6 +153,10 @@ class SettingState extends ChangeNotifier {
iconTheme: IconThemeData(color: Colors.black),
systemOverlayStyle: SystemUiOverlayStyle.dark),
textTheme: TextTheme(
headlineSmall: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.normal),
bodyLarge: TextStyle(
fontSize: 17.0,
color: Colors.black,
@ -162,6 +165,34 @@ class SettingState extends ChangeNotifier {
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.normal),
bodySmall: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.normal),
labelLarge: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.normal),
labelMedium: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.normal),
labelSmall: TextStyle(
fontSize: 12.0,
color: Colors.black,
fontWeight: FontWeight.normal),
titleLarge: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.normal),
titleMedium: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.normal),
titleSmall: TextStyle(
fontSize: 12.0,
color: Colors.black,
fontWeight: FontWeight.normal),
),
tabBarTheme: TabBarTheme(
labelColor: Colors.black,
@ -177,15 +208,18 @@ class SettingState extends ChangeNotifier {
hoverColor: _accentSetColor!.withAlpha(70),
splashColor: _accentSetColor!.withAlpha(70),
),
useMaterial3: true,
);
ThemeData get darkTheme => ThemeData.dark().copyWith(
colorScheme: ThemeData.dark()
.colorScheme
.copyWith(brightness: Brightness.dark, secondary: _accentSetColor),
brightness: Brightness.light,
colorScheme: _colors(Brightness.dark, _accentSetColor!),
brightness: Brightness.dark,
primaryColorDark: Colors.grey[800],
textTheme: TextTheme(
headlineSmall: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.normal),
bodyLarge: TextStyle(
fontSize: 17.0,
color: Colors.white,
@ -194,9 +228,31 @@ class SettingState extends ChangeNotifier {
fontSize: 15.0,
color: Colors.white,
fontWeight: FontWeight.normal),
labelLarge: TextStyle(
fontSize: 16.0,
color: Colors.white,
fontWeight: FontWeight.normal),
labelMedium: TextStyle(
fontSize: 14.0,
color: Colors.white,
fontWeight: FontWeight.normal),
labelSmall: TextStyle(
fontSize: 12.0,
color: Colors.white,
fontWeight: FontWeight.normal),
titleLarge: TextStyle(
fontSize: 16.0,
color: Colors.white,
fontWeight: FontWeight.normal),
titleMedium: TextStyle(
fontSize: 14.0,
color: Colors.white,
fontWeight: FontWeight.normal),
titleSmall: TextStyle(
fontSize: 12.0,
color: Colors.white,
fontWeight: FontWeight.normal),
),
scaffoldBackgroundColor:
_realDark! ? Colors.black87 : Color(0XFF212121),
primaryColor: _realDark! ? Colors.black : Color(0XFF1B1B1B),
popupMenuTheme: PopupMenuThemeData()
.copyWith(color: _realDark! ? Colors.grey[900] : null),
@ -206,6 +262,7 @@ class SettingState extends ChangeNotifier {
systemOverlayStyle: SystemUiOverlayStyle.light),
buttonTheme: ButtonThemeData(height: 32),
dialogBackgroundColor: _realDark! ? Colors.grey[900] : null,
useMaterial3: true,
);
set setTheme(ThemeMode? mode) {
@ -214,6 +271,13 @@ class SettingState extends ChangeNotifier {
notifyListeners();
}
ColorScheme _colors(Brightness brightness, Color targetColor) {
return ColorScheme.fromSeed(
seedColor: targetColor,
brightness: brightness,
);
}
void setWorkManager(int? hour) {
_updateInterval = hour;
notifyListeners();
@ -366,9 +430,9 @@ class SettingState extends ChangeNotifier {
_saveRewindSeconds();
}
int? _showNotesFontIndex;
int? get showNotesFontIndex => _showNotesFontIndex;
TextStyle get showNoteFontStyle => showNotesFontStyles[_showNotesFontIndex!];
late int _showNotesFontIndex;
int get showNotesFontIndex => _showNotesFontIndex;
TextStyle get showNoteFontStyle => showNotesFontStyles[_showNotesFontIndex];
set setShowNoteFontStyle(int index) {
_showNotesFontIndex = index;
notifyListeners();

View File

@ -3,39 +3,49 @@ import 'dart:developer' as developer;
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import '../generated/l10n.dart';
extension ContextExtension on BuildContext {
Color get primaryColor => Theme.of(this).primaryColor;
Color get accentColor => Theme.of(this).colorScheme.secondary;
Color get scaffoldBackgroundColor => Theme.of(this).scaffoldBackgroundColor;
ColorScheme get colorScheme => Theme.of(this).colorScheme;
Color get accentColor => Theme.of(this).colorScheme.primary;
Color get primaryColor => Theme.of(this).colorScheme.onPrimary;
Color get priamryContainer => Theme.of(this).colorScheme.primaryContainer;
Color get onPrimary => Theme.of(this).colorScheme.onPrimary;
Color get background => Theme.of(this).colorScheme.background;
Color get tertiary => colorScheme.tertiary;
Color get onTertiary => colorScheme.onTertiary;
Color get secondary => colorScheme.secondary;
Color get onsecondary => colorScheme.onSecondary;
Color get error => colorScheme.error;
Color get primaryColorDark => Theme.of(this).primaryColorDark;
Color get textColor => textTheme.bodyLarge!.color!;
Color get dialogBackgroundColor => Theme.of(this).dialogBackgroundColor;
Brightness get brightness => Theme.of(this).brightness;
Brightness get iconBrightness => Theme.of(this).colorScheme.brightness;
Brightness get iconBrightness =>
brightness == Brightness.dark ? Brightness.light : Brightness.dark;
double get width => MediaQuery.of(this).size.width;
double get height => MediaQuery.of(this).size.height;
double get paddingTop => MediaQuery.of(this).padding.top;
TextTheme get textTheme => Theme.of(this).textTheme;
S? get s => S.of(this);
S get s => S.of(this)!;
}
extension IntExtension on int {
String toDate(BuildContext context) {
final s = context.s;
var date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
var difference = DateTime.now().toUtc().difference(date);
final date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
final difference = DateTime.now().toUtc().difference(date);
if (difference.inMinutes < 30) {
return s!.minsAgo(difference.inMinutes);
return s.minsAgo(difference.inMinutes);
} else if (difference.inMinutes < 60) {
return s!.hoursAgo(0);
return s.hoursAgo(0);
} else if (difference.inHours < 24) {
return s!.hoursAgo(difference.inHours);
return s.hoursAgo(difference.inHours);
} else if (difference.inDays < 7) {
return s!.daysAgo(difference.inDays);
return s.daysAgo(difference.inDays);
} else {
return DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(this, isUtc: true).toLocal());
@ -50,21 +60,21 @@ extension IntExtension on int {
final s = context.s;
var interval = Duration(milliseconds: this);
if (interval.inHours <= 48) {
return s!.publishedDaily;
return s.publishedDaily;
} else if (interval.inDays > 2 && interval.inDays <= 14) {
return s!.publishedWeekly;
return s.publishedWeekly;
} else if (interval.inDays > 14 && interval.inDays < 60) {
return s!.publishedMonthly;
return s.publishedMonthly;
} else {
return s!.publishedYearly;
return s.publishedYearly;
}
}
}
extension StringExtension on String {
Future get launchUrl async {
if (await canLaunch(this)) {
await launch(this);
if (await canLaunchUrlString(this)) {
await launchUrlString(this);
} else {
developer.log('Could not launch $this');
Fluttertoast.showToast(

View File

@ -2,6 +2,7 @@ import 'dart:ui' as ui;
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
final ImageStream stream = imageProvider.resolve(
@ -17,3 +18,9 @@ Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
final image = await imageCompleter.future;
return image;
}
String formateDate(int timeStamp) {
return DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(timeStamp),
);
}

View File

@ -94,8 +94,8 @@ class AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
child: GestureDetector(
onTap: backToMini,
child: Container(
color: context.scaffoldBackgroundColor.withOpacity(0.9 *
math.min(_animation.value / widget.maxHeight, 1)),
color: context.background.withOpacity(
0.9 * math.min(_animation.value / widget.maxHeight, 1)),
),
),
)
@ -314,7 +314,7 @@ class __AudioPanelRouteState extends State<_AudioPanelRoute> {
// child:
// Container(
// color: Theme.of(context)
// .scaffoldBackgroundColor
// .background
// .withOpacity(0.8),
//
//),

View File

@ -651,8 +651,7 @@ class _LineLoaderState extends State<LineLoader>
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: LinePainter(_fraction, context.accentColor));
return CustomPaint(painter: LinePainter(_fraction, context.accentColor));
}
}
@ -1115,8 +1114,7 @@ class LayoutButton extends StatelessWidget {
height: 10,
width: 30,
child: CustomPaint(
painter:
LayoutPainter(4, context.textColor),
painter: LayoutPainter(4, context.textColor),
),
),
);
@ -1265,7 +1263,7 @@ class CustomBackButton extends StatelessWidget {
return IconButton(
splashRadius: 20,
icon: const BackButtonIcon(),
tooltip: context.s!.back,
tooltip: context.s.back,
onPressed: () {
Navigator.maybePop(context);
},

View File

@ -132,7 +132,7 @@ class EpisodeGrid extends StatelessWidget {
if (dataConfirm) {
context.read<DownloadState>().startTask(episode!);
Fluttertoast.showToast(
msg: context.s!.downloadStart,
msg: context.s.downloadStart,
gravity: ToastGravity.BOTTOM,
);
}
@ -155,7 +155,7 @@ class EpisodeGrid extends StatelessWidget {
Future<bool> _useDataConfirm(BuildContext context) async {
var ifUseData = false;
final s = context.s!;
final s = context.s;
await generalDialog(
context,
title: Text(s.cellularConfirm),
@ -205,7 +205,10 @@ class EpisodeGrid extends StatelessWidget {
/// Circel avatar widget.
Widget _circleImage(BuildContext context,
{EpisodeBrief? episode, Color? color, required bool boo, double? radius}) =>
{EpisodeBrief? episode,
Color? color,
required bool boo,
double? radius}) =>
InkWell(
onTap: () async {
if (openPodcast) {
@ -287,7 +290,8 @@ class EpisodeGrid extends StatelessWidget {
: Center();
/// Pubdate widget
Widget _pubDate(BuildContext context, {required EpisodeBrief episode, Color? color}) =>
Widget _pubDate(BuildContext context,
{required EpisodeBrief episode, Color? color}) =>
Text(
episode.pubDate!.toDate(context),
overflow: TextOverflow.visible,
@ -299,7 +303,11 @@ class EpisodeGrid extends StatelessWidget {
fontStyle: FontStyle.italic),
);
Widget _episodeCard(BuildContext context,
{int? index, Color? color, bool? isLiked, bool? isDownloaded, bool? boo}) {
{int? index,
Color? color,
bool? isLiked,
bool? isDownloaded,
bool? boo}) {
var width = context.width;
if (layout == Layout.one) {
return _layoutOneCard(context,
@ -323,7 +331,8 @@ class EpisodeGrid extends StatelessWidget {
layout != Layout.one
? _circleImage(context,
episode: episodes![index!], color: color, boo: boo!)
: _pubDate(context, episode: episodes![index!], color: color),
: _pubDate(context,
episode: episodes![index!], color: color),
Spacer(),
_isNewIndicator(episodes![index]),
_downloadIndicater(context,
@ -403,7 +412,11 @@ class EpisodeGrid extends StatelessWidget {
}
Widget _layoutOneCard(BuildContext context,
{required int index, Color? color, required bool isLiked, bool? isDownloaded, required bool boo}) {
{required int index,
Color? color,
required bool isLiked,
bool? isDownloaded,
required bool boo}) {
var width = context.width;
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
@ -443,7 +456,8 @@ class EpisodeGrid extends StatelessWidget {
),
_isNewIndicator(episodes![index]),
_downloadIndicater(context,
episode: episodes![index], isDownloaded: isDownloaded),
episode: episodes![index],
isDownloaded: isDownloaded),
_numberIndicater(context, index: index, color: color)
],
),
@ -574,7 +588,7 @@ class EpisodeGrid extends StatelessWidget {
? context.brightness == Brightness.light
? Colors.grey[200]
: Color.fromRGBO(50, 50, 50, 1)
: context.scaffoldBackgroundColor,
: context.background,
boxShadow: [
BoxShadow(
color: context.brightness == Brightness.light
@ -590,7 +604,8 @@ class EpisodeGrid extends StatelessWidget {
color: Colors.transparent,
child: InkWell(
onTap: () {
if (!selectedList!.contains(episodes![index])) {
if (!selectedList!
.contains(episodes![index])) {
_selectedList = selectedList;
_selectedList!.add(episodes![index]);
} else {
@ -609,7 +624,7 @@ class EpisodeGrid extends StatelessWidget {
: context.brightness ==
Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
: context.background,
width: 1.0,
),
),
@ -628,7 +643,7 @@ class EpisodeGrid extends StatelessWidget {
border: Border.all(
color: context.brightness == Brightness.light
? context.primaryColor
: context.scaffoldBackgroundColor,
: context.background,
width: 1.0,
),
),
@ -687,8 +702,8 @@ class EpisodeGrid extends StatelessWidget {
onPressed: () {
if (!data.item2.contains(
episodes![index].enclosureUrl)) {
audio
.addToPlaylist(episodes![index]);
audio.addToPlaylist(
episodes![index]);
Fluttertoast.showToast(
msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM,
@ -787,8 +802,7 @@ class EpisodeGrid extends StatelessWidget {
color: context.textColor!
.withOpacity(0.5)))
: Text(s.download),
trailingIcon: Icon(
LineIcons.download,
trailingIcon: Icon(LineIcons.download,
color: Colors.green),
onPressed: () async {
if (!isDownloaded) {
@ -858,9 +872,9 @@ class OpenContainerWrapper extends StatelessWidget {
beginColor: Theme.of(context).primaryColor,
endColor: Theme.of(context).primaryColor,
closedColor: Theme.of(context).brightness == Brightness.light
? Theme.of(context).primaryColor
: Theme.of(context).scaffoldBackgroundColor,
openColor: Theme.of(context).scaffoldBackgroundColor,
? context.primaryColor
: context.background,
openColor: context.background,
openElevation: 0,
closedElevation: 0,
openShape:

View File

@ -22,7 +22,7 @@ Widget featureDiscoveryOverlay(BuildContext context,
required Widget tapTarget,
required String title,
required String description}) {
final s = context.s!;
final s = context.s;
return DescribedFeatureOverlay(
featureId: featureId,
tapTarget: tapTarget,

View File

@ -140,7 +140,7 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
Future<bool> _useDataConfirm() async {
var ifUseData = false;
final s = context.s!;
final s = context.s;
await generalDialog(
context,
title: Text(s.cellularConfirm),
@ -575,7 +575,7 @@ class __NewPlaylistState extends State<_NewPlaylist> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

@ -58,7 +58,7 @@ class _SettingsSheetState extends State<SettingsSheet>
Navigator.pop(context);
},
child: Container(
color: context.scaffoldBackgroundColor.withOpacity(
color: context.background.withOpacity(
0.8 * math.min(_animation.value / widget.height, 1.0)),
),
),

View File

@ -68,6 +68,8 @@ dependencies:
collection: ^1.15.0-nullsafety.4
shared_preferences_android: ^2.0.12
path_provider_android: ^2.0.14
material_color_utilities: ^0.1.4
dynamic_color: ^1.4.0
dependency_overrides:
linkify: