mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-09 19:47:47 +08:00
feat: better form
This commit is contained in:
@@ -88,7 +88,7 @@ class _ActivityPageState extends ConsumerState<ActivityPage>
|
|||||||
ref
|
ref
|
||||||
.read(activitiesDataProvider("active").notifier)
|
.read(activitiesDataProvider("active").notifier)
|
||||||
.deleteActivity(id)
|
.deleteActivity(id)
|
||||||
.whenComplete(() => Utils.showSnakeBar("删除成功"))
|
.then((v) => Utils.showSnakeBar("删除成功"))
|
||||||
.onError((error, trace) => Utils.showSnakeBar("删除失败:$error"));
|
.onError((error, trace) => Utils.showSnakeBar("删除失败:$error"));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class _MovieDetailsPageState extends ConsumerState<MovieDetailsPage> {
|
|||||||
widget.id)
|
widget.id)
|
||||||
.notifier)
|
.notifier)
|
||||||
.delete()
|
.delete()
|
||||||
.whenComplete(() => context
|
.then((v) => context
|
||||||
.go(WelcomePage.routeMoivie))
|
.go(WelcomePage.routeMoivie))
|
||||||
.onError((error, trace) =>
|
.onError((error, trace) =>
|
||||||
Utils.showSnakeBar(
|
Utils.showSnakeBar(
|
||||||
@@ -253,7 +253,7 @@ class _NestedTabBarState extends ConsumerState<NestedTabBar>
|
|||||||
.read(movieTorrentsDataProvider(widget.id)
|
.read(movieTorrentsDataProvider(widget.id)
|
||||||
.notifier)
|
.notifier)
|
||||||
.download(torrent)
|
.download(torrent)
|
||||||
.whenComplete(() =>
|
.then((v) =>
|
||||||
Utils.showSnakeBar("开始下载:${torrent.name}"))
|
Utils.showSnakeBar("开始下载:${torrent.name}"))
|
||||||
.onError((error, trace) =>
|
.onError((error, trace) =>
|
||||||
Utils.showSnakeBar("操作失败: $error"));
|
Utils.showSnakeBar("操作失败: $error"));
|
||||||
|
|||||||
@@ -41,10 +41,11 @@ class AuthSettingData extends AutoDisposeAsyncNotifier<AuthSetting> {
|
|||||||
class AuthSetting {
|
class AuthSetting {
|
||||||
bool enable;
|
bool enable;
|
||||||
String user;
|
String user;
|
||||||
|
String password;
|
||||||
|
|
||||||
AuthSetting({required this.enable, required this.user});
|
AuthSetting({required this.enable, required this.user, required this.password});
|
||||||
|
|
||||||
factory AuthSetting.fromJson(Map<String, dynamic> json) {
|
factory AuthSetting.fromJson(Map<String, dynamic> json) {
|
||||||
return AuthSetting(enable: json["enable"], user: json["user"]);
|
return AuthSetting(enable: json["enable"], user: json["user"], password: json["password"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ class _SearchPageState extends ConsumerState<SearchPage> {
|
|||||||
.notifier)
|
.notifier)
|
||||||
.submit2Watchlist(item.id!, storageSelected,
|
.submit2Watchlist(item.id!, storageSelected,
|
||||||
resSelected, item.mediaType!, pathController.text)
|
resSelected, item.mediaType!, pathController.text)
|
||||||
.whenComplete(() {
|
.then((v) {
|
||||||
Utils.showSnakeBar("添加成功");
|
Utils.showSnakeBar("添加成功");
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}).onError((error, trace) {
|
}).onError((error, trace) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:quiver/strings.dart';
|
import 'package:quiver/strings.dart';
|
||||||
import 'package:ui/providers/login.dart';
|
import 'package:ui/providers/login.dart';
|
||||||
@@ -6,6 +7,7 @@ import 'package:ui/providers/settings.dart';
|
|||||||
import 'package:ui/utils.dart';
|
import 'package:ui/utils.dart';
|
||||||
import 'package:ui/widgets/progress_indicator.dart';
|
import 'package:ui/widgets/progress_indicator.dart';
|
||||||
import 'package:ui/widgets/widgets.dart';
|
import 'package:ui/widgets/widgets.dart';
|
||||||
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
|
|
||||||
class SystemSettingsPage extends ConsumerStatefulWidget {
|
class SystemSettingsPage extends ConsumerStatefulWidget {
|
||||||
static const route = "/settings";
|
static const route = "/settings";
|
||||||
@@ -18,10 +20,8 @@ class SystemSettingsPage extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||||
final GlobalKey _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
final _formKey2 = GlobalKey<FormBuilderState>();
|
||||||
final _tmdbApiController = TextEditingController();
|
|
||||||
final _downloadDirController = TextEditingController();
|
|
||||||
bool? _enableAuth;
|
bool? _enableAuth;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -30,62 +30,56 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
|
|
||||||
var tmdbSetting = settings.when(
|
var tmdbSetting = settings.when(
|
||||||
data: (v) {
|
data: (v) {
|
||||||
_tmdbApiController.text = v.tmdbApiKey!;
|
|
||||||
_downloadDirController.text = v.downloadDIr!;
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
|
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
|
||||||
child: Form(
|
child: FormBuilder(
|
||||||
key: _formKey, //设置globalKey,用于后面获取FormState
|
key: _formKey, //设置globalKey,用于后面获取FormState
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
initialValue: {
|
||||||
|
"tmdb_api": v.tmdbApiKey,
|
||||||
|
"download_dir": v.downloadDIr
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
TextFormField(
|
FormBuilderTextField(
|
||||||
|
name: "tmdb_api",
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: _tmdbApiController,
|
|
||||||
decoration: Commons.requiredTextFieldStyle(
|
decoration: Commons.requiredTextFieldStyle(
|
||||||
text: "TMDB Api Key", icon: const Icon(Icons.key)),
|
text: "TMDB Api Key", icon: const Icon(Icons.key)),
|
||||||
//
|
//
|
||||||
validator: (v) {
|
validator: FormBuilderValidators.required(),
|
||||||
return v!.trim().isNotEmpty ? null : "ApiKey 不能为空";
|
|
||||||
},
|
|
||||||
onSaved: (newValue) {},
|
|
||||||
),
|
),
|
||||||
TextFormField(
|
FormBuilderTextField(
|
||||||
|
name: "download_dir",
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: _downloadDirController,
|
|
||||||
decoration: Commons.requiredTextFieldStyle(
|
decoration: Commons.requiredTextFieldStyle(
|
||||||
text: "下载路径", icon: const Icon(Icons.folder)),
|
text: "下载路径", icon: const Icon(Icons.folder)),
|
||||||
//
|
//
|
||||||
validator: (v) {
|
validator: FormBuilderValidators.required(),
|
||||||
return v!.trim().isNotEmpty ? null : "下载路径不能为空";
|
|
||||||
},
|
|
||||||
onSaved: (newValue) {},
|
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 28.0),
|
padding: const EdgeInsets.only(top: 28.0),
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Text("保存"),
|
child: Text("保存"),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if ((_formKey.currentState as FormState)
|
if (_formKey.currentState!.saveAndValidate()) {
|
||||||
.validate()) {
|
var values = _formKey.currentState!.value;
|
||||||
var f = ref
|
var f = ref
|
||||||
.read(settingProvider.notifier)
|
.read(settingProvider.notifier)
|
||||||
.updateSettings(GeneralSetting(
|
.updateSettings(GeneralSetting(
|
||||||
tmdbApiKey: _tmdbApiController.text,
|
tmdbApiKey: values["tmdb_api"],
|
||||||
downloadDIr:
|
downloadDIr: values["download_dir"]));
|
||||||
_downloadDirController.text));
|
f.then((v) {
|
||||||
f.whenComplete(() {
|
Utils.showSnakeBar("更新成功");
|
||||||
Utils.showSnakeBar("更新成功");
|
}).onError((e, s) {
|
||||||
}).onError((e, s) {
|
Utils.showSnakeBar("更新失败:$e");
|
||||||
Utils.showSnakeBar("更新失败:$e");
|
});
|
||||||
});
|
}
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -103,7 +97,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
var indexer = value[i];
|
var indexer = value[i];
|
||||||
return SettingsCard(
|
return SettingsCard(
|
||||||
onTap: () => showIndexerDetails(indexer),
|
onTap: () => showIndexerDetails(indexer),
|
||||||
child: Text(indexer.name??""));
|
child: Text(indexer.name ?? ""));
|
||||||
}
|
}
|
||||||
return SettingsCard(
|
return SettingsCard(
|
||||||
onTap: () => showIndexerDetails(Indexer()),
|
onTap: () => showIndexerDetails(Indexer()),
|
||||||
@@ -121,7 +115,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
var client = value[i];
|
var client = value[i];
|
||||||
return SettingsCard(
|
return SettingsCard(
|
||||||
onTap: () => showDownloadClientDetails(client),
|
onTap: () => showDownloadClientDetails(client),
|
||||||
child: Text(client.name??""));
|
child: Text(client.name ?? ""));
|
||||||
}
|
}
|
||||||
return SettingsCard(
|
return SettingsCard(
|
||||||
onTap: () => showDownloadClientDetails(DownloadClient()),
|
onTap: () => showDownloadClientDetails(DownloadClient()),
|
||||||
@@ -138,7 +132,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
var storage = value[i];
|
var storage = value[i];
|
||||||
return SettingsCard(
|
return SettingsCard(
|
||||||
onTap: () => showStorageDetails(storage),
|
onTap: () => showStorageDetails(storage),
|
||||||
child: Text(storage.name??""));
|
child: Text(storage.name ?? ""));
|
||||||
}
|
}
|
||||||
return SettingsCard(
|
return SettingsCard(
|
||||||
onTap: () => showStorageDetails(Storage()),
|
onTap: () => showStorageDetails(Storage()),
|
||||||
@@ -159,53 +153,69 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
userController.text = data.user;
|
userController.text = data.user;
|
||||||
return Column(
|
return FormBuilder(
|
||||||
children: [
|
key: _formKey2,
|
||||||
SwitchListTile(
|
initialValue: {
|
||||||
title: const Text("开启认证"),
|
"user": data.user,
|
||||||
value: _enableAuth!,
|
"password": data.password,
|
||||||
onChanged: (v) {
|
"enable": data.enable
|
||||||
setState(() {
|
},
|
||||||
_enableAuth = v;
|
child: Column(
|
||||||
});
|
children: [
|
||||||
}),
|
FormBuilderSwitch(
|
||||||
_enableAuth!
|
name: "enable",
|
||||||
? Column(
|
title: const Text("开启认证"),
|
||||||
children: [
|
onChanged: (v) {
|
||||||
TextFormField(
|
setState(() {
|
||||||
controller: userController,
|
_enableAuth = v;
|
||||||
decoration: Commons.requiredTextFieldStyle(
|
|
||||||
text: "用户名",
|
|
||||||
icon: const Icon(Icons.account_box),
|
|
||||||
)),
|
|
||||||
TextFormField(
|
|
||||||
obscureText: true,
|
|
||||||
enableSuggestions: false,
|
|
||||||
autocorrect: false,
|
|
||||||
controller: passController,
|
|
||||||
decoration: Commons.requiredTextFieldStyle(
|
|
||||||
text: "密码",
|
|
||||||
icon: const Icon(Icons.password),
|
|
||||||
))
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: const Column(),
|
|
||||||
Center(
|
|
||||||
child: ElevatedButton(
|
|
||||||
child: const Text("保存"),
|
|
||||||
onPressed: () {
|
|
||||||
var f = ref
|
|
||||||
.read(authSettingProvider.notifier)
|
|
||||||
.updateAuthSetting(_enableAuth!,
|
|
||||||
userController.text, passController.text);
|
|
||||||
f.whenComplete(() {
|
|
||||||
Utils.showSnakeBar("更新成功");
|
|
||||||
}).onError((e, s) {
|
|
||||||
Utils.showSnakeBar("更新失败:$e");
|
|
||||||
});
|
});
|
||||||
}))
|
}),
|
||||||
],
|
_enableAuth!
|
||||||
);
|
? Column(
|
||||||
|
children: [
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "user",
|
||||||
|
autovalidateMode:
|
||||||
|
AutovalidateMode.onUserInteraction,
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
decoration: Commons.requiredTextFieldStyle(
|
||||||
|
text: "用户名",
|
||||||
|
icon: const Icon(Icons.account_box),
|
||||||
|
)),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "password",
|
||||||
|
obscureText: true,
|
||||||
|
enableSuggestions: false,
|
||||||
|
autocorrect: false,
|
||||||
|
autovalidateMode:
|
||||||
|
AutovalidateMode.onUserInteraction,
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
decoration: Commons.requiredTextFieldStyle(
|
||||||
|
text: "密码",
|
||||||
|
icon: const Icon(Icons.password),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const Column(),
|
||||||
|
Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: const Text("保存"),
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey2.currentState!.saveAndValidate()) {
|
||||||
|
var values = _formKey2.currentState!.value;
|
||||||
|
var f = ref
|
||||||
|
.read(authSettingProvider.notifier)
|
||||||
|
.updateAuthSetting(_enableAuth!,
|
||||||
|
values["user"], values["password"]);
|
||||||
|
f.then((v) {
|
||||||
|
Utils.showSnakeBar("更新成功");
|
||||||
|
}).onError((e, s) {
|
||||||
|
Utils.showSnakeBar("更新失败:$e");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
));
|
||||||
},
|
},
|
||||||
error: (err, trace) => Text("$err"),
|
error: (err, trace) => Text("$err"),
|
||||||
loading: () => const MyProgressIndicator());
|
loading: () => const MyProgressIndicator());
|
||||||
@@ -256,109 +266,155 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showIndexerDetails(Indexer indexer) {
|
Future<void> showIndexerDetails(Indexer indexer) {
|
||||||
var nameController = TextEditingController(text: indexer.name);
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
var urlController = TextEditingController(text: indexer.url);
|
|
||||||
var apiKeyController = TextEditingController(text: indexer.apiKey);
|
|
||||||
var selectImpl = "torznab";
|
var selectImpl = "torznab";
|
||||||
final children = <Widget>[
|
var body = FormBuilder(
|
||||||
DropdownMenu(
|
key: _formKey,
|
||||||
label: const Text("类型"),
|
initialValue: {
|
||||||
onSelected: (value) {
|
"name": indexer.name,
|
||||||
setState(() {
|
"url": indexer.url,
|
||||||
selectImpl = value!;
|
"api_key": indexer.apiKey,
|
||||||
});
|
"impl": "torznab"
|
||||||
},
|
},
|
||||||
initialSelection: selectImpl,
|
child: Column(
|
||||||
dropdownMenuEntries: const [
|
children: [
|
||||||
DropdownMenuEntry(value: "torznab", label: "Torznab"),
|
FormBuilderDropdown(
|
||||||
|
name: "impl",
|
||||||
|
decoration: const InputDecoration(labelText: "类型"),
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(value: "torznab", child: Text("Torznab")),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "name",
|
||||||
|
decoration: Commons.requiredTextFieldStyle(text: "名称"),
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "url",
|
||||||
|
decoration: Commons.requiredTextFieldStyle(text: "地址"),
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
validator: FormBuilderValidators.compose([
|
||||||
|
FormBuilderValidators.required(),
|
||||||
|
FormBuilderValidators.url()
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "api_key",
|
||||||
|
decoration: Commons.requiredTextFieldStyle(text: "API Key"),
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextField(
|
);
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "名称"),
|
|
||||||
controller: nameController,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "地址"),
|
|
||||||
controller: urlController,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "API Key"),
|
|
||||||
controller: apiKeyController,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
onDelete() async {
|
onDelete() async {
|
||||||
return ref.read(indexersProvider.notifier).deleteIndexer(indexer.id!);
|
return ref.read(indexersProvider.notifier).deleteIndexer(indexer.id!);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() async {
|
onSubmit() async {
|
||||||
return ref.read(indexersProvider.notifier).addIndexer(Indexer(
|
if (_formKey.currentState!.saveAndValidate()) {
|
||||||
name: nameController.text,
|
var values = _formKey.currentState!.value;
|
||||||
url: urlController.text,
|
return ref.read(indexersProvider.notifier).addIndexer(Indexer(
|
||||||
apiKey: apiKeyController.text));
|
name: values["name"],
|
||||||
|
url: values["url"],
|
||||||
|
apiKey: values["api_key"]));
|
||||||
|
} else {
|
||||||
|
throw "数据校验失败";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return showSettingDialog(
|
return showSettingDialog(
|
||||||
"索引器", indexer.id != null, children, onSubmit, onDelete);
|
"索引器", indexer.id != null, body, onSubmit, onDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showDownloadClientDetails(DownloadClient client) {
|
Future<void> showDownloadClientDetails(DownloadClient client) {
|
||||||
var nameController = TextEditingController(text: client.name);
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
var urlController = TextEditingController(text: client.url);
|
|
||||||
var userController = TextEditingController(text: client.user);
|
|
||||||
var passController = TextEditingController(text: client.password);
|
|
||||||
|
|
||||||
var _enableAuth = isNotBlank(client.user);
|
var _enableAuth = isNotBlank(client.user);
|
||||||
String selectImpl = "transmission";
|
String selectImpl = "transmission";
|
||||||
var body = <Widget>[
|
|
||||||
DropdownMenu(
|
final body =
|
||||||
label: const Text("类型"),
|
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
||||||
onSelected: (value) {
|
return FormBuilder(
|
||||||
setState(() {
|
key: _formKey,
|
||||||
selectImpl = value!;
|
initialValue: {
|
||||||
});
|
"name": client.name,
|
||||||
},
|
"url": client.url,
|
||||||
initialSelection: selectImpl,
|
"user": client.user,
|
||||||
dropdownMenuEntries: const [
|
"password": client.password,
|
||||||
DropdownMenuEntry(value: "transmission", label: "Transmission"),
|
"impl": "transmission"
|
||||||
],
|
},
|
||||||
),
|
child: Column(
|
||||||
TextField(
|
children: [
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "名称"),
|
FormBuilderDropdown<String>(
|
||||||
controller: nameController,
|
name: "impl",
|
||||||
),
|
decoration: const InputDecoration(labelText: "类型"),
|
||||||
TextField(
|
onChanged: (value) {
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "地址"),
|
|
||||||
controller: urlController,
|
|
||||||
),
|
|
||||||
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
SwitchListTile(
|
|
||||||
title: const Text("需要认证"),
|
|
||||||
value: _enableAuth,
|
|
||||||
onChanged: (v) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_enableAuth = v;
|
selectImpl = value!;
|
||||||
});
|
});
|
||||||
}),
|
},
|
||||||
_enableAuth
|
items: const [
|
||||||
? Column(
|
DropdownMenuItem(
|
||||||
children: [
|
value: "transmission", child: Text("Transmission")),
|
||||||
TextField(
|
],
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "用户"),
|
),
|
||||||
controller: userController,
|
FormBuilderTextField(
|
||||||
),
|
name: "name",
|
||||||
TextField(
|
decoration: const InputDecoration(labelText: "名称"),
|
||||||
decoration: Commons.requiredTextFieldStyle(text: "密码"),
|
validator: FormBuilderValidators.required(),
|
||||||
controller: passController,
|
autovalidateMode: AutovalidateMode.onUserInteraction),
|
||||||
),
|
FormBuilderTextField(
|
||||||
],
|
name: "url",
|
||||||
)
|
decoration: const InputDecoration(
|
||||||
: Container()
|
labelText: "地址", hintText: "http://127.0.0.1:9091"),
|
||||||
],
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
);
|
validator: FormBuilderValidators.compose([
|
||||||
})
|
FormBuilderValidators.required(),
|
||||||
];
|
FormBuilderValidators.url()
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
FormBuilderSwitch(
|
||||||
|
name: "auth",
|
||||||
|
title: const Text("需要认证"),
|
||||||
|
initialValue: _enableAuth,
|
||||||
|
onChanged: (v) {
|
||||||
|
setState(() {
|
||||||
|
_enableAuth = v!;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
_enableAuth
|
||||||
|
? Column(
|
||||||
|
children: [
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "user",
|
||||||
|
decoration: Commons.requiredTextFieldStyle(
|
||||||
|
text: "用户"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
autovalidateMode:
|
||||||
|
AutovalidateMode.onUserInteraction),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "password",
|
||||||
|
decoration: Commons.requiredTextFieldStyle(
|
||||||
|
text: "密码"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
obscureText: true,
|
||||||
|
autovalidateMode:
|
||||||
|
AutovalidateMode.onUserInteraction),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Container()
|
||||||
|
],
|
||||||
|
);
|
||||||
|
})
|
||||||
|
],
|
||||||
|
));
|
||||||
|
});
|
||||||
onDelete() async {
|
onDelete() async {
|
||||||
return ref
|
return ref
|
||||||
.read(dwonloadClientsProvider.notifier)
|
.read(dwonloadClientsProvider.notifier)
|
||||||
@@ -366,13 +422,18 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() async {
|
onSubmit() async {
|
||||||
return ref.read(dwonloadClientsProvider.notifier).addDownloadClients(
|
if (_formKey.currentState!.saveAndValidate()) {
|
||||||
DownloadClient(
|
var values = _formKey.currentState!.value;
|
||||||
name: nameController.text,
|
return ref.read(dwonloadClientsProvider.notifier).addDownloadClients(
|
||||||
implementation: "transmission",
|
DownloadClient(
|
||||||
url: urlController.text,
|
name: values["name"],
|
||||||
user: _enableAuth ? userController.text : null,
|
implementation: values["impl"],
|
||||||
password: _enableAuth ? passController.text : null));
|
url: values["url"],
|
||||||
|
user: _enableAuth ? values["user"] : null,
|
||||||
|
password: _enableAuth ? values["password"] : null));
|
||||||
|
} else {
|
||||||
|
throw "数据校验不通过";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return showSettingDialog(
|
return showSettingDialog(
|
||||||
@@ -380,112 +441,137 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showStorageDetails(Storage s) {
|
Future<void> showStorageDetails(Storage s) {
|
||||||
var nameController = TextEditingController(text: s.name);
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
var tvPathController = TextEditingController();
|
|
||||||
var moviePathController = TextEditingController();
|
|
||||||
var urlController = TextEditingController();
|
|
||||||
var userController = TextEditingController();
|
|
||||||
var passController = TextEditingController();
|
|
||||||
bool enablingChangeFileHash = false;
|
|
||||||
if (s.settings != null) {
|
|
||||||
tvPathController.text = s.settings!["tv_path"] ?? "";
|
|
||||||
moviePathController.text = s.settings!["movie_path"] ?? "";
|
|
||||||
urlController.text = s.settings!["url"] ?? "";
|
|
||||||
userController.text = s.settings!["user"] ?? "";
|
|
||||||
passController.text = s.settings!["password"] ?? "";
|
|
||||||
enablingChangeFileHash =
|
|
||||||
s.settings!["change_file_hash"] == "true" ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String selectImpl = s.implementation == null ? "local" : s.implementation!;
|
String selectImpl = s.implementation == null ? "local" : s.implementation!;
|
||||||
final widgets =
|
final widgets =
|
||||||
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
||||||
return Column(
|
return FormBuilder(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
key: _formKey,
|
||||||
children: <Widget>[
|
autovalidateMode: AutovalidateMode.disabled,
|
||||||
DropdownMenu(
|
initialValue: {
|
||||||
label: const Text("类型"),
|
"name": s.name,
|
||||||
onSelected: (value) {
|
"impl": s.implementation == null ? "local" : s.implementation!,
|
||||||
setState(() {
|
"user": s.settings != null ? s.settings!["user"] ?? "" : "",
|
||||||
selectImpl = value!;
|
"password": s.settings != null ? s.settings!["password"] ?? "" : "",
|
||||||
});
|
"tv_path": s.settings != null ? s.settings!["tv_path"] ?? "" : "",
|
||||||
},
|
"url": s.settings != null ? s.settings!["url"] ?? "" : "",
|
||||||
initialSelection: selectImpl,
|
"movie_path":
|
||||||
dropdownMenuEntries: const [
|
s.settings != null ? s.settings!["movie_path"] ?? "" : "",
|
||||||
DropdownMenuEntry(value: "local", label: "本地存储"),
|
"change_file_hash": s.settings != null
|
||||||
DropdownMenuEntry(value: "webdav", label: "webdav")
|
? s.settings!["change_file_hash"] == "true"
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
: false,
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
FormBuilderDropdown<String>(
|
||||||
|
name: "impl",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
decoration: const InputDecoration(labelText: "类型"),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
selectImpl = value!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: "local",
|
||||||
|
child: Text("本地存储"),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: "webdav",
|
||||||
|
child: Text("webdav"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "name",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
initialValue: s.name,
|
||||||
|
decoration: const InputDecoration(labelText: "名称"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
selectImpl != "local"
|
||||||
|
? Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "url",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
decoration:
|
||||||
|
const InputDecoration(labelText: "Webdav地址"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "user",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
decoration: const InputDecoration(labelText: "用户"),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "password",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
decoration: const InputDecoration(labelText: "密码"),
|
||||||
|
obscureText: true,
|
||||||
|
),
|
||||||
|
FormBuilderCheckbox(
|
||||||
|
name: "change_file_hash",
|
||||||
|
title: const Text(
|
||||||
|
"上传时更改文件哈希",
|
||||||
|
style: TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "tv_path",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
decoration: const InputDecoration(labelText: "电视剧路径"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "movie_path",
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
decoration: const InputDecoration(labelText: "电影路径"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
));
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: "名称"),
|
|
||||||
controller: nameController,
|
|
||||||
),
|
|
||||||
selectImpl != "local"
|
|
||||||
? Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: "Webdav地址"),
|
|
||||||
controller: urlController,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: "用户"),
|
|
||||||
controller: userController,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: "密码"),
|
|
||||||
controller: passController,
|
|
||||||
),
|
|
||||||
CheckboxListTile(
|
|
||||||
title: const Text("上传时更改文件哈希", style: TextStyle(fontSize: 14),),
|
|
||||||
value: enablingChangeFileHash,
|
|
||||||
onChanged: (v) {
|
|
||||||
setState(() {
|
|
||||||
enablingChangeFileHash = v??false;
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: "电视剧路径"),
|
|
||||||
controller: tvPathController,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: "电影路径"),
|
|
||||||
controller: moviePathController,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
onSubmit() async {
|
onSubmit() async {
|
||||||
return ref.read(storageSettingProvider.notifier).addStorage(Storage(
|
if (_formKey.currentState!.saveAndValidate()) {
|
||||||
name: nameController.text,
|
final values = _formKey.currentState!.value;
|
||||||
implementation: selectImpl,
|
return ref.read(storageSettingProvider.notifier).addStorage(Storage(
|
||||||
settings: {
|
name: values["name"],
|
||||||
"tv_path": tvPathController.text,
|
implementation: selectImpl,
|
||||||
"movie_path": moviePathController.text,
|
settings: {
|
||||||
"url": urlController.text,
|
"tv_path": values["tv_path"],
|
||||||
"user": userController.text,
|
"movie_path": values["movie_path"],
|
||||||
"password": passController.text,
|
"url": values["url"],
|
||||||
"change_file_hash": enablingChangeFileHash ? "true" : "false"
|
"user": values["user"],
|
||||||
},
|
"password": values["password"],
|
||||||
));
|
"change_file_hash":
|
||||||
|
values["change_file_hash"] as bool ? "true" : "false"
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
throw "数据校验位未通过";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDelete() async {
|
onDelete() async {
|
||||||
return ref.read(storageSettingProvider.notifier).deleteStorage(s.id!);
|
return ref.read(storageSettingProvider.notifier).deleteStorage(s.id!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return showSettingDialog('存储', s.id != null, [widgets], onSubmit, onDelete);
|
return showSettingDialog('存储', s.id != null, widgets, onSubmit, onDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showSettingDialog(
|
Future<void> showSettingDialog(String title, bool showDelete, Widget body,
|
||||||
String title,
|
Future Function() onSubmit, Future Function() onDelete) {
|
||||||
bool showDelete,
|
|
||||||
List<Widget> children,
|
|
||||||
Future Function() onSubmit,
|
|
||||||
Future Function() onDelete) {
|
|
||||||
return showDialog<void>(
|
return showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
@@ -495,9 +581,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 200),
|
constraints: const BoxConstraints(maxWidth: 200),
|
||||||
child: ListBody(
|
child: body,
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
@@ -505,7 +589,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
? TextButton(
|
? TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final f = onDelete();
|
final f = onDelete();
|
||||||
f.whenComplete(() {
|
f.then((v) {
|
||||||
Utils.showSnakeBar("删除成功");
|
Utils.showSnakeBar("删除成功");
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}).onError((e, s) {
|
}).onError((e, s) {
|
||||||
@@ -524,7 +608,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
child: const Text('确定'),
|
child: const Text('确定'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final f = onSubmit();
|
final f = onSubmit();
|
||||||
f.whenComplete(() {
|
f.then((v) {
|
||||||
Utils.showSnakeBar("操作成功");
|
Utils.showSnakeBar("操作成功");
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}).onError((e, s) {
|
}).onError((e, s) {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
|||||||
widget.seriesId)
|
widget.seriesId)
|
||||||
.notifier)
|
.notifier)
|
||||||
.delete()
|
.delete()
|
||||||
.whenComplete(() =>
|
.then((v) =>
|
||||||
context.go(WelcomePage.routeTv))
|
context.go(WelcomePage.routeTv))
|
||||||
.onError((error, trace) =>
|
.onError((error, trace) =>
|
||||||
Utils.showSnakeBar(
|
Utils.showSnakeBar(
|
||||||
|
|||||||
@@ -118,6 +118,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.11+1"
|
version: "0.1.11+1"
|
||||||
|
flutter_form_builder:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_form_builder
|
||||||
|
sha256: "447f8808f68070f7df968e8063aada3c9d2e90e789b5b70f3b44e4b315212656"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "9.3.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -126,6 +134,11 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_login:
|
flutter_login:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -160,6 +173,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.7.0"
|
version: "10.7.0"
|
||||||
|
form_builder_validators:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: form_builder_validators
|
||||||
|
sha256: c61ed7b1deecf0e1ebe49e2fa79e3283937c5a21c7e48e3ed9856a4a14e1191a
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.0"
|
||||||
go_router:
|
go_router:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ dependencies:
|
|||||||
percent_indicator: ^4.2.3
|
percent_indicator: ^4.2.3
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
flutter_adaptive_scaffold: ^0.1.11+1
|
flutter_adaptive_scaffold: ^0.1.11+1
|
||||||
|
flutter_form_builder: ^9.3.0
|
||||||
|
form_builder_validators: ^11.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user