feat: better form

This commit is contained in:
Simon Ding
2024-07-25 14:02:47 +08:00
parent d63a923589
commit 5cc88986d2
8 changed files with 392 additions and 284 deletions

View File

@@ -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"));
}; };
} }

View File

@@ -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"));

View File

@@ -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"]);
} }
} }

View File

@@ -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) {

View File

@@ -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,36 +30,32 @@ 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(
@@ -70,22 +66,20 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
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,11 +153,18 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
}); });
} }
userController.text = data.user; userController.text = data.user;
return Column( return FormBuilder(
key: _formKey2,
initialValue: {
"user": data.user,
"password": data.password,
"enable": data.enable
},
child: Column(
children: [ children: [
SwitchListTile( FormBuilderSwitch(
name: "enable",
title: const Text("开启认证"), title: const Text("开启认证"),
value: _enableAuth!,
onChanged: (v) { onChanged: (v) {
setState(() { setState(() {
_enableAuth = v; _enableAuth = v;
@@ -172,17 +173,23 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
_enableAuth! _enableAuth!
? Column( ? Column(
children: [ children: [
TextFormField( FormBuilderTextField(
controller: userController, name: "user",
autovalidateMode:
AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.required(),
decoration: Commons.requiredTextFieldStyle( decoration: Commons.requiredTextFieldStyle(
text: "用户名", text: "用户名",
icon: const Icon(Icons.account_box), icon: const Icon(Icons.account_box),
)), )),
TextFormField( FormBuilderTextField(
name: "password",
obscureText: true, obscureText: true,
enableSuggestions: false, enableSuggestions: false,
autocorrect: false, autocorrect: false,
controller: passController, autovalidateMode:
AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.required(),
decoration: Commons.requiredTextFieldStyle( decoration: Commons.requiredTextFieldStyle(
text: "密码", text: "密码",
icon: const Icon(Icons.password), icon: const Icon(Icons.password),
@@ -194,18 +201,21 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
child: ElevatedButton( child: ElevatedButton(
child: const Text("保存"), child: const Text("保存"),
onPressed: () { onPressed: () {
if (_formKey2.currentState!.saveAndValidate()) {
var values = _formKey2.currentState!.value;
var f = ref var f = ref
.read(authSettingProvider.notifier) .read(authSettingProvider.notifier)
.updateAuthSetting(_enableAuth!, .updateAuthSetting(_enableAuth!,
userController.text, passController.text); values["user"], values["password"]);
f.whenComplete(() { f.then((v) {
Utils.showSnakeBar("更新成功"); Utils.showSnakeBar("更新成功");
}).onError((e, s) { }).onError((e, s) {
Utils.showSnakeBar("更新失败:$e"); 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")),
], ],
), ),
TextField( FormBuilderTextField(
name: "name",
decoration: Commons.requiredTextFieldStyle(text: "名称"), decoration: Commons.requiredTextFieldStyle(text: "名称"),
controller: nameController, autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.required(),
), ),
TextField( FormBuilderTextField(
name: "url",
decoration: Commons.requiredTextFieldStyle(text: "地址"), decoration: Commons.requiredTextFieldStyle(text: "地址"),
controller: urlController, autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.url()
]),
), ),
TextField( FormBuilderTextField(
name: "api_key",
decoration: Commons.requiredTextFieldStyle(text: "API Key"), decoration: Commons.requiredTextFieldStyle(text: "API Key"),
controller: apiKeyController, autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.required(),
), ),
]; ],
),
);
onDelete() async { onDelete() async {
return ref.read(indexersProvider.notifier).deleteIndexer(indexer.id!); return ref.read(indexersProvider.notifier).deleteIndexer(indexer.id!);
} }
onSubmit() async { onSubmit() async {
if (_formKey.currentState!.saveAndValidate()) {
var values = _formKey.currentState!.value;
return ref.read(indexersProvider.notifier).addIndexer(Indexer( return ref.read(indexersProvider.notifier).addIndexer(Indexer(
name: nameController.text, name: values["name"],
url: urlController.text, url: values["url"],
apiKey: apiKeyController.text)); 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(
key: _formKey,
initialValue: {
"name": client.name,
"url": client.url,
"user": client.user,
"password": client.password,
"impl": "transmission"
},
child: Column(
children: [
FormBuilderDropdown<String>(
name: "impl",
decoration: const InputDecoration(labelText: "类型"),
onChanged: (value) {
setState(() { setState(() {
selectImpl = value!; selectImpl = value!;
}); });
}, },
initialSelection: selectImpl, items: const [
dropdownMenuEntries: const [ DropdownMenuItem(
DropdownMenuEntry(value: "transmission", label: "Transmission"), value: "transmission", child: Text("Transmission")),
], ],
), ),
TextField( FormBuilderTextField(
decoration: Commons.requiredTextFieldStyle(text: "名称"), name: "name",
controller: nameController, decoration: const InputDecoration(labelText: "名称"),
validator: FormBuilderValidators.required(),
autovalidateMode: AutovalidateMode.onUserInteraction),
FormBuilderTextField(
name: "url",
decoration: const InputDecoration(
labelText: "地址", hintText: "http://127.0.0.1:9091"),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.url()
]),
), ),
TextField( StatefulBuilder(
decoration: Commons.requiredTextFieldStyle(text: "地址"), builder: (BuildContext context, StateSetter setState) {
controller: urlController,
),
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Column( return Column(
children: [ children: [
SwitchListTile( FormBuilderSwitch(
name: "auth",
title: const Text("需要认证"), title: const Text("需要认证"),
value: _enableAuth, initialValue: _enableAuth,
onChanged: (v) { onChanged: (v) {
setState(() { setState(() {
_enableAuth = v; _enableAuth = v!;
}); });
}), }),
_enableAuth _enableAuth
? Column( ? Column(
children: [ children: [
TextField( FormBuilderTextField(
decoration: Commons.requiredTextFieldStyle(text: "用户"), name: "user",
controller: userController, decoration: Commons.requiredTextFieldStyle(
), text: "用户"),
TextField( validator: FormBuilderValidators.required(),
decoration: Commons.requiredTextFieldStyle(text: "密码"), autovalidateMode:
controller: passController, AutovalidateMode.onUserInteraction),
), FormBuilderTextField(
name: "password",
decoration: Commons.requiredTextFieldStyle(
text: "密码"),
validator: FormBuilderValidators.required(),
obscureText: true,
autovalidateMode:
AutovalidateMode.onUserInteraction),
], ],
) )
: Container() : 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 {
if (_formKey.currentState!.saveAndValidate()) {
var values = _formKey.currentState!.value;
return ref.read(dwonloadClientsProvider.notifier).addDownloadClients( return ref.read(dwonloadClientsProvider.notifier).addDownloadClients(
DownloadClient( DownloadClient(
name: nameController.text, name: values["name"],
implementation: "transmission", implementation: values["impl"],
url: urlController.text, url: values["url"],
user: _enableAuth ? userController.text : null, user: _enableAuth ? values["user"] : null,
password: _enableAuth ? passController.text : 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(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
initialValue: {
"name": s.name,
"impl": s.implementation == null ? "local" : s.implementation!,
"user": s.settings != null ? s.settings!["user"] ?? "" : "",
"password": s.settings != null ? s.settings!["password"] ?? "" : "",
"tv_path": s.settings != null ? s.settings!["tv_path"] ?? "" : "",
"url": s.settings != null ? s.settings!["url"] ?? "" : "",
"movie_path":
s.settings != null ? s.settings!["movie_path"] ?? "" : "",
"change_file_hash": s.settings != null
? s.settings!["change_file_hash"] == "true"
? true
: false
: false,
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
DropdownMenu( FormBuilderDropdown<String>(
label: const Text("类型"), name: "impl",
onSelected: (value) { autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(labelText: "类型"),
onChanged: (value) {
setState(() { setState(() {
selectImpl = value!; selectImpl = value!;
}); });
}, },
initialSelection: selectImpl, items: const [
dropdownMenuEntries: const [ DropdownMenuItem(
DropdownMenuEntry(value: "local", label: "本地存储"), value: "local",
DropdownMenuEntry(value: "webdav", label: "webdav") child: Text("本地存储"),
],
), ),
TextField( DropdownMenuItem(
value: "webdav",
child: Text("webdav"),
)
],
validator: FormBuilderValidators.required(),
),
FormBuilderTextField(
name: "name",
autovalidateMode: AutovalidateMode.onUserInteraction,
initialValue: s.name,
decoration: const InputDecoration(labelText: "名称"), decoration: const InputDecoration(labelText: "名称"),
controller: nameController, validator: FormBuilderValidators.required(),
), ),
selectImpl != "local" selectImpl != "local"
? Column( ? Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextField( FormBuilderTextField(
decoration: const InputDecoration(labelText: "Webdav地址"), name: "url",
controller: urlController, autovalidateMode: AutovalidateMode.onUserInteraction,
decoration:
const InputDecoration(labelText: "Webdav地址"),
validator: FormBuilderValidators.required(),
), ),
TextField( FormBuilderTextField(
name: "user",
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(labelText: "用户"), decoration: const InputDecoration(labelText: "用户"),
controller: userController,
), ),
TextField( FormBuilderTextField(
name: "password",
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(labelText: "密码"), decoration: const InputDecoration(labelText: "密码"),
controller: passController, obscureText: true,
),
FormBuilderCheckbox(
name: "change_file_hash",
title: const Text(
"上传时更改文件哈希",
style: TextStyle(fontSize: 14),
),
), ),
CheckboxListTile(
title: const Text("上传时更改文件哈希", style: TextStyle(fontSize: 14),),
value: enablingChangeFileHash,
onChanged: (v) {
setState(() {
enablingChangeFileHash = v??false;
});
}),
], ],
) )
: Container(), : Container(),
TextField( FormBuilderTextField(
name: "tv_path",
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(labelText: "电视剧路径"), decoration: const InputDecoration(labelText: "电视剧路径"),
controller: tvPathController, validator: FormBuilderValidators.required(),
), ),
TextField( FormBuilderTextField(
name: "movie_path",
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(labelText: "电影路径"), decoration: const InputDecoration(labelText: "电影路径"),
controller: moviePathController, validator: FormBuilderValidators.required(),
) )
], ],
); ));
}); });
onSubmit() async { onSubmit() async {
if (_formKey.currentState!.saveAndValidate()) {
final values = _formKey.currentState!.value;
return ref.read(storageSettingProvider.notifier).addStorage(Storage( return ref.read(storageSettingProvider.notifier).addStorage(Storage(
name: nameController.text, name: values["name"],
implementation: selectImpl, implementation: selectImpl,
settings: { settings: {
"tv_path": tvPathController.text, "tv_path": values["tv_path"],
"movie_path": moviePathController.text, "movie_path": values["movie_path"],
"url": urlController.text, "url": values["url"],
"user": userController.text, "user": values["user"],
"password": passController.text, "password": values["password"],
"change_file_hash": enablingChangeFileHash ? "true" : "false" "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) {

View File

@@ -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(

View File

@@ -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:

View File

@@ -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: