feat: intgrate material you design
This commit is contained in:
parent
49a70fb833
commit
bb6f57a6a3
|
@ -3,23 +3,20 @@ import 'dart:developer' as developer;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.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: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 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import '../home/audioplayer.dart';
|
import '../home/audioplayer.dart';
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
import '../state/audio_state.dart';
|
import '../state/audio_state.dart';
|
||||||
import '../state/setting_state.dart';
|
|
||||||
import '../type/episodebrief.dart';
|
import '../type/episodebrief.dart';
|
||||||
import '../type/play_histroy.dart';
|
import '../type/play_histroy.dart';
|
||||||
import '../util/extension_helper.dart';
|
import '../util/extension_helper.dart';
|
||||||
import '../widgets/audiopanel.dart';
|
import '../widgets/audiopanel.dart';
|
||||||
import '../widgets/custom_widget.dart';
|
import '../widgets/custom_widget.dart';
|
||||||
import 'episode_download.dart';
|
|
||||||
|
|
||||||
class EpisodeDetail extends StatefulWidget {
|
class EpisodeDetail extends StatefulWidget {
|
||||||
final EpisodeBrief? episodeItem;
|
final EpisodeBrief? episodeItem;
|
||||||
|
@ -48,23 +45,23 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||||
return await dbHelper.getPosition(episode);
|
return await dbHelper.getPosition(episode);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollController? _controller;
|
late ScrollController _controller;
|
||||||
_scrollListener() {
|
_scrollListener() {
|
||||||
if (_controller!.position.userScrollDirection == ScrollDirection.reverse) {
|
if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
|
||||||
if (_showMenu && mounted) {
|
if (_showMenu && mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showMenu = false;
|
_showMenu = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_controller!.position.userScrollDirection == ScrollDirection.forward) {
|
if (_controller.position.userScrollDirection == ScrollDirection.forward) {
|
||||||
if (!_showMenu && mounted) {
|
if (!_showMenu && mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showMenu = true;
|
_showMenu = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_controller!.offset > context.textTheme.headline5!.fontSize!) {
|
if (_controller.offset > context.textTheme.headline5!.fontSize!) {
|
||||||
if (!_showTitle) setState(() => _showTitle = true);
|
if (!_showTitle) setState(() => _showTitle = true);
|
||||||
} else if (_showTitle) setState(() => _showTitle = false);
|
} else if (_showTitle) setState(() => _showTitle = false);
|
||||||
}
|
}
|
||||||
|
@ -75,623 +72,256 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
||||||
_showMenu = true;
|
_showMenu = true;
|
||||||
_showTitle = false;
|
_showTitle = false;
|
||||||
_controller = ScrollController();
|
_controller = ScrollController();
|
||||||
_controller!.addListener(_scrollListener);
|
_controller.addListener(_scrollListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_controller!.dispose();
|
_controller.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
final s = context.s;
|
||||||
SystemUiOverlayStyle(
|
final audio = context.watch<AudioPlayerNotifier>();
|
||||||
statusBarColor: Colors.transparent,
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
systemNavigationBarColor: Colors.transparent,
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarColor: context.priamryContainer,
|
||||||
|
systemNavigationBarColor: context.priamryContainer,
|
||||||
systemNavigationBarContrastEnforced: false,
|
systemNavigationBarContrastEnforced: false,
|
||||||
systemNavigationBarIconBrightness: context.iconBrightness,
|
systemNavigationBarIconBrightness: context.iconBrightness,
|
||||||
statusBarBrightness: context.brightness,
|
statusBarBrightness: context.brightness,
|
||||||
statusBarIconBrightness: context.iconBrightness),
|
statusBarIconBrightness: context.iconBrightness),
|
||||||
);
|
child: WillPopScope(
|
||||||
final s = context.s!;
|
onWillPop: () async {
|
||||||
final audio = context.watch<AudioPlayerNotifier>();
|
if (_playerKey.currentState != null &&
|
||||||
return WillPopScope(
|
_playerKey.currentState!.initSize! > 100) {
|
||||||
onWillPop: () async {
|
_playerKey.currentState!.backToMini();
|
||||||
if (_playerKey.currentState != null &&
|
return false;
|
||||||
_playerKey.currentState!.initSize! > 100) {
|
} else {
|
||||||
_playerKey.currentState!.backToMini();
|
return true;
|
||||||
return false;
|
}
|
||||||
} else {
|
},
|
||||||
return true;
|
child: Scaffold(
|
||||||
}
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
},
|
body: SafeArea(
|
||||||
child: Scaffold(
|
child: Stack(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
children: <Widget>[
|
||||||
body: SafeArea(
|
StretchingOverscrollIndicator(
|
||||||
child: Stack(
|
axisDirection: AxisDirection.down,
|
||||||
children: <Widget>[
|
child: NestedScrollView(
|
||||||
ScrollConfiguration(
|
scrollDirection: Axis.vertical,
|
||||||
behavior: NoGrowBehavior(),
|
controller: _controller,
|
||||||
child: NestedScrollView(
|
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||||
scrollDirection: Axis.vertical,
|
return <Widget>[
|
||||||
controller: _controller,
|
SliverAppBar(
|
||||||
headerSliverBuilder: (context, innerBoxScrolled) {
|
backgroundColor: context.priamryContainer,
|
||||||
return <Widget>[
|
floating: true,
|
||||||
SliverAppBar(
|
pinned: true,
|
||||||
floating: true,
|
title: _showTitle
|
||||||
pinned: true,
|
? Text(
|
||||||
title: _showTitle
|
widget.episodeItem?.title ?? '',
|
||||||
? Text(
|
maxLines: 1,
|
||||||
widget.episodeItem?.title ?? '',
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
)
|
||||||
overflow: TextOverflow.ellipsis,
|
: Text(
|
||||||
)
|
widget.episodeItem!.feedTitle!,
|
||||||
: Text(
|
maxLines: 1,
|
||||||
widget.episodeItem!.feedTitle!,
|
style: TextStyle(
|
||||||
maxLines: 1,
|
fontSize: 15,
|
||||||
style: TextStyle(
|
color:
|
||||||
fontSize: 15,
|
context.textColor.withOpacity(0.7)),
|
||||||
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(
|
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
|
||||||
padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
|
child: Row(
|
||||||
child: Row(
|
children: [
|
||||||
children: [
|
Text(
|
||||||
Text(
|
s.published(formateDate(
|
||||||
s.published(DateFormat.yMMMd().format(
|
widget.episodeItem!.pubDate!)),
|
||||||
DateTime.fromMillisecondsSinceEpoch(
|
style:
|
||||||
widget.episodeItem!.pubDate!))),
|
TextStyle(color: context.accentColor)),
|
||||||
style: TextStyle(color: context.accentColor)),
|
SizedBox(width: 10),
|
||||||
SizedBox(width: 10),
|
if (widget.episodeItem!.explicit == 1)
|
||||||
if (widget.episodeItem!.explicit == 1)
|
Text('E',
|
||||||
Text('E',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontWeight: FontWeight.bold,
|
||||||
fontWeight: FontWeight.bold,
|
color: context.error)),
|
||||||
color: Colors.red)),
|
Spacer(),
|
||||||
Spacer(),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: EdgeInsets.symmetric(
|
||||||
padding:
|
horizontal: 20, vertical: 5),
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 5),
|
child: Row(
|
||||||
child: Row(
|
children: <Widget>[
|
||||||
children: <Widget>[
|
if (widget.episodeItem!.duration != 0)
|
||||||
if (widget.episodeItem!.duration != 0)
|
Container(
|
||||||
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(
|
decoration: BoxDecoration(
|
||||||
color: Colors.cyan[300],
|
color: context.tertiary,
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(16.0))),
|
Radius.circular(16.0))),
|
||||||
height: 28.0,
|
height: 30.0,
|
||||||
margin: EdgeInsets.only(right: 10.0),
|
margin: EdgeInsets.only(right: 12.0),
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.symmetric(horizontal: 10.0),
|
EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
s.minsCount(
|
'${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
|
||||||
widget.episodeItem!.duration! ~/ 60,
|
style:
|
||||||
),
|
TextStyle(color: context.onPrimary),
|
||||||
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),
|
|
||||||
),
|
),
|
||||||
),
|
FutureBuilder<PlayHistory>(
|
||||||
FutureBuilder<PlayHistory>(
|
future: _getPosition(widget.episodeItem!),
|
||||||
future: _getPosition(widget.episodeItem!),
|
builder: (context, snapshot) {
|
||||||
builder: (context, snapshot) {
|
if (snapshot.hasError) {
|
||||||
if (snapshot.hasError) {
|
developer.log(snapshot.error as String);
|
||||||
developer.log(snapshot.error as String);
|
}
|
||||||
}
|
if (snapshot.hasData &&
|
||||||
if (snapshot.hasData &&
|
snapshot.data!.seekValue! < 0.9 &&
|
||||||
snapshot.data!.seekValue! < 0.9 &&
|
snapshot.data!.seconds! > 10) {
|
||||||
snapshot.data!.seconds! > 10) {
|
return ButtonTheme(
|
||||||
return ButtonTheme(
|
height: 28,
|
||||||
height: 28,
|
padding: EdgeInsets.symmetric(
|
||||||
padding:
|
horizontal: 0),
|
||||||
EdgeInsets.symmetric(horizontal: 0),
|
child: OutlinedButton(
|
||||||
child: OutlinedButton(
|
style: OutlinedButton.styleFrom(
|
||||||
style: OutlinedButton.styleFrom(
|
shape: RoundedRectangleBorder(
|
||||||
shape: RoundedRectangleBorder(
|
borderRadius:
|
||||||
borderRadius:
|
BorderRadius.circular(
|
||||||
BorderRadius.circular(
|
100.0),
|
||||||
100.0),
|
side: BorderSide(
|
||||||
side: BorderSide(
|
color:
|
||||||
color:
|
context.accentColor)),
|
||||||
context.accentColor)),
|
),
|
||||||
),
|
onPressed: () => audio.episodeLoad(
|
||||||
onPressed: () => audio.episodeLoad(
|
widget.episodeItem,
|
||||||
widget.episodeItem,
|
startPosition:
|
||||||
startPosition:
|
(snapshot.data!.seconds! *
|
||||||
(snapshot.data!.seconds! *
|
1000)
|
||||||
1000)
|
.toInt()),
|
||||||
.toInt()),
|
child: Row(
|
||||||
child: Row(
|
children: [
|
||||||
children: [
|
SizedBox(
|
||||||
SizedBox(
|
width: 20,
|
||||||
width: 20,
|
height: 20,
|
||||||
height: 20,
|
child: CustomPaint(
|
||||||
child: CustomPaint(
|
painter: ListenedPainter(
|
||||||
painter: ListenedPainter(
|
context.textColor,
|
||||||
context.textColor,
|
stroke: 2.0),
|
||||||
stroke: 2.0),
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 5),
|
||||||
SizedBox(width: 5),
|
Text(
|
||||||
Text(
|
snapshot
|
||||||
snapshot.data!.seconds!.toTime,
|
.data!.seconds!.toTime,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
return Center();
|
||||||
return Center();
|
}
|
||||||
}
|
}),
|
||||||
}),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
ShowNote(episode: widget.episodeItem),
|
||||||
ShowNote(episode: widget.episodeItem),
|
Selector<AudioPlayerNotifier,
|
||||||
Selector<AudioPlayerNotifier,
|
Tuple2<bool, PlayerHeight?>>(
|
||||||
Tuple2<bool, PlayerHeight?>>(
|
selector: (_, audio) => Tuple2(
|
||||||
selector: (_, audio) =>
|
audio.playerRunning, audio.playerHeight),
|
||||||
Tuple2(audio.playerRunning, audio.playerHeight),
|
builder: (_, data, __) {
|
||||||
builder: (_, data, __) {
|
final height =
|
||||||
var height = kMinPlayerHeight[data.item2!.index];
|
kMinPlayerHeight[data.item2!.index];
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: data.item1 ? height : 0,
|
height: data.item1 ? height : 0,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
|
||||||
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
|
selector: (_, audio) =>
|
||||||
selector: (_, audio) =>
|
Tuple2(audio.playerRunning, audio.playerHeight),
|
||||||
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,
|
|
||||||
builder: (_, data, __) {
|
builder: (_, data, __) {
|
||||||
final inPlaylist = data.contains(widget.episodeItem);
|
final height = kMinPlayerHeight[data.item2!.index];
|
||||||
return inPlaylist
|
return Container(
|
||||||
? _buttonOnMenu(
|
alignment: Alignment.bottomCenter,
|
||||||
child: Icon(Icons.playlist_add_check,
|
padding:
|
||||||
color: context.accentColor),
|
EdgeInsets.only(bottom: data.item1 ? height : 0),
|
||||||
onTap: () {
|
child: AnimatedContainer(
|
||||||
audio.delFromPlaylist(widget.episodeItem!);
|
duration: Duration(milliseconds: 400),
|
||||||
Fluttertoast.showToast(
|
height: _showMenu ? 50 : 0,
|
||||||
msg: s!.toastRemovePlaylist,
|
child: SingleChildScrollView(
|
||||||
gravity: ToastGravity.BOTTOM,
|
scrollDirection: Axis.vertical,
|
||||||
);
|
child: MenuBar(
|
||||||
})
|
episodeItem: widget.episodeItem,
|
||||||
: _buttonOnMenu(
|
heroTag: widget.heroTag,
|
||||||
child: Icon(Icons.playlist_add,
|
hide: widget.hide),
|
||||||
color: Colors.grey[700]),
|
),
|
||||||
onTap: () {
|
),
|
||||||
audio.addToPlaylist(widget.episodeItem!);
|
);
|
||||||
Fluttertoast.showToast(
|
}),
|
||||||
msg: s!.toastAddPlaylist,
|
Selector<AudioPlayerNotifier, EpisodeBrief?>(
|
||||||
gravity: ToastGravity.BOTTOM,
|
selector: (_, audio) => audio.episode,
|
||||||
);
|
builder: (_, data, __) => Container(
|
||||||
});
|
child: PlayerWidget(
|
||||||
},
|
playerKey: _playerKey,
|
||||||
),
|
isPlayingPage: data == widget.episodeItem))),
|
||||||
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||||
void _deleteDownload(EpisodeBrief episode) async {
|
void _deleteDownload(EpisodeBrief episode) async {
|
||||||
Provider.of<DownloadState>(context, listen: false).delTask(episode);
|
Provider.of<DownloadState>(context, listen: false).delTask(episode);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.downloadRemovedToast,
|
msg: context.s.downloadRemovedToast,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -78,22 +78,20 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||||
|
|
||||||
Future<bool> _useDataConfirm() async {
|
Future<bool> _useDataConfirm() async {
|
||||||
var ifUseData = false;
|
var ifUseData = false;
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
await generalDialog(
|
await generalDialog(
|
||||||
context,
|
context,
|
||||||
title: Text(s.cellularConfirm),
|
title: Text(s.cellularConfirm),
|
||||||
content: Text(s.cellularConfirmDes),
|
content: Text(s.cellularConfirmDes),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
FlatButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: Navigator.of(context).pop,
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
s.cancel,
|
s.cancel,
|
||||||
style: TextStyle(color: Colors.grey[600]),
|
style: TextStyle(color: Colors.grey[600]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FlatButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ifUseData = true;
|
ifUseData = true;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -130,7 +128,7 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
duration: Duration(seconds: 1),
|
duration: Duration(seconds: 1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).accentColor,
|
color: context.accentColor,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(15.0))),
|
borderRadius: BorderRadius.all(Radius.circular(15.0))),
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
width: (_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
|
width: (_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
|
||||||
|
@ -222,7 +220,6 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 3:
|
case 3:
|
||||||
Provider.of<AudioPlayerNotifier>(context, listen: false)
|
Provider.of<AudioPlayerNotifier>(context, listen: false)
|
||||||
.updateMediaItem(task.episode!);
|
.updateMediaItem(task.episode!);
|
||||||
|
@ -251,11 +248,9 @@ class _DownloadButtonState extends State<DownloadButton> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 4:
|
case 4:
|
||||||
return _buttonOnMenu(Icon(Icons.refresh, color: Colors.red),
|
return _buttonOnMenu(Icon(Icons.refresh, color: Colors.red),
|
||||||
() => _retryDownload(task.episode!));
|
() => _retryDownload(task.episode!));
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return Center();
|
return Center();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(
|
Container(
|
||||||
height: 50.0,
|
height: 50.0,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20.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>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
@ -208,7 +209,10 @@ class _AboutAppState extends State<AboutApp> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_listItem(context, 'Twitter @tsacdop',
|
_listItem(context, 'Twitter @tsacdop',
|
||||||
LineIcons.twitter, 'https://twitter.com/tsacdop'),
|
LineIcons.twitter, 'https://twitter.com/tsacdop'),
|
||||||
_listItem(context, 'GitHub', LineIcons.alternateGithub,
|
_listItem(
|
||||||
|
context,
|
||||||
|
'GitHub',
|
||||||
|
LineIcons.alternateGithub,
|
||||||
'https://github.com/stonega/tsacdop'),
|
'https://github.com/stonega/tsacdop'),
|
||||||
_listItem(context, 'Telegram', LineIcons.telegram,
|
_listItem(context, 'Telegram', LineIcons.telegram,
|
||||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||||
|
@ -281,7 +285,7 @@ class _AboutAppState extends State<AboutApp> {
|
||||||
name: 'Bruno Pinheiro', flag: 'pt'),
|
name: 'Bruno Pinheiro', flag: 'pt'),
|
||||||
_translatorInfo(context,
|
_translatorInfo(context,
|
||||||
name: 'Edoardo Maria Elidoro', flag: 'it'),
|
name: 'Edoardo Maria Elidoro', flag: 'it'),
|
||||||
_translatorInfo(context,
|
_translatorInfo(context,
|
||||||
name: 'Murat T. Akyuz', flag: 'tr'),
|
name: 'Murat T. Akyuz', flag: 'tr'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
import 'package:marquee/marquee.dart';
|
import 'package:marquee/marquee.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:tsacdop/episodes/shownote.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
|
||||||
|
@ -499,8 +500,8 @@ class _PlaylistWidgetState extends State<PlaylistWidget> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
data.item1!.name == 'Queue'
|
data.item1!.name == 'Queue'
|
||||||
? context.s!.queue
|
? context.s.queue
|
||||||
: '${context.s!.homeMenuPlaylist}${'-${data.item1!.name}'}',
|
: '${context.s.homeMenuPlaylist}${'-${data.item1!.name}'}',
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.accentColor,
|
color: context.accentColor,
|
||||||
|
@ -826,7 +827,7 @@ class SleepModeState extends State<SleepMode>
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(context.s!.sleepTimer,
|
Text(context.s.sleepTimer,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.accentColor,
|
color: context.accentColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -935,10 +936,9 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
|
||||||
padding: EdgeInsets.symmetric(horizontal: 0),
|
padding: EdgeInsets.symmetric(horizontal: 0),
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
shape: RoundedRectangleBorder(
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
borderRadius: BorderRadius.circular(100.0),
|
side: BorderSide(color: context.accentColor)),
|
||||||
side: BorderSide(color: context.accentColor)),
|
|
||||||
),
|
),
|
||||||
// highlightedBorderColor: Colors.green[700],
|
// highlightedBorderColor: Colors.green[700],
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -1088,7 +1088,7 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
context.s!.homeToprightMenuAbout,
|
context.s.homeToprightMenuAbout,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.accentColor,
|
color: context.accentColor,
|
||||||
|
@ -1332,7 +1332,7 @@ class _ControlPanelState extends State<ControlPanel>
|
||||||
.buffering ||
|
.buffering ||
|
||||||
data.audioState ==
|
data.audioState ==
|
||||||
AudioProcessingState.loading
|
AudioProcessingState.loading
|
||||||
? context.s!.buffering
|
? context.s.buffering
|
||||||
: '',
|
: '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.accentColor),
|
color: context.accentColor),
|
||||||
|
|
|
@ -64,7 +64,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
_controller = TabController(length: 3, vsync: this);
|
_controller = TabController(length: 3, vsync: this);
|
||||||
// FeatureDiscovery.hasPreviouslyCompleted(context, addFeature).then((value) {
|
// FeatureDiscovery.hasPreviouslyCompleted(context, addFeature).then((value) {
|
||||||
// if (!value) {
|
// if (!value) {
|
||||||
SchedulerBinding.instance!.addPostFrameCallback((_) {
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
FeatureDiscovery.discoverFeatures(
|
FeatureDiscovery.discoverFeatures(
|
||||||
context,
|
context,
|
||||||
const <String>{
|
const <String>{
|
||||||
|
@ -90,15 +90,15 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
double top = 0;
|
double top = 0;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var height = (context.width - 20) / 3 + 140;
|
final height = (context.width - 20) / 3 + 140;
|
||||||
var settings = Provider.of<SettingState>(context, listen: false);
|
final settings = Provider.of<SettingState>(context, listen: false);
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
systemNavigationBarIconBrightness:
|
systemNavigationBarIconBrightness: context.brightness,
|
||||||
context.brightness,
|
|
||||||
statusBarIconBrightness: context.iconBrightness,
|
statusBarIconBrightness: context.iconBrightness,
|
||||||
systemNavigationBarColor: context.primaryColor,
|
systemNavigationBarColor: context.onPrimary,
|
||||||
|
statusBarColor: context.onPrimary,
|
||||||
),
|
),
|
||||||
child: WillPopScope(
|
child: WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
|
@ -115,12 +115,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
|
backgroundColor: context.onPrimary,
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SafeArea(
|
SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: ScrollConfiguration(
|
child: StretchingOverscrollIndicator(
|
||||||
behavior: NoGrowBehavior(),
|
axisDirection: AxisDirection.down,
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
innerScrollPositionKeyBuilder: () {
|
innerScrollPositionKeyBuilder: () {
|
||||||
return Key('tab${_controller!.index}');
|
return Key('tab${_controller!.index}');
|
||||||
|
@ -141,7 +142,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
context,
|
context,
|
||||||
featureId: addFeature,
|
featureId: addFeature,
|
||||||
tapTarget: Icon(Icons.add_circle_outline),
|
tapTarget: Icon(Icons.add_circle_outline),
|
||||||
title: s!.featureDiscoverySearch,
|
title: s.featureDiscoverySearch,
|
||||||
backgroundColor: Colors.cyan[600],
|
backgroundColor: Colors.cyan[600],
|
||||||
buttonColor: Colors.cyan[500],
|
buttonColor: Colors.cyan[500],
|
||||||
description: s.featureDiscoverySearchDes,
|
description: s.featureDiscoverySearchDes,
|
||||||
|
@ -240,14 +241,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Selector<AudioPlayerNotifier, bool>(
|
Selector<AudioPlayerNotifier, bool>(
|
||||||
selector: (_, audio) =>
|
selector: (_, audio) => audio.playerRunning,
|
||||||
audio.playerRunning,
|
builder: (_, data, __) {
|
||||||
builder: (_, data, __) {
|
return Padding(
|
||||||
return Padding(
|
padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
|
||||||
padding:
|
);
|
||||||
EdgeInsets.only(bottom: data ? 60.0 : 0),
|
},
|
||||||
);
|
),
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -274,9 +274,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||||
@override
|
@override
|
||||||
Widget build(
|
Widget build(
|
||||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return Container(
|
return Container(
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -335,7 +335,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(100),
|
borderRadius: BorderRadius.circular(100),
|
||||||
|
@ -406,19 +406,23 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 2),
|
padding: EdgeInsets.symmetric(vertical: 2),
|
||||||
),
|
),
|
||||||
Container(
|
SizedBox(
|
||||||
height: 70,
|
height: 70,
|
||||||
width: 140,
|
width: 140,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
(data.item3 ~/ 1000).toTime,
|
(data.item3 ~/ 1000).toTime,
|
||||||
|
style:
|
||||||
|
TextStyle(color: context.textColor),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
data.item2!.title!,
|
data.item2!.title!,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
|
style:
|
||||||
|
TextStyle(color: context.textColor),
|
||||||
// style: TextStyle(color: Colors.white),
|
// style: TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -443,7 +447,10 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
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(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => PlaylistHome(),
|
builder: (_) => PlaylistHome(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -530,7 +537,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
|
||||||
refreshWorker.start(_group);
|
refreshWorker.start(_group);
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.refreshStarted,
|
msg: context.s.refreshStarted,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -593,14 +600,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
|
||||||
builder: (context, groupList, child) => PopupMenuButton<String>(
|
builder: (context, groupList, child) => PopupMenuButton<String>(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
tooltip: context.s!.groupFilter,
|
tooltip: context.s.groupFilter,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
height: 50,
|
height: 50,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(_groupName == 'All' ? context.s!.all : _groupName!),
|
Text(_groupName == 'All' ? context.s.all : _groupName!),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
),
|
),
|
||||||
|
@ -613,7 +620,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
Text(context.s!.all),
|
Text(context.s.all),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
if (_groupName == 'All') DotIndicator()
|
if (_groupName == 'All') DotIndicator()
|
||||||
]),
|
]),
|
||||||
|
@ -806,7 +813,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
|
||||||
Material(
|
Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: context.s!.refresh,
|
tooltip: context.s.refresh,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
LineIcons.alternateRedo,
|
LineIcons.alternateRedo,
|
||||||
size: 16),
|
size: 16),
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,72 +23,10 @@ class PopupMenu extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PopupMenuState extends State<PopupMenu> {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var refreshWorker = Provider.of<RefreshWorker>(context, listen: false);
|
var refreshWorker = Provider.of<RefreshWorker>(context, listen: false);
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(100),
|
borderRadius: BorderRadius.circular(100),
|
||||||
|
@ -98,10 +36,11 @@ class _PopupMenuState extends State<PopupMenu> {
|
||||||
width: 40,
|
width: 40,
|
||||||
child: PopupMenuButton<int>(
|
child: PopupMenuButton<int>(
|
||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
shape: RoundedRectangleBorder(
|
shape:
|
||||||
borderRadius: BorderRadius.circular(10)),
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
tooltip: s.menu,
|
tooltip: s.menu,
|
||||||
|
color: context.priamryContainer,
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 1,
|
value: 1,
|
||||||
|
@ -126,8 +65,8 @@ class _PopupMenuState extends State<PopupMenu> {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return Text(
|
return Text(
|
||||||
snapshot.data!,
|
snapshot.data!,
|
||||||
style:
|
style: TextStyle(
|
||||||
TextStyle(color: Colors.red, fontSize: 12),
|
color: Colors.red, fontSize: 12),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Center();
|
return Center();
|
||||||
|
@ -141,7 +80,7 @@ class _PopupMenuState extends State<PopupMenu> {
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 2,
|
value: 2,
|
||||||
child: Container(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(left: 10),
|
padding: EdgeInsets.only(left: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
||||||
// disabledTextColor: Colors.grey[500],
|
// disabledTextColor: Colors.grey[500],
|
||||||
// disabledBorderColor: Colors.grey[500],
|
// disabledBorderColor: Colors.grey[500],
|
||||||
),
|
),
|
||||||
child: Text(context.s!.subscribe),
|
child: Text(context.s.subscribe),
|
||||||
onPressed: () {}),
|
onPressed: () {}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -277,7 +277,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
||||||
padding: EdgeInsets.fromLTRB(50, 20, 50, 20),
|
padding: EdgeInsets.fromLTRB(50, 20, 50, 20),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
context.s!.searchHelper,
|
context.s.searchHelper,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: context.textTheme.headline6!
|
style: context.textTheme.headline6!
|
||||||
.copyWith(color: Colors.grey[400]),
|
.copyWith(color: Colors.grey[400]),
|
||||||
|
@ -477,7 +477,7 @@ class __TopPodcastListState extends State<_TopPodcastList> {
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
))
|
))
|
||||||
: Text(context.s!.loadMore),
|
: Text(context.s.loadMore),
|
||||||
onPressed: () => _loading
|
onPressed: () => _loading
|
||||||
? null
|
? null
|
||||||
: setState(
|
: setState(
|
||||||
|
|
|
@ -52,7 +52,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
|
||||||
Widget _invalidRss(BuildContext context) => Container(
|
Widget _invalidRss(BuildContext context) => Container(
|
||||||
padding: EdgeInsets.only(top: 200),
|
padding: EdgeInsets.only(top: 200),
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: Text(context.s!.searchInvalidRss,
|
child: Text(context.s.searchInvalidRss,
|
||||||
style: context.textTheme.headline6!.copyWith(color: Colors.red)),
|
style: context.textTheme.headline6!.copyWith(color: Colors.red)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: context.s!.back,
|
tooltip: context.s.back,
|
||||||
splashRadius: 20,
|
splashRadius: 20,
|
||||||
icon: Icon(_getIconData(Theme.of(context).platform)),
|
icon: Icon(_getIconData(Theme.of(context).platform)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -111,7 +111,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
if (query.isNotEmpty)
|
if (query.isNotEmpty)
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: context.s!.clear,
|
tooltip: context.s.clear,
|
||||||
splashRadius: 20,
|
splashRadius: 20,
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -216,7 +216,7 @@ class _RssResultState extends State<RssResult> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var items = widget.rssFeed!.items!;
|
var items = widget.rssFeed!.items!;
|
||||||
return DefaultTabController(
|
return DefaultTabController(
|
||||||
length: 2,
|
length: 2,
|
||||||
|
@ -339,7 +339,7 @@ class _RssResultState extends State<RssResult> {
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.all(Radius.circular(100))),
|
BorderRadius.all(Radius.circular(100))),
|
||||||
),
|
),
|
||||||
child: Text(context.s!.loadMore),
|
child: Text(context.s.loadMore),
|
||||||
onPressed: () => setState(
|
onPressed: () => setState(
|
||||||
() => _loadItems += 10,
|
() => _loadItems += 10,
|
||||||
),
|
),
|
||||||
|
@ -560,7 +560,7 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
|
||||||
: CircularProgressIndicator(
|
: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
))
|
))
|
||||||
: Text(context.s!.loadMore),
|
: Text(context.s.loadMore),
|
||||||
onPressed: () => _loading
|
onPressed: () => _loading
|
||||||
? null
|
? null
|
||||||
: setState(
|
: setState(
|
||||||
|
@ -722,7 +722,7 @@ class __PodcastIndexSearchState extends State<_PodcastIndexSearch> {
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
))
|
))
|
||||||
: Text(context.s!.loadMore),
|
: Text(context.s.loadMore),
|
||||||
onPressed: () => _loading
|
onPressed: () => _loading
|
||||||
? null
|
? null
|
||||||
: setState(
|
: setState(
|
||||||
|
@ -1011,7 +1011,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
))
|
))
|
||||||
: Text(context.s!.loadMore),
|
: Text(context.s.loadMore),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (widget.searchEngine ==
|
if (widget.searchEngine ==
|
||||||
SearchEngine.listenNotes &&
|
SearchEngine.listenNotes &&
|
||||||
|
@ -1057,12 +1057,11 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
||||||
foregroundColor: MaterialStateProperty.all<Color>(
|
foregroundColor: MaterialStateProperty.all<Color>(
|
||||||
context.accentColor),
|
context.accentColor),
|
||||||
overlayColor: MaterialStateProperty.all<Color>(
|
overlayColor: MaterialStateProperty.all<Color>(
|
||||||
context.scaffoldBackgroundColor
|
context.background.withOpacity(0.3)),
|
||||||
.withOpacity(0.3)),
|
|
||||||
padding:
|
padding:
|
||||||
MaterialStateProperty.all<EdgeInsetsGeometry>(
|
MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||||
EdgeInsets.symmetric(horizontal: 2))),
|
EdgeInsets.symmetric(horizontal: 2))),
|
||||||
child: Text(context.s!.play.toUpperCase()),
|
child: Text(context.s.play.toUpperCase()),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<AudioPlayerNotifier>().episodeLoad(
|
context.read<AudioPlayerNotifier>().episodeLoad(
|
||||||
content[index].toEpisode,
|
content[index].toEpisode,
|
||||||
|
@ -1215,7 +1214,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
child: TabBarView(children: [
|
child: TabBarView(children: [
|
||||||
ListView(
|
ListView(
|
||||||
physics: _animation.value != widget.maxHeight
|
physics: _animation.value != widget.maxHeight
|
||||||
|
@ -1327,7 +1326,7 @@ class PodcastSlideup extends StatelessWidget {
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: searchState.clearSelect,
|
onTap: searchState.clearSelect,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: context.scaffoldBackgroundColor.withOpacity(0.9),
|
color: context.background.withOpacity(0.9),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -142,7 +142,7 @@ class _SlideIntroState extends State<SlideIntro> {
|
||||||
height: 40,
|
height: 40,
|
||||||
width: 80,
|
width: 80,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(context.s!.next,
|
child: Text(context.s.next,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight:
|
fontWeight:
|
||||||
|
@ -164,7 +164,7 @@ class _SlideIntroState extends State<SlideIntro> {
|
||||||
height: 40,
|
height: 40,
|
||||||
width: 80,
|
width: 80,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(context.s!.done,
|
child: Text(context.s.done,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight:
|
fontWeight:
|
||||||
|
|
|
@ -24,7 +24,7 @@ class _FourthPageState extends State<FourthPage> {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
|
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.s!.introFourthPage,
|
context.s.introFourthPage,
|
||||||
style: TextStyle(fontSize: 30, color: Colors.white),
|
style: TextStyle(fontSize: 30, color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,7 +24,7 @@ class _SecondPageState extends State<SecondPage> {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
|
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.s!.introSecondPage,
|
context.s.introSecondPage,
|
||||||
style: TextStyle(fontSize: 30, color: Colors.white),
|
style: TextStyle(fontSize: 30, color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,7 +24,7 @@ class _ThirdPageState extends State<ThirdPage> {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
|
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.s!.introThirdPage,
|
context.s.introThirdPage,
|
||||||
style: TextStyle(fontSize: 30, color: Colors.white),
|
style: TextStyle(fontSize: 30, color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -60,7 +60,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
|
||||||
Color? color}) {
|
Color? color}) {
|
||||||
return OutlinedButton.icon(
|
return OutlinedButton.icon(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
side: BorderSide(color: context.scaffoldBackgroundColor),
|
side: BorderSide(color: context.background),
|
||||||
primary: color,
|
primary: color,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
isSelected ? context.primaryColorDark : Colors.transparent,
|
isSelected ? context.primaryColorDark : Colors.transparent,
|
||||||
|
@ -73,7 +73,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
systemNavigationBarIconBrightness:
|
systemNavigationBarIconBrightness:
|
||||||
|
@ -100,7 +100,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
|
||||||
return Text(data?.title ?? '', maxLines: 1);
|
return Text(data?.title ?? '', maxLines: 1);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
backgroundColor: context.scaffoldBackgroundColor,
|
backgroundColor: context.background,
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
|
@ -994,7 +994,7 @@ class __NewPlaylistState extends State<_NewPlaylist> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
|
|
@ -36,7 +36,7 @@ class _PlaylistDetailState extends State<PlaylistDetail> {
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
splashRadius: 20,
|
splashRadius: 20,
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
tooltip: context.s!.back,
|
tooltip: context.s.back,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.maybePop(context);
|
Navigator.maybePop(context);
|
||||||
},
|
},
|
||||||
|
@ -110,7 +110,8 @@ class _PlaylistDetailState extends State<PlaylistDetail> {
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
children: episodes.map<Widget>((episode) {
|
children: episodes.map<Widget>((episode) {
|
||||||
return _PlaylistItem(episode,
|
return _PlaylistItem(episode,
|
||||||
key: ValueKey(episode!.enclosureUrl), onSelect: (episode) {
|
key: ValueKey(episode!.enclosureUrl),
|
||||||
|
onSelect: (episode) {
|
||||||
_selectedEpisodes.add(episode);
|
_selectedEpisodes.add(episode);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}, onRemove: (episode) {
|
}, onRemove: (episode) {
|
||||||
|
@ -341,8 +342,8 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
|
||||||
onPressed: () => setState(() {
|
onPressed: () => setState(() {
|
||||||
_clearConfirm = false;
|
_clearConfirm = false;
|
||||||
}),
|
}),
|
||||||
child:
|
child: Text(s!.cancel,
|
||||||
Text(s!.cancel, style: TextStyle(color: Colors.grey[600])),
|
style: TextStyle(color: Colors.grey[600])),
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
splashColor: Colors.red.withAlpha(70),
|
splashColor: Colors.red.withAlpha(70),
|
||||||
|
@ -384,8 +385,8 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
|
||||||
onPressed: () => setState(() {
|
onPressed: () => setState(() {
|
||||||
_removeConfirm = false;
|
_removeConfirm = false;
|
||||||
}),
|
}),
|
||||||
child:
|
child: Text(s!.cancel,
|
||||||
Text(s!.cancel, style: TextStyle(color: Colors.grey[600])),
|
style: TextStyle(color: Colors.grey[600])),
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
splashColor: Colors.red.withAlpha(70),
|
splashColor: Colors.red.withAlpha(70),
|
||||||
|
|
|
@ -117,7 +117,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
final result = await _dbHelper.updatePodcastRss(podcastLocal);
|
final result = await _dbHelper.updatePodcastRss(podcastLocal);
|
||||||
if (result >= 0) {
|
if (result >= 0) {
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.updateEpisodesCount(result),
|
msg: context.s.updateEpisodesCount(result),
|
||||||
gravity: ToastGravity.TOP,
|
gravity: ToastGravity.TOP,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
}
|
}
|
||||||
} else if (result != 0) {
|
} else if (result != 0) {
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.updateFailed,
|
msg: context.s.updateFailed,
|
||||||
gravity: ToastGravity.TOP,
|
gravity: ToastGravity.TOP,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -359,7 +359,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.fromLTRB(15, 10, 15, 10),
|
padding: EdgeInsets.fromLTRB(15, 10, 15, 10),
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
child: AboutPodcast(podcastLocal: widget.podcastLocal),
|
child: AboutPodcast(podcastLocal: widget.podcastLocal),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -389,7 +389,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _actionBar(BuildContext context) {
|
Widget _actionBar(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 30,
|
height: 30,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -668,7 +668,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final color = widget.podcastLocal!.primaryColor!.colorizedark();
|
final color = widget.podcastLocal!.primaryColor!.colorizedark();
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.dark,
|
statusBarIconBrightness: Brightness.dark,
|
||||||
|
@ -1138,7 +1138,7 @@ class _SearchEpisodeState extends State<SearchEpisode> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
|
|
@ -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(
|
Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
@ -166,7 +169,7 @@ class __PodcastCardState extends State<_PodcastCard>
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final c = widget.podcastLocal!.backgroudColor(context);
|
final c = widget.podcastLocal!.backgroudColor(context);
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var groupList = context.watch<GroupList>();
|
var groupList = context.watch<GroupList>();
|
||||||
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id);
|
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id);
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -243,7 +246,7 @@ class __PodcastCardState extends State<_PodcastCard>
|
||||||
: Container(
|
: Container(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
),
|
),
|
||||||
// border: Border(
|
// border: Border(
|
||||||
// bottom: BorderSide(
|
// bottom: BorderSide(
|
||||||
|
@ -365,7 +368,8 @@ class __PodcastCardState extends State<_PodcastCard>
|
||||||
tooltip: s.autoDownload,
|
tooltip: s.autoDownload,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _setAutoDownload(
|
await _setAutoDownload(
|
||||||
widget.podcastLocal!.id, !snapshot.data!);
|
widget.podcastLocal!.id,
|
||||||
|
!snapshot.data!);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -504,7 +508,7 @@ class _RenameGroupState extends State<RenameGroup> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var groupList = Provider.of<GroupList>(context, listen: false);
|
var groupList = Provider.of<GroupList>(context, listen: false);
|
||||||
List list = groupList.groups.map((e) => e!.name).toList();
|
List list = groupList.groups.map((e) => e!.name).toList();
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
|
|
@ -113,9 +113,10 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
groupList.saveOrder(groupList.groups[_index!]);
|
groupList.saveOrder(groupList.groups[_index!]);
|
||||||
groupList.drlFromOrderChanged(groupList.groups[_index!]!.name);
|
groupList
|
||||||
|
.drlFromOrderChanged(groupList.groups[_index!]!.name);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.toastSettingSaved,
|
msg: context.s.toastSettingSaved,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
_controller.reverse();
|
_controller.reverse();
|
||||||
|
@ -155,7 +156,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
@ -166,7 +167,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.s!.groups(2)),
|
title: Text(context.s.groups(2)),
|
||||||
leading: CustomBackButton(),
|
leading: CustomBackButton(),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
featureDiscoveryOverlay(
|
featureDiscoveryOverlay(
|
||||||
|
@ -218,7 +219,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
: Stack(
|
: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
child: CustomTabView(
|
child: CustomTabView(
|
||||||
itemCount: _groups.length,
|
itemCount: _groups.length,
|
||||||
tabBuilder: (context, index) => Tab(
|
tabBuilder: (context, index) => Tab(
|
||||||
|
@ -265,10 +266,8 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: context.scaffoldBackgroundColor
|
color: context.background.withOpacity(0.8 *
|
||||||
.withOpacity(0.8 *
|
math.min(_menuController.value * 2, 1.0)),
|
||||||
math.min(
|
|
||||||
_menuController.value * 2, 1.0)),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -332,7 +331,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: 5.0),
|
horizontal: 5.0),
|
||||||
),
|
),
|
||||||
Text(context.s!.editGroupName,
|
Text(context.s.editGroupName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white)),
|
color: Colors.white)),
|
||||||
],
|
],
|
||||||
|
@ -366,7 +365,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pop(),
|
.pop(),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.s!.cancel,
|
context.s.cancel,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color:
|
||||||
Colors.grey[600]),
|
Colors.grey[600]),
|
||||||
|
@ -385,7 +384,8 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
_index = _index! - 1;
|
_index = _index! - 1;
|
||||||
});
|
});
|
||||||
groupList.delGroup(
|
groupList.delGroup(
|
||||||
_groups[_index! + 1]!);
|
_groups[
|
||||||
|
_index! + 1]!);
|
||||||
} else {
|
} else {
|
||||||
groupList.delGroup(
|
groupList.delGroup(
|
||||||
_groups[_index!]!);
|
_groups[_index!]!);
|
||||||
|
@ -393,7 +393,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
context.s!.confirm,
|
context.s.confirm,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red),
|
color: Colors.red),
|
||||||
),
|
),
|
||||||
|
@ -441,7 +441,7 @@ class _PodcastManageState extends State<PodcastManage>
|
||||||
class _OrderMenu extends StatelessWidget {
|
class _OrderMenu extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
|
@ -494,7 +494,7 @@ class _AddGroupState extends State<AddGroup> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var groupList = Provider.of<GroupList>(context, listen: false);
|
var groupList = Provider.of<GroupList>(context, listen: false);
|
||||||
List list = groupList.groups.map((e) => e!.name).toList();
|
List list = groupList.groups.map((e) => e!.name).toList();
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
|
|
@ -19,6 +19,7 @@ import '../widgets/custom_widget.dart';
|
||||||
import '../widgets/duraiton_picker.dart';
|
import '../widgets/duraiton_picker.dart';
|
||||||
|
|
||||||
enum MarkStatus { start, complete, none }
|
enum MarkStatus { start, complete, none }
|
||||||
|
|
||||||
enum RefreshCoverStatus { start, complete, error, none }
|
enum RefreshCoverStatus { start, complete, error, none }
|
||||||
|
|
||||||
class PodcastSetting extends StatefulWidget {
|
class PodcastSetting extends StatefulWidget {
|
||||||
|
@ -200,7 +201,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
final groupList = context.watch<GroupList>();
|
final groupList = context.watch<GroupList>();
|
||||||
final textStyle = context.textTheme.bodyText2!;
|
final textStyle = context.textTheme.bodyText2!;
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -233,8 +234,8 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
||||||
),
|
),
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
scale: 0.8,
|
scale: 0.8,
|
||||||
child:
|
child: Switch(
|
||||||
Switch(value: snapshot.data!, onChanged: _setAutoDownload),
|
value: snapshot.data!, onChanged: _setAutoDownload),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -464,7 +465,7 @@ class _TimePicker extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return Container(
|
return Container(
|
||||||
color: context.primaryColorDark,
|
color: context.primaryColorDark,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
@ -51,7 +51,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var _groupList = Provider.of<GroupList>(context, listen: false);
|
var _groupList = Provider.of<GroupList>(context, listen: false);
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
|
||||||
titlePadding: EdgeInsets.only(
|
titlePadding: EdgeInsets.only(
|
||||||
|
@ -116,7 +116,7 @@ class _PodcastListState extends State<PodcastList> {
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.s!.podcast(2)),
|
title: Text(context.s.podcast(2)),
|
||||||
leading: CustomBackButton(),
|
leading: CustomBackButton(),
|
||||||
actions: [
|
actions: [
|
||||||
Selector<SettingState, bool?>(
|
Selector<SettingState, bool?>(
|
||||||
|
|
|
@ -79,7 +79,7 @@ class _DataBackupState extends State<DataBackup> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _importSetting(String path, BuildContext context) async {
|
Future<void> _importSetting(String path, BuildContext context) async {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var settings = context.read<SettingState>();
|
var settings = context.read<SettingState>();
|
||||||
var file = File(path);
|
var file = File(path);
|
||||||
try {
|
try {
|
||||||
|
@ -178,7 +178,7 @@ class _DataBackupState extends State<DataBackup> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: context.iconBrightness,
|
statusBarIconBrightness: context.iconBrightness,
|
||||||
|
@ -634,14 +634,14 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
} else {
|
} else {
|
||||||
if (mounted) setState(() => _loginStatus = LoginStatus.error);
|
if (mounted) setState(() => _loginStatus = LoginStatus.error);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.loginFailed,
|
msg: context.s.loginFailed,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mounted) setState(() => _loginStatus = LoginStatus.error);
|
if (mounted) setState(() => _loginStatus = LoginStatus.error);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.loginFailed,
|
msg: context.s.loginFailed,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -680,11 +680,11 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
|
|
||||||
String? _validateName(String? value) {
|
String? _validateName(String? value) {
|
||||||
if (value!.isEmpty) {
|
if (value!.isEmpty) {
|
||||||
return context.s!.usernameRequired;
|
return context.s.usernameRequired;
|
||||||
}
|
}
|
||||||
final nameExp = RegExp(r'^[A-Za-z ]+$');
|
final nameExp = RegExp(r'^[A-Za-z ]+$');
|
||||||
if (!nameExp.hasMatch(value)) {
|
if (!nameExp.hasMatch(value)) {
|
||||||
return context.s!.invalidName;
|
return context.s.invalidName;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -692,7 +692,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
String? _validatePassword(String? value) {
|
String? _validatePassword(String? value) {
|
||||||
final passwordField = _passwordFieldKey.currentState!;
|
final passwordField = _passwordFieldKey.currentState!;
|
||||||
if (passwordField.value == null || passwordField.value!.isEmpty) {
|
if (passwordField.value == null || passwordField.value!.isEmpty) {
|
||||||
return context.s!.passwdRequired;
|
return context.s.passwdRequired;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -701,13 +701,13 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
switch (_loginStatus) {
|
switch (_loginStatus) {
|
||||||
case LoginStatus.none:
|
case LoginStatus.none:
|
||||||
return Text(
|
return Text(
|
||||||
context.s!.login,
|
context.s.login,
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: Colors.white),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case LoginStatus.syncing:
|
case LoginStatus.syncing:
|
||||||
return Text(
|
return Text(
|
||||||
context.s!.settingsSyncing,
|
context.s.settingsSyncing,
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: Colors.white),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -722,7 +722,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return Text(
|
return Text(
|
||||||
context.s!.login,
|
context.s.login,
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: Colors.white),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -731,7 +731,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.dark,
|
statusBarIconBrightness: Brightness.dark,
|
||||||
|
|
|
@ -117,7 +117,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
@ -219,11 +219,9 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
),
|
),
|
||||||
Icon(
|
Icon(
|
||||||
_mode == 0
|
_mode == 0
|
||||||
? LineIcons
|
? LineIcons.hourglassStart
|
||||||
.hourglassStart
|
|
||||||
: _mode == 1
|
: _mode == 1
|
||||||
? LineIcons
|
? LineIcons.hourglassHalf
|
||||||
.hourglassHalf
|
|
||||||
: LineIcons.save,
|
: LineIcons.save,
|
||||||
size: 18,
|
size: 18,
|
||||||
)
|
)
|
||||||
|
@ -304,7 +302,8 @@ class _DownloadsManageState extends State<DownloadsManage> {
|
||||||
future: _isListened(_episodes[index]),
|
future: _isListened(_episodes[index]),
|
||||||
initialData: 0,
|
initialData: 0,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return (_onlyListened! && snapshot.data == 0)
|
return (_onlyListened! &&
|
||||||
|
snapshot.data == 0)
|
||||||
? Center()
|
? Center()
|
||||||
: Column(
|
: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|
|
@ -76,7 +76,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
|
|
||||||
Future recoverSub(BuildContext context, String url) async {
|
Future recoverSub(BuildContext context, String url) async {
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.toastPodcastRecovering,
|
msg: context.s.toastPodcastRecovering,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
var subscribeWorker = context.watch<GroupList>();
|
var subscribeWorker = context.watch<GroupList>();
|
||||||
|
@ -99,7 +99,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(e.toString(), name: 'Recover podcast error');
|
developer.log(e.toString(), name: 'Recover podcast error');
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.toastRecoverFailed,
|
msg: context.s.toastRecoverFailed,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
return Container(
|
return Container(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(vertical: 5),
|
const EdgeInsets.symmetric(vertical: 5),
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
|
@ -234,7 +234,8 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
DateFormat.yMd()
|
DateFormat.yMd()
|
||||||
.add_jm()
|
.add_jm()
|
||||||
.format(snapshot
|
.format(snapshot
|
||||||
.data![index].playdate!),
|
.data![index]
|
||||||
|
.playdate!),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.textColor!
|
color: context.textColor!
|
||||||
.withOpacity(0.8),
|
.withOpacity(0.8),
|
||||||
|
@ -318,7 +319,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var _status = snapshot.data![index].status;
|
var _status = snapshot.data![index].status;
|
||||||
return Container(
|
return Container(
|
||||||
color: context.scaffoldBackgroundColor,
|
color: context.background,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
|
@ -362,7 +363,8 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
||||||
.alternativeTrashRestore),
|
.alternativeTrashRestore),
|
||||||
onPressed: () => recoverSub(
|
onPressed: () => recoverSub(
|
||||||
context,
|
context,
|
||||||
snapshot.data![index].rssUrl!),
|
snapshot
|
||||||
|
.data![index].rssUrl!),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
@ -476,11 +478,11 @@ class HistoryChart extends StatelessWidget {
|
||||||
lineTouchData: LineTouchData(
|
lineTouchData: LineTouchData(
|
||||||
enabled: true,
|
enabled: true,
|
||||||
touchTooltipData: LineTouchTooltipData(
|
touchTooltipData: LineTouchTooltipData(
|
||||||
tooltipBgColor: context.scaffoldBackgroundColor,
|
tooltipBgColor: context.background,
|
||||||
fitInsideHorizontally: true,
|
fitInsideHorizontally: true,
|
||||||
getTooltipItems: (touchedBarSpots) {
|
getTooltipItems: (touchedBarSpots) {
|
||||||
return touchedBarSpots.map((barSpot) {
|
return touchedBarSpots.map((barSpot) {
|
||||||
return LineTooltipItem(context.s!.minsCount(barSpot.y.toInt()),
|
return LineTooltipItem(context.s.minsCount(barSpot.y.toInt()),
|
||||||
context.textTheme.subtitle1!);
|
context.textTheme.subtitle1!);
|
||||||
}).toList();
|
}).toList();
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,7 +41,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await localeStorage
|
await localeStorage
|
||||||
.saveStringList([locale!.languageCode, locale.countryCode??'']);
|
.saveStringList([locale!.languageCode, locale.countryCode ?? '']);
|
||||||
await S.load(locale);
|
await S.load(locale);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
@ -66,7 +66,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textStyle = context.textTheme.bodyText2!;
|
final textStyle = context.textTheme.bodyText2!;
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|
|
@ -184,7 +184,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Libries extends StatelessWidget {
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.s!.settingsLibraries),
|
title: Text(context.s.settingsLibraries),
|
||||||
leading: CustomBackButton(),
|
leading: CustomBackButton(),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
@ -68,7 +68,7 @@ class Libries extends StatelessWidget {
|
||||||
height: 30.0,
|
height: 30.0,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(context.s!.fonts,
|
child: Text(context.s.fonts,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyText1!
|
.bodyText1!
|
||||||
|
@ -90,7 +90,7 @@ class Libries extends StatelessWidget {
|
||||||
height: 30.0,
|
height: 30.0,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(context.s!.plugins,
|
child: Text(context.s.plugins,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyText1!
|
.bodyText1!
|
||||||
|
|
|
@ -89,7 +89,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||||
topLeft: Radius.circular(5)),
|
topLeft: Radius.circular(5)),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(context.s!.endOfEpisode,
|
child: Text(context.s.endOfEpisode,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: data.item1 == 0 ? Colors.white : null)),
|
color: data.item1 == 0 ? Colors.white : null)),
|
||||||
),
|
),
|
||||||
|
@ -113,7 +113,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||||
topRight: Radius.circular(5)),
|
topRight: Radius.circular(5)),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(context.s!.minsCount(data.item2!),
|
child: Text(context.s.minsCount(data.item2!),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: data.item1 == 1 ? Colors.white : null)),
|
color: data.item1 == 1 ? Colors.white : null)),
|
||||||
),
|
),
|
||||||
|
@ -224,7 +224,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var settings = context.watch<SettingState>();
|
var settings = context.watch<SettingState>();
|
||||||
var audio = context.watch<AudioPlayerNotifier>();
|
var audio = context.watch<AudioPlayerNotifier>();
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
@ -298,7 +298,8 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
child: Switch(
|
child: Switch(
|
||||||
value: snapshot.data!, onChanged: _saveMarkListenedSkip),
|
value: snapshot.data!,
|
||||||
|
onChanged: _saveMarkListenedSkip),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -350,7 +351,8 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||||
displayItemCount: 5,
|
displayItemCount: 5,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
value: data,
|
value: data,
|
||||||
onChanged: (dynamic value) => settings.setRewindSeconds = value,
|
onChanged: (dynamic value) =>
|
||||||
|
settings.setRewindSeconds = value,
|
||||||
items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) {
|
items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) {
|
||||||
return DropdownMenuItem<int>(
|
return DropdownMenuItem<int>(
|
||||||
value: e, child: Text(s.secCount(e)));
|
value: e, child: Text(s.secCount(e)));
|
||||||
|
@ -524,8 +526,8 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_notificationIcon(
|
_notificationIcon(Icon(Icons.pause_circle_filled),
|
||||||
Icon(Icons.pause_circle_filled), '${s!.play}| ${s.pause}'),
|
'${s!.play}| ${s.pause}'),
|
||||||
_notificationIcon(Icon(Icons.fast_forward), s.fastForward),
|
_notificationIcon(Icon(Icons.fast_forward), s.fastForward),
|
||||||
_notificationIcon(Icon(Icons.skip_next), s.skipToNext),
|
_notificationIcon(Icon(Icons.skip_next), s.skipToNext),
|
||||||
_notificationIcon(Icon(Icons.close), s.stop),
|
_notificationIcon(Icon(Icons.close), s.stop),
|
||||||
|
@ -600,7 +602,7 @@ class __SpeedListState extends State<_SpeedList> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
contentPadding:
|
contentPadding:
|
||||||
EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10),
|
EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10),
|
||||||
|
|
|
@ -68,7 +68,7 @@ class _SettingsState extends State<Settings> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
@ -107,8 +107,7 @@ class _SettingsState extends State<Settings> {
|
||||||
onTap: () => Navigator.push(context,
|
onTap: () => Navigator.push(context,
|
||||||
MaterialPageRoute(builder: (context) => ThemeSetting())),
|
MaterialPageRoute(builder: (context) => ThemeSetting())),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading:
|
leading: Icon(LineIcons.adjust, color: context.accentColor),
|
||||||
Icon(LineIcons.adjust, color: context.accentColor),
|
|
||||||
title: Text(s.settingsAppearance),
|
title: Text(s.settingsAppearance),
|
||||||
subtitle: Text(s.settingsAppearanceDes),
|
subtitle: Text(s.settingsAppearanceDes),
|
||||||
),
|
),
|
||||||
|
@ -117,8 +116,7 @@ class _SettingsState extends State<Settings> {
|
||||||
onTap: () => Navigator.push(context,
|
onTap: () => Navigator.push(context,
|
||||||
MaterialPageRoute(builder: (context) => LayoutSetting())),
|
MaterialPageRoute(builder: (context) => LayoutSetting())),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading: Icon(LineIcons.stopCircle,
|
leading: Icon(LineIcons.stopCircle, color: Colors.blueAccent),
|
||||||
color: Colors.blueAccent),
|
|
||||||
title: Text(s.settingsLayout),
|
title: Text(s.settingsLayout),
|
||||||
subtitle: Text(s.settingsLayoutDes),
|
subtitle: Text(s.settingsLayoutDes),
|
||||||
),
|
),
|
||||||
|
@ -168,8 +166,7 @@ class _SettingsState extends State<Settings> {
|
||||||
title: s.settingsLanguages, child: LanguagesSetting())
|
title: s.settingsLanguages, child: LanguagesSetting())
|
||||||
.then((value) => setState(() {})),
|
.then((value) => setState(() {})),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading: Icon(LineIcons.language,
|
leading: Icon(LineIcons.language, color: Colors.purpleAccent),
|
||||||
color: Colors.purpleAccent),
|
|
||||||
title: Text(s.settingsLanguages),
|
title: Text(s.settingsLanguages),
|
||||||
subtitle: Text(s.settingsLanguagesDes),
|
subtitle: Text(s.settingsLanguagesDes),
|
||||||
),
|
),
|
||||||
|
@ -181,8 +178,8 @@ class _SettingsState extends State<Settings> {
|
||||||
MaterialPageRoute(builder: (context) => DataBackup()));
|
MaterialPageRoute(builder: (context) => DataBackup()));
|
||||||
},
|
},
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading: Icon(LineIcons.codeFile,
|
leading:
|
||||||
color: Colors.lightGreen[700]),
|
Icon(LineIcons.codeFile, color: Colors.lightGreen[700]),
|
||||||
title: Text(s.settingsBackup),
|
title: Text(s.settingsBackup),
|
||||||
subtitle: Text(s.settingsBackupDes),
|
subtitle: Text(s.settingsBackupDes),
|
||||||
),
|
),
|
||||||
|
@ -204,8 +201,7 @@ class _SettingsState extends State<Settings> {
|
||||||
onTap: () => Navigator.push(context,
|
onTap: () => Navigator.push(context,
|
||||||
MaterialPageRoute(builder: (context) => Libries())),
|
MaterialPageRoute(builder: (context) => Libries())),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading: Icon(LineIcons.bookOpen,
|
leading: Icon(LineIcons.bookOpen, color: Colors.purple[700]),
|
||||||
color: Colors.purple[700]),
|
|
||||||
title: Text(s.settingsLibraries),
|
title: Text(s.settingsLibraries),
|
||||||
subtitle: Text(s.settingsLibrariesDes),
|
subtitle: Text(s.settingsLibrariesDes),
|
||||||
),
|
),
|
||||||
|
@ -257,8 +253,7 @@ class _SettingsState extends State<Settings> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading:
|
leading: Icon(LineIcons.capsules, color: Colors.pinkAccent),
|
||||||
Icon(LineIcons.capsules, color: Colors.pinkAccent),
|
|
||||||
title: Text(s.settingsDiscovery),
|
title: Text(s.settingsDiscovery),
|
||||||
),
|
),
|
||||||
Divider(height: 1),
|
Divider(height: 1),
|
||||||
|
@ -269,8 +264,7 @@ class _SettingsState extends State<Settings> {
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
SlideIntro(goto: Goto.settings))),
|
SlideIntro(goto: Goto.settings))),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
leading:
|
leading: Icon(LineIcons.columns, color: Colors.blueGrey),
|
||||||
Icon(LineIcons.columns, color: Colors.blueGrey),
|
|
||||||
title: Text(s.settingsAppIntro),
|
title: Text(s.settingsAppIntro),
|
||||||
),
|
),
|
||||||
Divider(height: 1),
|
Divider(height: 1),
|
||||||
|
|
|
@ -110,7 +110,7 @@ class _StorageSettingState extends State<StorageSetting>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var settings = Provider.of<SettingState>(context, listen: false);
|
var settings = Provider.of<SettingState>(context, listen: false);
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
|
|
|
@ -11,7 +11,7 @@ import '../widgets/custom_widget.dart';
|
||||||
class SyncingSetting extends StatelessWidget {
|
class SyncingSetting extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var settings = Provider.of<SettingState>(context, listen: false);
|
var settings = Provider.of<SettingState>(context, listen: false);
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
|
|
|
@ -10,7 +10,7 @@ import '../widgets/general_dialog.dart';
|
||||||
class ThemeSetting extends StatelessWidget {
|
class ThemeSetting extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
var settings = Provider.of<SettingState>(context, listen: false);
|
var settings = Provider.of<SettingState>(context, listen: false);
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
|
|
|
@ -65,9 +65,10 @@ final showNotesFontStyles = <TextStyle>[
|
||||||
height: 1.8,
|
height: 1.8,
|
||||||
)),
|
)),
|
||||||
GoogleFonts.bitter(
|
GoogleFonts.bitter(
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
height: 1.8,
|
height: 1.8,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
class SettingState extends ChangeNotifier {
|
class SettingState extends ChangeNotifier {
|
||||||
|
@ -133,15 +134,13 @@ class SettingState extends ChangeNotifier {
|
||||||
/// Load locale.
|
/// Load locale.
|
||||||
Locale? get locale => _locale;
|
Locale? get locale => _locale;
|
||||||
|
|
||||||
/// Spp thememode. default auto.
|
/// Set thememode. default auto.
|
||||||
ThemeMode? _theme;
|
ThemeMode? _theme;
|
||||||
ThemeMode? get theme => _theme;
|
ThemeMode? get theme => _theme;
|
||||||
|
|
||||||
ThemeData get lightTheme => ThemeData().copyWith(
|
ThemeData get lightTheme => ThemeData.light().copyWith(
|
||||||
colorScheme: ThemeData()
|
colorScheme: _colors(Brightness.light, _accentSetColor!),
|
||||||
.colorScheme
|
brightness: Brightness.light,
|
||||||
.copyWith(brightness: Brightness.light, secondary: _accentSetColor),
|
|
||||||
brightness: Brightness.dark,
|
|
||||||
primaryColor: Colors.grey[100],
|
primaryColor: Colors.grey[100],
|
||||||
primaryColorLight: Colors.white,
|
primaryColorLight: Colors.white,
|
||||||
primaryColorDark: Colors.grey[300],
|
primaryColorDark: Colors.grey[300],
|
||||||
|
@ -154,6 +153,10 @@ class SettingState extends ChangeNotifier {
|
||||||
iconTheme: IconThemeData(color: Colors.black),
|
iconTheme: IconThemeData(color: Colors.black),
|
||||||
systemOverlayStyle: SystemUiOverlayStyle.dark),
|
systemOverlayStyle: SystemUiOverlayStyle.dark),
|
||||||
textTheme: TextTheme(
|
textTheme: TextTheme(
|
||||||
|
headlineSmall: TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
bodyLarge: TextStyle(
|
bodyLarge: TextStyle(
|
||||||
fontSize: 17.0,
|
fontSize: 17.0,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
|
@ -162,6 +165,34 @@ class SettingState extends ChangeNotifier {
|
||||||
fontSize: 15.0,
|
fontSize: 15.0,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.normal),
|
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(
|
tabBarTheme: TabBarTheme(
|
||||||
labelColor: Colors.black,
|
labelColor: Colors.black,
|
||||||
|
@ -177,15 +208,18 @@ class SettingState extends ChangeNotifier {
|
||||||
hoverColor: _accentSetColor!.withAlpha(70),
|
hoverColor: _accentSetColor!.withAlpha(70),
|
||||||
splashColor: _accentSetColor!.withAlpha(70),
|
splashColor: _accentSetColor!.withAlpha(70),
|
||||||
),
|
),
|
||||||
|
useMaterial3: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
ThemeData get darkTheme => ThemeData.dark().copyWith(
|
ThemeData get darkTheme => ThemeData.dark().copyWith(
|
||||||
colorScheme: ThemeData.dark()
|
colorScheme: _colors(Brightness.dark, _accentSetColor!),
|
||||||
.colorScheme
|
brightness: Brightness.dark,
|
||||||
.copyWith(brightness: Brightness.dark, secondary: _accentSetColor),
|
|
||||||
brightness: Brightness.light,
|
|
||||||
primaryColorDark: Colors.grey[800],
|
primaryColorDark: Colors.grey[800],
|
||||||
textTheme: TextTheme(
|
textTheme: TextTheme(
|
||||||
|
headlineSmall: TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
bodyLarge: TextStyle(
|
bodyLarge: TextStyle(
|
||||||
fontSize: 17.0,
|
fontSize: 17.0,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
@ -194,9 +228,31 @@ class SettingState extends ChangeNotifier {
|
||||||
fontSize: 15.0,
|
fontSize: 15.0,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.normal),
|
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),
|
primaryColor: _realDark! ? Colors.black : Color(0XFF1B1B1B),
|
||||||
popupMenuTheme: PopupMenuThemeData()
|
popupMenuTheme: PopupMenuThemeData()
|
||||||
.copyWith(color: _realDark! ? Colors.grey[900] : null),
|
.copyWith(color: _realDark! ? Colors.grey[900] : null),
|
||||||
|
@ -206,6 +262,7 @@ class SettingState extends ChangeNotifier {
|
||||||
systemOverlayStyle: SystemUiOverlayStyle.light),
|
systemOverlayStyle: SystemUiOverlayStyle.light),
|
||||||
buttonTheme: ButtonThemeData(height: 32),
|
buttonTheme: ButtonThemeData(height: 32),
|
||||||
dialogBackgroundColor: _realDark! ? Colors.grey[900] : null,
|
dialogBackgroundColor: _realDark! ? Colors.grey[900] : null,
|
||||||
|
useMaterial3: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
set setTheme(ThemeMode? mode) {
|
set setTheme(ThemeMode? mode) {
|
||||||
|
@ -214,6 +271,13 @@ class SettingState extends ChangeNotifier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColorScheme _colors(Brightness brightness, Color targetColor) {
|
||||||
|
return ColorScheme.fromSeed(
|
||||||
|
seedColor: targetColor,
|
||||||
|
brightness: brightness,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void setWorkManager(int? hour) {
|
void setWorkManager(int? hour) {
|
||||||
_updateInterval = hour;
|
_updateInterval = hour;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -366,9 +430,9 @@ class SettingState extends ChangeNotifier {
|
||||||
_saveRewindSeconds();
|
_saveRewindSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
int? _showNotesFontIndex;
|
late int _showNotesFontIndex;
|
||||||
int? get showNotesFontIndex => _showNotesFontIndex;
|
int get showNotesFontIndex => _showNotesFontIndex;
|
||||||
TextStyle get showNoteFontStyle => showNotesFontStyles[_showNotesFontIndex!];
|
TextStyle get showNoteFontStyle => showNotesFontStyles[_showNotesFontIndex];
|
||||||
set setShowNoteFontStyle(int index) {
|
set setShowNoteFontStyle(int index) {
|
||||||
_showNotesFontIndex = index;
|
_showNotesFontIndex = index;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
|
@ -3,39 +3,49 @@ import 'dart:developer' as developer;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:material_color_utilities/material_color_utilities.dart';
|
||||||
import 'package:intl/intl.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';
|
import '../generated/l10n.dart';
|
||||||
|
|
||||||
extension ContextExtension on BuildContext {
|
extension ContextExtension on BuildContext {
|
||||||
Color get primaryColor => Theme.of(this).primaryColor;
|
ColorScheme get colorScheme => Theme.of(this).colorScheme;
|
||||||
Color get accentColor => Theme.of(this).colorScheme.secondary;
|
Color get accentColor => Theme.of(this).colorScheme.primary;
|
||||||
Color get scaffoldBackgroundColor => Theme.of(this).scaffoldBackgroundColor;
|
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 primaryColorDark => Theme.of(this).primaryColorDark;
|
||||||
Color get textColor => textTheme.bodyLarge!.color!;
|
Color get textColor => textTheme.bodyLarge!.color!;
|
||||||
Color get dialogBackgroundColor => Theme.of(this).dialogBackgroundColor;
|
Color get dialogBackgroundColor => Theme.of(this).dialogBackgroundColor;
|
||||||
Brightness get brightness => Theme.of(this).brightness;
|
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 width => MediaQuery.of(this).size.width;
|
||||||
double get height => MediaQuery.of(this).size.height;
|
double get height => MediaQuery.of(this).size.height;
|
||||||
double get paddingTop => MediaQuery.of(this).padding.top;
|
double get paddingTop => MediaQuery.of(this).padding.top;
|
||||||
TextTheme get textTheme => Theme.of(this).textTheme;
|
TextTheme get textTheme => Theme.of(this).textTheme;
|
||||||
S? get s => S.of(this);
|
S get s => S.of(this)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
extension IntExtension on int {
|
extension IntExtension on int {
|
||||||
String toDate(BuildContext context) {
|
String toDate(BuildContext context) {
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
var date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
|
final date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
|
||||||
var difference = DateTime.now().toUtc().difference(date);
|
final difference = DateTime.now().toUtc().difference(date);
|
||||||
if (difference.inMinutes < 30) {
|
if (difference.inMinutes < 30) {
|
||||||
return s!.minsAgo(difference.inMinutes);
|
return s.minsAgo(difference.inMinutes);
|
||||||
} else if (difference.inMinutes < 60) {
|
} else if (difference.inMinutes < 60) {
|
||||||
return s!.hoursAgo(0);
|
return s.hoursAgo(0);
|
||||||
} else if (difference.inHours < 24) {
|
} else if (difference.inHours < 24) {
|
||||||
return s!.hoursAgo(difference.inHours);
|
return s.hoursAgo(difference.inHours);
|
||||||
} else if (difference.inDays < 7) {
|
} else if (difference.inDays < 7) {
|
||||||
return s!.daysAgo(difference.inDays);
|
return s.daysAgo(difference.inDays);
|
||||||
} else {
|
} else {
|
||||||
return DateFormat.yMMMd().format(
|
return DateFormat.yMMMd().format(
|
||||||
DateTime.fromMillisecondsSinceEpoch(this, isUtc: true).toLocal());
|
DateTime.fromMillisecondsSinceEpoch(this, isUtc: true).toLocal());
|
||||||
|
@ -50,21 +60,21 @@ extension IntExtension on int {
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
var interval = Duration(milliseconds: this);
|
var interval = Duration(milliseconds: this);
|
||||||
if (interval.inHours <= 48) {
|
if (interval.inHours <= 48) {
|
||||||
return s!.publishedDaily;
|
return s.publishedDaily;
|
||||||
} else if (interval.inDays > 2 && interval.inDays <= 14) {
|
} else if (interval.inDays > 2 && interval.inDays <= 14) {
|
||||||
return s!.publishedWeekly;
|
return s.publishedWeekly;
|
||||||
} else if (interval.inDays > 14 && interval.inDays < 60) {
|
} else if (interval.inDays > 14 && interval.inDays < 60) {
|
||||||
return s!.publishedMonthly;
|
return s.publishedMonthly;
|
||||||
} else {
|
} else {
|
||||||
return s!.publishedYearly;
|
return s.publishedYearly;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StringExtension on String {
|
extension StringExtension on String {
|
||||||
Future get launchUrl async {
|
Future get launchUrl async {
|
||||||
if (await canLaunch(this)) {
|
if (await canLaunchUrlString(this)) {
|
||||||
await launch(this);
|
await launchUrlString(this);
|
||||||
} else {
|
} else {
|
||||||
developer.log('Could not launch $this');
|
developer.log('Could not launch $this');
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:ui' as ui;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
|
Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
|
||||||
final ImageStream stream = imageProvider.resolve(
|
final ImageStream stream = imageProvider.resolve(
|
||||||
|
@ -17,3 +18,9 @@ Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
|
||||||
final image = await imageCompleter.future;
|
final image = await imageCompleter.future;
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String formateDate(int timeStamp) {
|
||||||
|
return DateFormat.yMMMd().format(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(timeStamp),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -94,8 +94,8 @@ class AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: backToMini,
|
onTap: backToMini,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: context.scaffoldBackgroundColor.withOpacity(0.9 *
|
color: context.background.withOpacity(
|
||||||
math.min(_animation.value / widget.maxHeight, 1)),
|
0.9 * math.min(_animation.value / widget.maxHeight, 1)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -314,7 +314,7 @@ class __AudioPanelRouteState extends State<_AudioPanelRoute> {
|
||||||
// child:
|
// child:
|
||||||
// Container(
|
// Container(
|
||||||
// color: Theme.of(context)
|
// color: Theme.of(context)
|
||||||
// .scaffoldBackgroundColor
|
// .background
|
||||||
// .withOpacity(0.8),
|
// .withOpacity(0.8),
|
||||||
//
|
//
|
||||||
//),
|
//),
|
||||||
|
|
|
@ -651,8 +651,7 @@ class _LineLoaderState extends State<LineLoader>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CustomPaint(
|
return CustomPaint(painter: LinePainter(_fraction, context.accentColor));
|
||||||
painter: LinePainter(_fraction, context.accentColor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,8 +1114,7 @@ class LayoutButton extends StatelessWidget {
|
||||||
height: 10,
|
height: 10,
|
||||||
width: 30,
|
width: 30,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter:
|
painter: LayoutPainter(4, context.textColor),
|
||||||
LayoutPainter(4, context.textColor),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1265,7 +1263,7 @@ class CustomBackButton extends StatelessWidget {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
splashRadius: 20,
|
splashRadius: 20,
|
||||||
icon: const BackButtonIcon(),
|
icon: const BackButtonIcon(),
|
||||||
tooltip: context.s!.back,
|
tooltip: context.s.back,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.maybePop(context);
|
Navigator.maybePop(context);
|
||||||
},
|
},
|
||||||
|
|
|
@ -132,7 +132,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
if (dataConfirm) {
|
if (dataConfirm) {
|
||||||
context.read<DownloadState>().startTask(episode!);
|
context.read<DownloadState>().startTask(episode!);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: context.s!.downloadStart,
|
msg: context.s.downloadStart,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
|
|
||||||
Future<bool> _useDataConfirm(BuildContext context) async {
|
Future<bool> _useDataConfirm(BuildContext context) async {
|
||||||
var ifUseData = false;
|
var ifUseData = false;
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
await generalDialog(
|
await generalDialog(
|
||||||
context,
|
context,
|
||||||
title: Text(s.cellularConfirm),
|
title: Text(s.cellularConfirm),
|
||||||
|
@ -205,7 +205,10 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
|
|
||||||
/// Circel avatar widget.
|
/// Circel avatar widget.
|
||||||
Widget _circleImage(BuildContext context,
|
Widget _circleImage(BuildContext context,
|
||||||
{EpisodeBrief? episode, Color? color, required bool boo, double? radius}) =>
|
{EpisodeBrief? episode,
|
||||||
|
Color? color,
|
||||||
|
required bool boo,
|
||||||
|
double? radius}) =>
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (openPodcast) {
|
if (openPodcast) {
|
||||||
|
@ -287,7 +290,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: Center();
|
: Center();
|
||||||
|
|
||||||
/// Pubdate widget
|
/// Pubdate widget
|
||||||
Widget _pubDate(BuildContext context, {required EpisodeBrief episode, Color? color}) =>
|
Widget _pubDate(BuildContext context,
|
||||||
|
{required EpisodeBrief episode, Color? color}) =>
|
||||||
Text(
|
Text(
|
||||||
episode.pubDate!.toDate(context),
|
episode.pubDate!.toDate(context),
|
||||||
overflow: TextOverflow.visible,
|
overflow: TextOverflow.visible,
|
||||||
|
@ -299,7 +303,11 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
fontStyle: FontStyle.italic),
|
fontStyle: FontStyle.italic),
|
||||||
);
|
);
|
||||||
Widget _episodeCard(BuildContext context,
|
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;
|
var width = context.width;
|
||||||
if (layout == Layout.one) {
|
if (layout == Layout.one) {
|
||||||
return _layoutOneCard(context,
|
return _layoutOneCard(context,
|
||||||
|
@ -323,7 +331,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
layout != Layout.one
|
layout != Layout.one
|
||||||
? _circleImage(context,
|
? _circleImage(context,
|
||||||
episode: episodes![index!], color: color, boo: boo!)
|
episode: episodes![index!], color: color, boo: boo!)
|
||||||
: _pubDate(context, episode: episodes![index!], color: color),
|
: _pubDate(context,
|
||||||
|
episode: episodes![index!], color: color),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
_isNewIndicator(episodes![index]),
|
_isNewIndicator(episodes![index]),
|
||||||
_downloadIndicater(context,
|
_downloadIndicater(context,
|
||||||
|
@ -403,7 +412,11 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _layoutOneCard(BuildContext context,
|
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;
|
var width = context.width;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
|
@ -443,7 +456,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
),
|
),
|
||||||
_isNewIndicator(episodes![index]),
|
_isNewIndicator(episodes![index]),
|
||||||
_downloadIndicater(context,
|
_downloadIndicater(context,
|
||||||
episode: episodes![index], isDownloaded: isDownloaded),
|
episode: episodes![index],
|
||||||
|
isDownloaded: isDownloaded),
|
||||||
_numberIndicater(context, index: index, color: color)
|
_numberIndicater(context, index: index, color: color)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -574,7 +588,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
? context.brightness == Brightness.light
|
? context.brightness == Brightness.light
|
||||||
? Colors.grey[200]
|
? Colors.grey[200]
|
||||||
: Color.fromRGBO(50, 50, 50, 1)
|
: Color.fromRGBO(50, 50, 50, 1)
|
||||||
: context.scaffoldBackgroundColor,
|
: context.background,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: context.brightness == Brightness.light
|
color: context.brightness == Brightness.light
|
||||||
|
@ -590,7 +604,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!selectedList!.contains(episodes![index])) {
|
if (!selectedList!
|
||||||
|
.contains(episodes![index])) {
|
||||||
_selectedList = selectedList;
|
_selectedList = selectedList;
|
||||||
_selectedList!.add(episodes![index]);
|
_selectedList!.add(episodes![index]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -609,7 +624,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: context.brightness ==
|
: context.brightness ==
|
||||||
Brightness.light
|
Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context.background,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -628,7 +643,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: context.brightness == Brightness.light
|
color: context.brightness == Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context.background,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -687,8 +702,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (!data.item2.contains(
|
if (!data.item2.contains(
|
||||||
episodes![index].enclosureUrl)) {
|
episodes![index].enclosureUrl)) {
|
||||||
audio
|
audio.addToPlaylist(
|
||||||
.addToPlaylist(episodes![index]);
|
episodes![index]);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.toastAddPlaylist,
|
msg: s.toastAddPlaylist,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
@ -787,8 +802,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
color: context.textColor!
|
color: context.textColor!
|
||||||
.withOpacity(0.5)))
|
.withOpacity(0.5)))
|
||||||
: Text(s.download),
|
: Text(s.download),
|
||||||
trailingIcon: Icon(
|
trailingIcon: Icon(LineIcons.download,
|
||||||
LineIcons.download,
|
|
||||||
color: Colors.green),
|
color: Colors.green),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (!isDownloaded) {
|
if (!isDownloaded) {
|
||||||
|
@ -858,9 +872,9 @@ class OpenContainerWrapper extends StatelessWidget {
|
||||||
beginColor: Theme.of(context).primaryColor,
|
beginColor: Theme.of(context).primaryColor,
|
||||||
endColor: Theme.of(context).primaryColor,
|
endColor: Theme.of(context).primaryColor,
|
||||||
closedColor: Theme.of(context).brightness == Brightness.light
|
closedColor: Theme.of(context).brightness == Brightness.light
|
||||||
? Theme.of(context).primaryColor
|
? context.primaryColor
|
||||||
: Theme.of(context).scaffoldBackgroundColor,
|
: context.background,
|
||||||
openColor: Theme.of(context).scaffoldBackgroundColor,
|
openColor: context.background,
|
||||||
openElevation: 0,
|
openElevation: 0,
|
||||||
closedElevation: 0,
|
closedElevation: 0,
|
||||||
openShape:
|
openShape:
|
||||||
|
|
|
@ -22,7 +22,7 @@ Widget featureDiscoveryOverlay(BuildContext context,
|
||||||
required Widget tapTarget,
|
required Widget tapTarget,
|
||||||
required String title,
|
required String title,
|
||||||
required String description}) {
|
required String description}) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return DescribedFeatureOverlay(
|
return DescribedFeatureOverlay(
|
||||||
featureId: featureId,
|
featureId: featureId,
|
||||||
tapTarget: tapTarget,
|
tapTarget: tapTarget,
|
||||||
|
|
|
@ -140,7 +140,7 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
|
||||||
|
|
||||||
Future<bool> _useDataConfirm() async {
|
Future<bool> _useDataConfirm() async {
|
||||||
var ifUseData = false;
|
var ifUseData = false;
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
await generalDialog(
|
await generalDialog(
|
||||||
context,
|
context,
|
||||||
title: Text(s.cellularConfirm),
|
title: Text(s.cellularConfirm),
|
||||||
|
@ -575,7 +575,7 @@ class __NewPlaylistState extends State<_NewPlaylist> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s!;
|
final s = context.s;
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
|
|
@ -58,7 +58,7 @@ class _SettingsSheetState extends State<SettingsSheet>
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: context.scaffoldBackgroundColor.withOpacity(
|
color: context.background.withOpacity(
|
||||||
0.8 * math.min(_animation.value / widget.height, 1.0)),
|
0.8 * math.min(_animation.value / widget.height, 1.0)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -68,6 +68,8 @@ dependencies:
|
||||||
collection: ^1.15.0-nullsafety.4
|
collection: ^1.15.0-nullsafety.4
|
||||||
shared_preferences_android: ^2.0.12
|
shared_preferences_android: ^2.0.12
|
||||||
path_provider_android: ^2.0.14
|
path_provider_android: ^2.0.14
|
||||||
|
material_color_utilities: ^0.1.4
|
||||||
|
dynamic_color: ^1.4.0
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
linkify:
|
linkify:
|
||||||
|
|
Loading…
Reference in New Issue