feat(metadata): add plugin form

This commit is contained in:
Kingkor Roy Tirtho 2025-07-03 23:55:29 +06:00
parent 5b457fc4bd
commit 5be4747c66
10 changed files with 1097 additions and 190 deletions

View File

@ -91,6 +91,10 @@ class AppRouter extends RootStackRouter {
path: "settings/metadata-provider", path: "settings/metadata-provider",
page: SettingsMetadataProviderRoute.page, page: SettingsMetadataProviderRoute.page,
), ),
AutoRoute(
path: "settings/metadata-provider/metadata-form",
page: SettingsMetadataProviderFormRoute.page,
),
AutoRoute( AutoRoute(
path: "settings/blacklist", path: "settings/blacklist",
page: BlackListRoute.page, page: BlackListRoute.page,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
part of 'metadata.dart';
enum FormFieldVariant { text, password, number }
@Freezed(unionKey: 'objectType')
class MetadataFormFieldObject with _$MetadataFormFieldObject {
@FreezedUnionValue("input")
factory MetadataFormFieldObject.input({
required String objectType,
required String id,
@Default(FormFieldVariant.text) FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex,
}) = MetadataFormFieldInputObject;
@FreezedUnionValue("text")
factory MetadataFormFieldObject.text({
required String objectType,
required String text,
}) = MetadataFormFieldTextObject;
factory MetadataFormFieldObject.fromJson(Map<String, dynamic> json) =>
_$MetadataFormFieldObjectFromJson(json);
}

View File

@ -18,6 +18,7 @@ part 'metadata.freezed.dart';
part 'album.dart'; part 'album.dart';
part 'artist.dart'; part 'artist.dart';
part 'browse.dart'; part 'browse.dart';
part 'fields.dart';
part 'image.dart'; part 'image.dart';
part 'pagination.dart'; part 'pagination.dart';
part 'playlist.dart'; part 'playlist.dart';

View File

@ -1469,6 +1469,609 @@ abstract class _SpotubeBrowseSectionObject<T>
get copyWith => throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }
MetadataFormFieldObject _$MetadataFormFieldObjectFromJson(
Map<String, dynamic> json) {
switch (json['objectType']) {
case 'input':
return MetadataFormFieldInputObject.fromJson(json);
case 'text':
return MetadataFormFieldTextObject.fromJson(json);
default:
throw CheckedFromJsonException(
json,
'objectType',
'MetadataFormFieldObject',
'Invalid union type "${json['objectType']}"!');
}
}
/// @nodoc
mixin _$MetadataFormFieldObject {
String get objectType => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)
input,
required TResult Function(String objectType, String text) text,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)?
input,
TResult? Function(String objectType, String text)? text,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)?
input,
TResult Function(String objectType, String text)? text,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(MetadataFormFieldInputObject value) input,
required TResult Function(MetadataFormFieldTextObject value) text,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(MetadataFormFieldInputObject value)? input,
TResult? Function(MetadataFormFieldTextObject value)? text,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(MetadataFormFieldInputObject value)? input,
TResult Function(MetadataFormFieldTextObject value)? text,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
/// Serializes this MetadataFormFieldObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$MetadataFormFieldObjectCopyWith<MetadataFormFieldObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $MetadataFormFieldObjectCopyWith<$Res> {
factory $MetadataFormFieldObjectCopyWith(MetadataFormFieldObject value,
$Res Function(MetadataFormFieldObject) then) =
_$MetadataFormFieldObjectCopyWithImpl<$Res, MetadataFormFieldObject>;
@useResult
$Res call({String objectType});
}
/// @nodoc
class _$MetadataFormFieldObjectCopyWithImpl<$Res,
$Val extends MetadataFormFieldObject>
implements $MetadataFormFieldObjectCopyWith<$Res> {
_$MetadataFormFieldObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? objectType = null,
}) {
return _then(_value.copyWith(
objectType: null == objectType
? _value.objectType
: objectType // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$MetadataFormFieldInputObjectImplCopyWith<$Res>
implements $MetadataFormFieldObjectCopyWith<$Res> {
factory _$$MetadataFormFieldInputObjectImplCopyWith(
_$MetadataFormFieldInputObjectImpl value,
$Res Function(_$MetadataFormFieldInputObjectImpl) then) =
__$$MetadataFormFieldInputObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex});
}
/// @nodoc
class __$$MetadataFormFieldInputObjectImplCopyWithImpl<$Res>
extends _$MetadataFormFieldObjectCopyWithImpl<$Res,
_$MetadataFormFieldInputObjectImpl>
implements _$$MetadataFormFieldInputObjectImplCopyWith<$Res> {
__$$MetadataFormFieldInputObjectImplCopyWithImpl(
_$MetadataFormFieldInputObjectImpl _value,
$Res Function(_$MetadataFormFieldInputObjectImpl) _then)
: super(_value, _then);
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? objectType = null,
Object? id = null,
Object? variant = null,
Object? placeholder = freezed,
Object? defaultValue = freezed,
Object? required = freezed,
Object? regex = freezed,
}) {
return _then(_$MetadataFormFieldInputObjectImpl(
objectType: null == objectType
? _value.objectType
: objectType // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
variant: null == variant
? _value.variant
: variant // ignore: cast_nullable_to_non_nullable
as FormFieldVariant,
placeholder: freezed == placeholder
? _value.placeholder
: placeholder // ignore: cast_nullable_to_non_nullable
as String?,
defaultValue: freezed == defaultValue
? _value.defaultValue
: defaultValue // ignore: cast_nullable_to_non_nullable
as String?,
required: freezed == required
? _value.required
: required // ignore: cast_nullable_to_non_nullable
as bool?,
regex: freezed == regex
? _value.regex
: regex // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MetadataFormFieldInputObjectImpl
implements MetadataFormFieldInputObject {
_$MetadataFormFieldInputObjectImpl(
{required this.objectType,
required this.id,
this.variant = FormFieldVariant.text,
this.placeholder,
this.defaultValue,
this.required,
this.regex});
factory _$MetadataFormFieldInputObjectImpl.fromJson(
Map<String, dynamic> json) =>
_$$MetadataFormFieldInputObjectImplFromJson(json);
@override
final String objectType;
@override
final String id;
@override
@JsonKey()
final FormFieldVariant variant;
@override
final String? placeholder;
@override
final String? defaultValue;
@override
final bool? required;
@override
final String? regex;
@override
String toString() {
return 'MetadataFormFieldObject.input(objectType: $objectType, id: $id, variant: $variant, placeholder: $placeholder, defaultValue: $defaultValue, required: $required, regex: $regex)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MetadataFormFieldInputObjectImpl &&
(identical(other.objectType, objectType) ||
other.objectType == objectType) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.variant, variant) || other.variant == variant) &&
(identical(other.placeholder, placeholder) ||
other.placeholder == placeholder) &&
(identical(other.defaultValue, defaultValue) ||
other.defaultValue == defaultValue) &&
(identical(other.required, required) ||
other.required == required) &&
(identical(other.regex, regex) || other.regex == regex));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, objectType, id, variant,
placeholder, defaultValue, required, regex);
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MetadataFormFieldInputObjectImplCopyWith<
_$MetadataFormFieldInputObjectImpl>
get copyWith => __$$MetadataFormFieldInputObjectImplCopyWithImpl<
_$MetadataFormFieldInputObjectImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)
input,
required TResult Function(String objectType, String text) text,
}) {
return input(
objectType, id, variant, placeholder, defaultValue, required, regex);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)?
input,
TResult? Function(String objectType, String text)? text,
}) {
return input?.call(
objectType, id, variant, placeholder, defaultValue, required, regex);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)?
input,
TResult Function(String objectType, String text)? text,
required TResult orElse(),
}) {
if (input != null) {
return input(
objectType, id, variant, placeholder, defaultValue, required, regex);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(MetadataFormFieldInputObject value) input,
required TResult Function(MetadataFormFieldTextObject value) text,
}) {
return input(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(MetadataFormFieldInputObject value)? input,
TResult? Function(MetadataFormFieldTextObject value)? text,
}) {
return input?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(MetadataFormFieldInputObject value)? input,
TResult Function(MetadataFormFieldTextObject value)? text,
required TResult orElse(),
}) {
if (input != null) {
return input(this);
}
return orElse();
}
@override
Map<String, dynamic> toJson() {
return _$$MetadataFormFieldInputObjectImplToJson(
this,
);
}
}
abstract class MetadataFormFieldInputObject implements MetadataFormFieldObject {
factory MetadataFormFieldInputObject(
{required final String objectType,
required final String id,
final FormFieldVariant variant,
final String? placeholder,
final String? defaultValue,
final bool? required,
final String? regex}) = _$MetadataFormFieldInputObjectImpl;
factory MetadataFormFieldInputObject.fromJson(Map<String, dynamic> json) =
_$MetadataFormFieldInputObjectImpl.fromJson;
@override
String get objectType;
String get id;
FormFieldVariant get variant;
String? get placeholder;
String? get defaultValue;
bool? get required;
String? get regex;
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MetadataFormFieldInputObjectImplCopyWith<
_$MetadataFormFieldInputObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$MetadataFormFieldTextObjectImplCopyWith<$Res>
implements $MetadataFormFieldObjectCopyWith<$Res> {
factory _$$MetadataFormFieldTextObjectImplCopyWith(
_$MetadataFormFieldTextObjectImpl value,
$Res Function(_$MetadataFormFieldTextObjectImpl) then) =
__$$MetadataFormFieldTextObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String objectType, String text});
}
/// @nodoc
class __$$MetadataFormFieldTextObjectImplCopyWithImpl<$Res>
extends _$MetadataFormFieldObjectCopyWithImpl<$Res,
_$MetadataFormFieldTextObjectImpl>
implements _$$MetadataFormFieldTextObjectImplCopyWith<$Res> {
__$$MetadataFormFieldTextObjectImplCopyWithImpl(
_$MetadataFormFieldTextObjectImpl _value,
$Res Function(_$MetadataFormFieldTextObjectImpl) _then)
: super(_value, _then);
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? objectType = null,
Object? text = null,
}) {
return _then(_$MetadataFormFieldTextObjectImpl(
objectType: null == objectType
? _value.objectType
: objectType // ignore: cast_nullable_to_non_nullable
as String,
text: null == text
? _value.text
: text // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MetadataFormFieldTextObjectImpl implements MetadataFormFieldTextObject {
_$MetadataFormFieldTextObjectImpl(
{required this.objectType, required this.text});
factory _$MetadataFormFieldTextObjectImpl.fromJson(
Map<String, dynamic> json) =>
_$$MetadataFormFieldTextObjectImplFromJson(json);
@override
final String objectType;
@override
final String text;
@override
String toString() {
return 'MetadataFormFieldObject.text(objectType: $objectType, text: $text)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MetadataFormFieldTextObjectImpl &&
(identical(other.objectType, objectType) ||
other.objectType == objectType) &&
(identical(other.text, text) || other.text == text));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, objectType, text);
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MetadataFormFieldTextObjectImplCopyWith<_$MetadataFormFieldTextObjectImpl>
get copyWith => __$$MetadataFormFieldTextObjectImplCopyWithImpl<
_$MetadataFormFieldTextObjectImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)
input,
required TResult Function(String objectType, String text) text,
}) {
return text(objectType, this.text);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)?
input,
TResult? Function(String objectType, String text)? text,
}) {
return text?.call(objectType, this.text);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(
String objectType,
String id,
FormFieldVariant variant,
String? placeholder,
String? defaultValue,
bool? required,
String? regex)?
input,
TResult Function(String objectType, String text)? text,
required TResult orElse(),
}) {
if (text != null) {
return text(objectType, this.text);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(MetadataFormFieldInputObject value) input,
required TResult Function(MetadataFormFieldTextObject value) text,
}) {
return text(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(MetadataFormFieldInputObject value)? input,
TResult? Function(MetadataFormFieldTextObject value)? text,
}) {
return text?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(MetadataFormFieldInputObject value)? input,
TResult Function(MetadataFormFieldTextObject value)? text,
required TResult orElse(),
}) {
if (text != null) {
return text(this);
}
return orElse();
}
@override
Map<String, dynamic> toJson() {
return _$$MetadataFormFieldTextObjectImplToJson(
this,
);
}
}
abstract class MetadataFormFieldTextObject implements MetadataFormFieldObject {
factory MetadataFormFieldTextObject(
{required final String objectType,
required final String text}) = _$MetadataFormFieldTextObjectImpl;
factory MetadataFormFieldTextObject.fromJson(Map<String, dynamic> json) =
_$MetadataFormFieldTextObjectImpl.fromJson;
@override
String get objectType;
String get text;
/// Create a copy of MetadataFormFieldObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MetadataFormFieldTextObjectImplCopyWith<_$MetadataFormFieldTextObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}
SpotubeImageObject _$SpotubeImageObjectFromJson(Map<String, dynamic> json) { SpotubeImageObject _$SpotubeImageObjectFromJson(Map<String, dynamic> json) {
return _SpotubeImageObject.fromJson(json); return _SpotubeImageObject.fromJson(json);
} }

View File

@ -153,6 +153,52 @@ Map<String, dynamic> _$$SpotubeBrowseSectionObjectImplToJson<T>(
'items': instance.items.map(toJsonT).toList(), 'items': instance.items.map(toJsonT).toList(),
}; };
_$MetadataFormFieldInputObjectImpl _$$MetadataFormFieldInputObjectImplFromJson(
Map json) =>
_$MetadataFormFieldInputObjectImpl(
objectType: json['objectType'] as String,
id: json['id'] as String,
variant:
$enumDecodeNullable(_$FormFieldVariantEnumMap, json['variant']) ??
FormFieldVariant.text,
placeholder: json['placeholder'] as String?,
defaultValue: json['defaultValue'] as String?,
required: json['required'] as bool?,
regex: json['regex'] as String?,
);
Map<String, dynamic> _$$MetadataFormFieldInputObjectImplToJson(
_$MetadataFormFieldInputObjectImpl instance) =>
<String, dynamic>{
'objectType': instance.objectType,
'id': instance.id,
'variant': _$FormFieldVariantEnumMap[instance.variant]!,
'placeholder': instance.placeholder,
'defaultValue': instance.defaultValue,
'required': instance.required,
'regex': instance.regex,
};
const _$FormFieldVariantEnumMap = {
FormFieldVariant.text: 'text',
FormFieldVariant.password: 'password',
FormFieldVariant.number: 'number',
};
_$MetadataFormFieldTextObjectImpl _$$MetadataFormFieldTextObjectImplFromJson(
Map json) =>
_$MetadataFormFieldTextObjectImpl(
objectType: json['objectType'] as String,
text: json['text'] as String,
);
Map<String, dynamic> _$$MetadataFormFieldTextObjectImplToJson(
_$MetadataFormFieldTextObjectImpl instance) =>
<String, dynamic>{
'objectType': instance.objectType,
'text': instance.text,
};
_$SpotubeImageObjectImpl _$$SpotubeImageObjectImplFromJson(Map json) => _$SpotubeImageObjectImpl _$$SpotubeImageObjectImplFromJson(Map json) =>
_$SpotubeImageObjectImpl( _$SpotubeImageObjectImpl(
url: json['url'] as String, url: json['url'] as String,

View File

@ -0,0 +1,141 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/metadata/metadata.dart';
@RoutePage()
class SettingsMetadataProviderFormPage extends HookConsumerWidget {
final String title;
final List<MetadataFormFieldObject> fields;
const SettingsMetadataProviderFormPage({
super.key,
required this.title,
required this.fields,
});
@override
Widget build(BuildContext context, ref) {
final formKey = useMemoized(() => GlobalKey<FormBuilderState>(), []);
return Scaffold(
headers: [
TitleBar(
title: Text(title),
),
],
child: FormBuilder(
key: formKey,
child: Center(
child: Container(
padding: const EdgeInsets.all(16),
constraints: const BoxConstraints(maxWidth: 600),
child: CustomScrollView(
shrinkWrap: true,
slivers: [
SliverToBoxAdapter(
child: Text(
title,
textAlign: TextAlign.center,
style: context.theme.typography.h2,
),
),
const SliverGap(24),
SliverList.separated(
itemCount: fields.length,
separatorBuilder: (context, index) => const Gap(12),
itemBuilder: (context, index) {
if (fields[index] is MetadataFormFieldTextObject) {
final field =
fields[index] as MetadataFormFieldTextObject;
return MarkdownBody(data: field.text);
}
final field = fields[index] as MetadataFormFieldInputObject;
return FormBuilderField(
name: field.id,
initialValue: field.defaultValue,
validator: FormBuilderValidators.compose([
if (field.required == true)
FormBuilderValidators.required(
errorText: 'This field is required',
),
if (field.regex != null)
FormBuilderValidators.match(
RegExp(field.regex!),
errorText:
"Input doesn't match the required format",
),
]),
builder: (formField) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4,
children: [
TextField(
placeholder: field.placeholder == null
? null
: Text(field.placeholder!),
initialValue: formField.value,
onChanged: (value) {
formField.didChange(value);
},
obscureText:
field.variant == FormFieldVariant.password,
keyboardType:
field.variant == FormFieldVariant.number
? TextInputType.number
: TextInputType.text,
features: [
if (field.variant == FormFieldVariant.password)
const InputFeature.passwordToggle(),
],
),
if (formField.hasError)
Text(
formField.errorText ?? '',
style: const TextStyle(
color: Colors.red, fontSize: 12),
),
],
);
},
);
},
),
const SliverGap(24),
SliverToBoxAdapter(
child: Button.primary(
onPressed: () {
if (formKey.currentState?.saveAndValidate() != true) {
return;
}
final data = formKey.currentState!.value.entries
.map((e) => <String, dynamic>{
"id": e.key,
"value": e.value,
})
.toList();
context.router.maybePop(data);
},
child: Text(context.l10n.submit),
),
),
const SliverGap(200)
],
),
),
),
),
);
}
}

