feat: ui improvement

This commit is contained in:
Simon Ding
2024-08-08 10:56:03 +08:00
parent 64e98647a8
commit b34e39889c
6 changed files with 190 additions and 99 deletions

View File

@@ -161,15 +161,15 @@ class _SubmitSearchResultState extends ConsumerState<SubmitSearchResult> {
Navigator.of(context).pop();
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('确定'),
LoadingTextButton(
// style: TextButton.styleFrom(
// textStyle: Theme.of(context).textTheme.labelLarge,
// ),
label: const Text('确定'),
onPressed: () async {
if (_formKey.currentState!.saveAndValidate()) {
final values = _formKey.currentState!.value;
var f = ref
await ref
.read(searchPageDataProvider(widget.query).notifier)
.submit2Watchlist(
widget.item.id!,
@@ -185,7 +185,6 @@ class _SubmitSearchResultState extends ConsumerState<SubmitSearchResult> {
Navigator.of(context).pop();
showSnakeBar("添加成功:${widget.item.name}");
});
showLoadingWithFuture(f);
}
},
),

View File

@@ -22,12 +22,11 @@ Future<void> showSettingDialog(
),
actions: <Widget>[
showDelete
? TextButton(
onPressed: () {
final f = onDelete().then((v) => Navigator.of(context).pop());
showLoadingWithFuture(f);
? LoadingTextButton(
onPressed: () async {
await onDelete().then((v) => Navigator.of(context).pop());
},
child: const Text(
label: const Text(
'删除',
style: TextStyle(color: Colors.red),
))
@@ -35,11 +34,10 @@ Future<void> showSettingDialog(
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('取消')),
TextButton(
child: const Text('确定'),
onPressed: () {
final f = onSubmit().then((v) => Navigator.of(context).pop());
showLoadingWithFuture(f);
LoadingTextButton(
label: const Text('确定'),
onPressed: () async {
await onSubmit().then((v) => Navigator.of(context).pop());
},
),
],

View File

@@ -48,62 +48,55 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
Opacity(
opacity: 0.7,
child: ep.status == "downloading"
? const Tooltip(
message: "下载中",
child: IconButton(onPressed: null, icon: Icon(Icons.downloading)),
)
? const IconButton(
tooltip: "下载中",
onPressed: null,
icon: Icon(Icons.downloading))
: (ep.status == "downloaded"
? const Tooltip(
message: "已下载",
child: IconButton(onPressed: null, icon: Icon(Icons.download_done)),
)
? const IconButton(
tooltip: "已下载",
onPressed: null,
icon: Icon(Icons.download_done))
: (ep.monitored == true
? Tooltip(
message: "监控中",
? IconButton(
tooltip: "监控中",
onPressed: () {
ref
.read(mediaDetailsProvider(
widget.seriesId)
.notifier)
.changeMonitoringStatus(
ep.id!, false);
},
icon: const Icon(Icons.alarm))
: Opacity(
opacity: 0.7,
child: IconButton(
tooltip: "未监控",
onPressed: () {
ref
.read(mediaDetailsProvider(
widget.seriesId)
.notifier)
.changeMonitoringStatus(
ep.id!, false);
ep.id!, true);
},
icon: const Icon(Icons.alarm)),
)
: Opacity(
opacity: 0.7,
child: Tooltip(
message: "未监控",
child: IconButton(
onPressed: () {
ref
.read(mediaDetailsProvider(
widget.seriesId)
.notifier)
.changeMonitoringStatus(
ep.id!, true);
},
icon: const Icon(Icons.alarm_off)),
),
icon: const Icon(Icons.alarm_off)),
)))),
),
DataCell(Row(
children: [
Tooltip(
message: "搜索下载对应剧集",
child: IconButton(
onPressed: () {
var f = ref
.read(mediaDetailsProvider(widget.seriesId)
.notifier)
.searchAndDownload(widget.seriesId,
ep.seasonNumber!, ep.episodeNumber!)
.then((v) => showSnakeBar("开始下载: $v"));
showLoadingWithFuture(f);
},
icon: const Icon(Icons.download)),
),
LoadingIconButton(
tooltip: "搜索下载对应剧集",
onPressed: () async {
await ref
.read(
mediaDetailsProvider(widget.seriesId).notifier)
.searchAndDownload(widget.seriesId,
ep.seasonNumber!, ep.episodeNumber!)
.then((v) => showSnakeBar("开始下载: $v"));
},
icon: Icons.download),
const SizedBox(
width: 10,
),
@@ -142,19 +135,17 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
DataColumn(
label: Row(
children: [
Tooltip(
message: "搜索下载全部剧集",
child: IconButton(
onPressed: () {
final f = ref
.read(mediaDetailsProvider(widget.seriesId)
.notifier)
.searchAndDownload(widget.seriesId, k, 0)
.then((v) => showSnakeBar("开始下载: $v"));
showLoadingWithFuture(f);
},
icon: const Icon(Icons.download)),
),
LoadingIconButton(
tooltip: "搜索下载全部剧集",
onPressed: () async {
await ref
.read(mediaDetailsProvider(widget.seriesId)
.notifier)
.searchAndDownload(widget.seriesId, k, 0)
.then((v) => showSnakeBar("开始下载: $v"));
//showLoadingWithFuture(f);
},
icon: Icons.download),
const SizedBox(
width: 10,
),

View File

@@ -66,8 +66,7 @@ class _DetailCardState extends ConsumerState<DetailCard> {
const SizedBox(
width: 30,
),
Text(
"${widget.details.storage!.name}:"),
Text("${widget.details.storage!.name}:"),
Text(
"${widget.details.mediaType == "tv" ? widget.details.storage!.tvPath : widget.details.storage!.moviePath}"
"${widget.details.targetDir}"),
@@ -90,13 +89,15 @@ class _DetailCardState extends ConsumerState<DetailCard> {
const Text(""),
Expanded(
child: Text(
overflow: TextOverflow.ellipsis,
overflow: TextOverflow.visible,
maxLines: 9,
widget.details.overview ?? "",
)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
editIcon(widget.details),
downloadButton(),
editIcon(),
deleteIcon(),
],
)
@@ -113,11 +114,10 @@ class _DetailCardState extends ConsumerState<DetailCard> {
}
Widget deleteIcon() {
return Tooltip(
message: widget.details.mediaType == "tv" ? "删除剧集" : "删除电影",
child: IconButton(
onPressed: () => showConfirmDialog(), icon: const Icon(Icons.delete)),
);
return IconButton(
tooltip: widget.details.mediaType == "tv" ? "删除剧集" : "删除电影",
onPressed: () => showConfirmDialog(),
icon: const Icon(Icons.delete));
}
Future<void> showConfirmDialog() {
@@ -150,19 +150,21 @@ class _DetailCardState extends ConsumerState<DetailCard> {
);
}
Widget editIcon(SeriesDetails details) {
Widget editIcon() {
return IconButton(
onPressed: () => showEditDialog(details), icon: const Icon(Icons.edit));
tooltip: "编辑",
onPressed: () => showEditDialog(),
icon: const Icon(Icons.edit));
}
showEditDialog(SeriesDetails details) {
showEditDialog() {
final _formKey = GlobalKey<FormBuilderState>();
return showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
title: Text("编辑 ${details.name}"),
title: Text("编辑 ${widget.details.name}"),
content: SelectionArea(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
@@ -171,11 +173,16 @@ class _DetailCardState extends ConsumerState<DetailCard> {
child: FormBuilder(
key: _formKey,
initialValue: {
"resolution": details.resolution,
"target_dir": details.targetDir,
"limiter": details.limiter != null
? RangeValues(details.limiter!.sizeMin.toDouble()/1000/1000,
details.limiter!.sizeMax.toDouble()/1000/1000)
"resolution": widget.details.resolution,
"target_dir": widget.details.targetDir,
"limiter": widget.details.limiter != null
? RangeValues(
widget.details.limiter!.sizeMin.toDouble() /
1000 /
1000,
widget.details.limiter!.sizeMax.toDouble() /
1000 /
1000)
: const RangeValues(0, 0)
},
child: Column(
@@ -204,23 +211,29 @@ class _DetailCardState extends ConsumerState<DetailCard> {
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("取消")),
TextButton(
onPressed: () {
LoadingTextButton(
onPressed: () async {
if (_formKey.currentState!.saveAndValidate()) {
final values = _formKey.currentState!.value;
var f = ref
await ref
.read(mediaDetailsProvider(widget.details.id.toString())
.notifier)
.edit(values["resolution"], values["target_dir"],
values["limiter"])
.then((v) => Navigator.of(context).pop());
showLoadingWithFuture(f);
}
},
child: const Text("确认"))
label: const Text("确认"))
],
);
},
);
}
Widget downloadButton() {
return IconButton(
tooltip: widget.details.mediaType == "tv" ? "查找并下载所有监控剧集" : "查找并下载此电影",
onPressed: () {},
icon: const Icon(Icons.download_rounded));
}
}

View File

@@ -59,10 +59,10 @@ class ResourceList extends ConsumerWidget {
: "-")));
}
rows.add(DataCell(IconButton(
icon: const Icon(Icons.download),
rows.add(DataCell(LoadingIconButton(
icon: Icons.download,
onPressed: () async {
var f = ref
await ref
.read(mediaTorrentsDataProvider((
mediaId: mediaId,
seasonNumber: seasonNum,
@@ -70,7 +70,6 @@ class ResourceList extends ConsumerWidget {
)).notifier)
.download(torrent)
.then((v) => showSnakeBar("开始下载:${torrent.name}"));
showLoadingWithFuture(f);
},
)));
return DataRow(cells: rows);

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:ui/providers/APIs.dart';
import 'package:ui/widgets/utils.dart';
class Commons {
static InputDecoration requiredTextFieldStyle({
@@ -139,3 +140,93 @@ class _MySliderState extends State<MyRangeSlider> {
return "$v MB";
}
}
class LoadingIconButton extends StatefulWidget {
LoadingIconButton({required this.onPressed, required this.icon, this.tooltip});
final Future<void> Function() onPressed;
final IconData icon;
final String? tooltip;
@override
State<StatefulWidget> createState() {
return _LoadingIconButtonState();
}
}
class _LoadingIconButtonState extends State<LoadingIconButton> {
bool loading = false;
@override
Widget build(BuildContext context) {
return IconButton(
tooltip: widget.tooltip,
onPressed: loading
? null
: () async {
setState(() => loading = true);
try {
await widget.onPressed();
} catch (e) {
showSnakeBar("操作失败:$e");
} finally {
setState(() => loading = false);
}
},
icon: loading
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: Colors.grey,
strokeWidth: 3,
),
)
: Icon(widget.icon));
}
}
class LoadingTextButton extends StatefulWidget {
LoadingTextButton({required this.onPressed, required this.label});
final Future<void> Function() onPressed;
final Widget label;
@override
State<StatefulWidget> createState() {
return _LoadingTextButtonState();
}
}
class _LoadingTextButtonState extends State<LoadingTextButton> {
bool loading = false;
@override
Widget build(BuildContext context) {
return TextButton.icon(
onPressed: loading
? null
: () async {
setState(() => loading = true);
try {
await widget.onPressed();
} catch (e) {
showSnakeBar("操作失败:$e");
} finally {
setState(() => loading = false);
}
},
icon: loading
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: Colors.grey,
strokeWidth: 3,
),
)
: Text(""),
label: widget.label,
);
}
}