WIP: init wizard

This commit is contained in:
Simon Ding
2024-11-10 13:28:21 +08:00
parent a83f860624
commit aaa006a322
3 changed files with 287 additions and 83 deletions

190
ui/lib/init_wizard.dart Normal file
View File

@@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:ui/settings/prowlarr.dart';
import 'package:ui/widgets/widgets.dart';
class InitWizard extends ConsumerStatefulWidget {
const InitWizard({super.key});
static final String route = "/init_wizard";
@override
ConsumerState<ConsumerStatefulWidget> createState() {
return _InitWizardState();
}
}
class _InitWizardState extends ConsumerState<InitWizard> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SelectionArea(
child: Container(
padding: EdgeInsets.all(50),
child: ListView(
children: [
Container(
alignment: Alignment.center,
child: Text(
"Polaris 影视追踪下载",
style: TextStyle(
fontSize: 30,
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold),
),
),
Container(
padding: EdgeInsets.only(left: 10, top: 30, bottom: 30),
child: Text(
"设置向导",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary),
),
),
tmdbSetting(),
downloaderSetting(),
indexerSetting(),
],
),
)),
);
}
Widget tmdbSetting() {
return ExpansionTile(
title: Text(
"第一步TMDB设置",
style: TextStyle(fontWeight: FontWeight.bold),
),
childrenPadding: EdgeInsets.only(left: 100, right: 20),
initiallyExpanded: true,
children: [
Container(
alignment: Alignment.topLeft,
child: Text("TMDB API Key 设置用来获取各种影视的信息API Key获取方式参考官网"),
),
FormBuilder(
child: Column(
children: [
FormBuilderTextField(
name: "tmdb",
decoration: InputDecoration(labelText: "TMDB API Key"),
),
Center(
child: Padding(
padding: EdgeInsets.all(10),
child: ElevatedButton(onPressed: null, child: Text("保存")),
))
],
))
],
);
}
Widget indexerSetting() {
return ExpansionTile(
initiallyExpanded: true,
childrenPadding: EdgeInsets.only(left: 100, right: 20),
title: Text(
"第三步Prowlarr设置",
style: TextStyle(fontWeight: FontWeight.bold),
),
children: [ProwlarrSettingPage()],
);
}
Widget downloaderSetting() {
final _formKey = GlobalKey<FormBuilderState>();
var _enableAuth = false;
String selectImpl = "transmission";
return ExpansionTile(
childrenPadding: EdgeInsets.only(left: 100, right: 20),
initiallyExpanded: true,
title: Text("第二步:下载客户端", style: TextStyle(fontWeight: FontWeight.bold)),
children: [
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return FormBuilder(
key: _formKey,
initialValue: {
"name": "client.name",
"url": "client.url",
"user": "client.user",
"password": "client.password",
"impl": selectImpl,
},
child: Column(
children: [
FormBuilderDropdown<String>(
name: "impl",
decoration: const InputDecoration(labelText: "类型"),
items: const [
DropdownMenuItem(
value: "transmission", child: Text("Transmission")),
DropdownMenuItem(
value: "qbittorrent", child: Text("qBittorrent")),
],
validator: FormBuilderValidators.required(),
),
FormBuilderTextField(
name: "name",
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.required(),
),
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()
],
);
})
],
));
}),
],
);
}
}

View File