View File

@ -8,6 +8,7 @@ import 'package:hetu_std/hetu_std.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/collections/routes.dart'; import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/services/metadata/apis/localstorage.dart'; import 'package:spotube/services/metadata/apis/localstorage.dart';
@ -57,6 +58,20 @@ class MetadataPlugin {
onNavigatorPop: () { onNavigatorPop: () {
pageContext?.maybePop(); pageContext?.maybePop();
}, },
onShowForm: (title, fields) async {
if (rootNavigatorKey.currentContext == null) {
return [];
}
return await rootNavigatorKey.currentContext!.router
.push<List<Map<String, dynamic>>?>(
SettingsMetadataProviderFormRoute(
title: title,
fields:
fields.map((e) => MetadataFormFieldObject.fromJson(e)).toList(),
),
);
},
); );
await HetuStdLoader.loadBytecodeFlutter(hetu); await HetuStdLoader.loadBytecodeFlutter(hetu);

View File

@ -938,6 +938,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_markdown_plus:
dependency: "direct main"
description:
name: flutter_markdown_plus
sha256: fe74214c5ac2f850d93efda290dcde3f18006e90a87caa9e3e6c13222a5db4de
url: "https://pub.dev"
source: hosted
version: "1.0.3"
flutter_native_splash: flutter_native_splash:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1209,7 +1217,7 @@ packages:
description: description:
path: "." path: "."
ref: main ref: main
resolved-ref: eeb574c3fac0da07ba93c9d972b7eb38960538d2 resolved-ref: c4895250ee45a59c88770f97abebc9e9bbb62259
url: "https://github.com/KRTirtho/hetu_spotube_plugin.git" url: "https://github.com/KRTirtho/hetu_spotube_plugin.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@ -1523,6 +1531,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.3-main.0" version: "0.1.3-main.0"
markdown:
dependency: transitive
description:
name: markdown
sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1"
url: "https://pub.dev"
source: hosted
version: "7.3.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:

View File

@ -156,6 +156,7 @@ dependencies:
url: https://github.com/KRTirtho/hetu_spotube_plugin.git url: https://github.com/KRTirtho/hetu_spotube_plugin.git
ref: main ref: main
get_it: ^8.0.3 get_it: ^8.0.3
flutter_markdown_plus: ^1.0.3
dev_dependencies: dev_dependencies:
build_runner: ^2.4.13 build_runner: ^2.4.13