@@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:ui/activity.dart';
import 'package:ui/calendar.dart';
import 'package:ui/init_wizard.dart';
import 'package:ui/login_page.dart';
import 'package:ui/movie_watchlist.dart';
import 'package:ui/providers/APIs.dart';
@@ -124,9 +125,17 @@ class _MyAppState extends ConsumerState<MyApp> {
initialLocation: WelcomePage.routeTv,
routes: [
shellRoute,
GoRoute(
path: "/",
redirect: (context, state) => WelcomePage.routeTv,
),
GoRoute(
path: LoginScreen.route,
builder: (context, state) => const LoginScreen(),
),
GoRoute(
path: InitWizard.route,
builder: (context, state) => const InitWizard(),
)
],
);
@@ -171,88 +180,7 @@ class _MainSkeletonState extends State<MainSkeleton> {
var padding = isSmallScreen(context) ? 5.0 : 20.0;
return AdaptiveScaffold(
appBarBreakpoint: Breakpoints.standard,
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
leading: Container(
alignment: Alignment.centerLeft,
child: TextButton(
onPressed: () => context.go(WelcomePage.routeTv),
child: const Text(
"Polaris",
overflow: TextOverflow.clip,
style: TextStyle(fontSize: 28),
),
),
),
leadingWidth: isSmallScreen(context) ? 0 : 190,
title: Container(
alignment: Alignment.bottomLeft,
child: SearchAnchor(
builder: (BuildContext context, SearchController controller) {
return Container(
constraints: const BoxConstraints(maxWidth: 250, maxHeight: 40),
child: Opacity(
opacity: 0.8,
child: SearchBar(
hintText: "在此搜索...",
leading: const Icon(Icons.search),
controller: controller,
shadowColor: WidgetStateColor.transparent,
backgroundColor: WidgetStatePropertyAll(
Theme.of(context).colorScheme.primaryContainer),
onSubmitted: (value) => context.go(Uri(
path: SearchPage.route,
queryParameters: {'query': value}).toString()),
),
),
);
}, suggestionsBuilder:
(BuildContext context, SearchController controller) {
return [Text("dadada")];
}),
),
actions: [
// IconButton(
// onPressed: () => showCalendar(context),
// icon: Icon(Icons.calendar_month)),
IconButton(
onPressed: () => showDonate(context),
icon: Icon(
Icons.favorite_rounded,
color: Colors.red,
)),
MenuAnchor(
menuChildren: [
MenuItemButton(
leadingIcon: const Icon(Icons.exit_to_app),
child: const Text("登出"),
onPressed: () async {
await APIs.logout();
},
),
],
builder: (context, controller, child) {
return TextButton(
onPressed: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
child: const Icon(Icons.account_circle),
);
},
),
],
),
appBar: appBar(),
useDrawer: false,
selectedIndex: widget.body.currentIndex,
onSelectedIndexChange: (p0) => widget.body
@@ -314,4 +242,89 @@ class _MainSkeletonState extends State<MainSkeleton> {
},
);
}
AppBar appBar() {
return AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
leading: Container(
alignment: Alignment.centerLeft,
child: TextButton(
onPressed: () => context.go(WelcomePage.routeTv),
child: const Text(
"Polaris",
overflow: TextOverflow.clip,
style: TextStyle(fontSize: 28),
),
),
),
leadingWidth: isSmallScreen(context) ? 0 : 190,
title: Container(
alignment: Alignment.bottomLeft,
child: SearchAnchor(
builder: (BuildContext context, SearchController controller) {
return Container(
constraints: const BoxConstraints(maxWidth: 250, maxHeight: 40),
child: Opacity(
opacity: 0.8,
child: SearchBar(
hintText: "在此搜索...",
leading: const Icon(Icons.search),
controller: controller,
shadowColor: WidgetStateColor.transparent,
backgroundColor: WidgetStatePropertyAll(
Theme.of(context).colorScheme.primaryContainer),
onSubmitted: (value) => context.go(Uri(
path: SearchPage.route,
queryParameters: {'query': value}).toString()),
),
),
);
}, suggestionsBuilder:
(BuildContext context, SearchController controller) {
return [Text("dadada")];
}),
),
actions: [
// IconButton(
// onPressed: () => showCalendar(context),
// icon: Icon(Icons.calendar_month)),
IconButton(
onPressed: () => showDonate(context),
icon: Icon(
Icons.favorite_rounded,
color: Colors.red,
)),
MenuAnchor(
menuChildren: [
MenuItemButton(
leadingIcon: const Icon(Icons.exit_to_app),
child: const Text("登出"),
onPressed: () async {
await APIs.logout();
},
),
],
builder: (context, controller, child) {
return TextButton(
onPressed: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
child: const Icon(Icons.account_circle),
);
},
),
],
);
}
}

View File

@@ -51,7 +51,8 @@ class ProwlarrSettingState extends ConsumerState<ProwlarrSettingPage> {
FormBuilderSwitch(
name: "disabled",
title: const Text("禁用 Prowlarr"),
decoration: InputDecoration(icon: Icon(Icons.do_not_disturb)),
decoration:
InputDecoration(icon: Icon(Icons.do_not_disturb)),
),
Center(
child: Padding(