mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 13:38:13 -08:00
738 lines
42 KiB
Dart
738 lines
42 KiB
Dart
import 'package:collection/collection.dart';
|
|
import 'package:ficonsax/ficonsax.dart';
|
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
|
import 'package:fladder/models/items/item_shared_models.dart';
|
|
import 'package:fladder/providers/edit_item_provider.dart';
|
|
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
|
import 'package:fladder/screens/shared/focused_outlined_text_field.dart';
|
|
import 'package:fladder/screens/shared/media/external_urls.dart';
|
|
import 'package:fladder/screens/shared/outlined_text_field.dart';
|
|
import 'package:fladder/util/jelly_id.dart';
|
|
import 'package:fladder/util/list_extensions.dart';
|
|
import 'package:fladder/util/string_extensions.dart';
|
|
import 'package:fladder/widgets/shared/adaptive_date_picker.dart';
|
|
import 'package:fladder/widgets/shared/enum_selection.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
class EditFields extends ConsumerStatefulWidget {
|
|
final Map<String, dynamic> fields;
|
|
final Map<String, dynamic>? json;
|
|
const EditFields({
|
|
required this.fields,
|
|
required this.json,
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
ConsumerState<ConsumerStatefulWidget> createState() => _EditGeneralState();
|
|
}
|
|
|
|
class _EditGeneralState extends ConsumerState<EditFields> {
|
|
TextEditingController? currentController = TextEditingController();
|
|
String? currentEditingKey;
|
|
List<String> expandedKeys = [];
|
|
|
|
final personName = TextEditingController();
|
|
PersonKind personType = PersonKind.actor;
|
|
final personRole = TextEditingController();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Flexible(
|
|
child: ListView(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
shrinkWrap: true,
|
|
children: [
|
|
if (widget.json != null)
|
|
...widget.fields.entries.map(
|
|
(e) {
|
|
final keyLabel = e.key.toUpperCaseSplit();
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
child: switch (e.value) {
|
|
Map<String, bool> _ => Builder(builder: (context) {
|
|
final map = e.value as Map<String, bool>;
|
|
return SettingsListTile(
|
|
label: Text(keyLabel),
|
|
trailing: EnumBox(
|
|
current: map.entries.firstWhereOrNull((element) => element.value == true)?.key ?? "",
|
|
itemBuilder: (context) => [
|
|
PopupMenuItem(
|
|
child: const Text(""),
|
|
onTap: () => ref.read(editItemProvider.notifier).updateField(MapEntry(e.key, "")),
|
|
),
|
|
...map.entries.map(
|
|
(mapEntry) => PopupMenuItem(
|
|
child: Text(mapEntry.key),
|
|
onTap: () => ref
|
|
.read(editItemProvider.notifier)
|
|
.updateField(MapEntry(e.key, mapEntry.key)),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
List<String> _ => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 21),
|
|
child: Builder(builder: (context) {
|
|
final expanded = expandedKeys.contains(e.key);
|
|
final list = e.value as List<String>;
|
|
return Card(
|
|
child: InkWell(
|
|
onTap: () => setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
keyLabel,
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () =>
|
|
setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
icon: Icon(expanded
|
|
? Icons.keyboard_arrow_up_rounded
|
|
: Icons.keyboard_arrow_down_rounded),
|
|
)
|
|
],
|
|
),
|
|
if (expanded) ...{
|
|
const SizedBox(height: 6),
|
|
...list.map(
|
|
(genre) => Row(
|
|
children: [
|
|
Text(genre.toString()),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () => ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, list..remove(genre)),
|
|
),
|
|
icon: const Icon(Icons.remove_rounded))
|
|
],
|
|
),
|
|
),
|
|
OutlinedTextField(
|
|
label: "Add",
|
|
controller: TextEditingController(),
|
|
onSubmitted: (value) {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, list..add(value)),
|
|
);
|
|
},
|
|
)
|
|
},
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
List<Person> _ => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 21),
|
|
child: Builder(builder: (context) {
|
|
final expanded = expandedKeys.contains(e.key);
|
|
final list = e.value as List<Person>;
|
|
|
|
List<Map<String, dynamic>> listToMap(List<Person> people) {
|
|
return people.map((e) => e.toPerson().toJson()).toList();
|
|
}
|
|
|
|
return Card(
|
|
child: InkWell(
|
|
onTap: () => setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
keyLabel,
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () =>
|
|
setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
icon: Icon(expanded
|
|
? Icons.keyboard_arrow_up_rounded
|
|
: Icons.keyboard_arrow_down_rounded),
|
|
)
|
|
],
|
|
),
|
|
if (expanded) ...{
|
|
const SizedBox(height: 6),
|
|
...list.map(
|
|
(person) => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
|
child: Row(
|
|
children: [
|
|
SizedBox(
|
|
height: 50,
|
|
width: 50,
|
|
child: Card(
|
|
elevation: 2,
|
|
color: Theme.of(context).colorScheme.onPrimary,
|
|
child: Center(
|
|
child: Text(
|
|
person.name.getInitials(),
|
|
style: Theme.of(context)
|
|
.textTheme
|
|
.bodyMedium
|
|
?.copyWith(fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 6),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(person.name),
|
|
Opacity(
|
|
opacity: 0.65,
|
|
child: Text(person.role.isNotEmpty
|
|
? "${person.role} (${person.type}) "
|
|
: person.type?.value ?? ""),
|
|
),
|
|
],
|
|
),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, listToMap(list..remove(person))));
|
|
},
|
|
icon: const Icon(Icons.remove_rounded))
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: OutlinedTextField(
|
|
label: "Name",
|
|
controller: personName,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Flexible(
|
|
child: EnumBox<PersonKind>(
|
|
current: personType.name.toUpperCaseSplit(),
|
|
itemBuilder: (context) => [
|
|
...PersonKind.values
|
|
.whereNot(
|
|
(element) => element == PersonKind.swaggerGeneratedUnknown)
|
|
.map(
|
|
(entry) => PopupMenuItem(
|
|
child: Text(entry.name.toUpperCaseSplit()),
|
|
onTap: () {
|
|
setState(() {
|
|
personType = entry;
|
|
});
|
|
},
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
IconButton(
|
|
onPressed: () {
|
|
ref.read(editItemProvider.notifier).updateField(MapEntry(
|
|
e.key,
|
|
listToMap(list
|
|
..add(
|
|
Person(
|
|
id: jellyId,
|
|
name: personName.text,
|
|
type: personType,
|
|
role: personRole.text,
|
|
),
|
|
))));
|
|
setState(() {
|
|
personName.text = "";
|
|
personType = PersonKind.actor;
|
|
personRole.text = "";
|
|
});
|
|
},
|
|
icon: const Icon(Icons.add_rounded),
|
|
)
|
|
],
|
|
),
|
|
},
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
List<ExternalUrls> _ => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 21),
|
|
child: Builder(builder: (context) {
|
|
final expanded = expandedKeys.contains(e.key);
|
|
final list = e.value as List<ExternalUrls>;
|
|
final name = TextEditingController();
|
|
final url = TextEditingController();
|
|
return Card(
|
|
child: InkWell(
|
|
onTap: () => setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
keyLabel,
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () =>
|
|
setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
icon: Icon(expanded
|
|
? Icons.keyboard_arrow_up_rounded
|
|
: Icons.keyboard_arrow_down_rounded),
|
|
)
|
|
],
|
|
),
|
|
if (expanded) ...{
|
|
const SizedBox(height: 6),
|
|
...list.map(
|
|
(externalUrl) => Row(
|
|
children: [
|
|
Text(externalUrl.name),
|
|
const Spacer(),
|
|
Tooltip(
|
|
message: "Open in browser",
|
|
child: IconButton(
|
|
onPressed: () => launchUrl(context, externalUrl.url),
|
|
icon: const Icon(Icons.open_in_browser_rounded)),
|
|
),
|
|
IconButton(
|
|
onPressed: () {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(
|
|
e.key,
|
|
(list..remove(externalUrl))
|
|
.map((e) => e.toMap())
|
|
.toList()),
|
|
);
|
|
},
|
|
icon: const Icon(Icons.remove_rounded))
|
|
],
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Flexible(
|
|
child: OutlinedTextField(
|
|
label: "Name",
|
|
controller: name,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Flexible(
|
|
child: OutlinedTextField(
|
|
label: "Url",
|
|
controller: url,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
IconButton(
|
|
onPressed: () {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(
|
|
e.key,
|
|
(list
|
|
..add(
|
|
ExternalUrls(name: name.text, url: url.text),
|
|
))
|
|
.map((e) => e.toMap())
|
|
.toList()),
|
|
);
|
|
},
|
|
icon: const Icon(Icons.add_rounded),
|
|
)
|
|
],
|
|
),
|
|
},
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
List<Studio> _ => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 21),
|
|
child: Builder(builder: (context) {
|
|
final expanded = expandedKeys.contains(e.key);
|
|
|
|
final list = e.value as List<Studio>;
|
|
|
|
void setMapping(List<Studio> newList) {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, newList.map((e) => e.toMap()).toList()),
|
|
);
|
|
}
|
|
|
|
return Card(
|
|
child: InkWell(
|
|
onTap: () => setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
keyLabel,
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () =>
|
|
setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
icon: Icon(expanded
|
|
? Icons.keyboard_arrow_up_rounded
|
|
: Icons.keyboard_arrow_down_rounded),
|
|
)
|
|
],
|
|
),
|
|
if (expanded) ...[
|
|
const SizedBox(height: 6),
|
|
...list.map(
|
|
(studio) => Row(
|
|
children: [
|
|
Text(studio.name),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: () => setMapping(list..remove(studio)),
|
|
icon: const Icon(Icons.remove_rounded))
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 6),
|
|
OutlinedTextField(
|
|
label: "Add",
|
|
controller: TextEditingController(),
|
|
onSubmitted: (value) =>
|
|
setMapping(list..add(Studio(id: jellyId, name: value))),
|
|
)
|
|
]
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
int value => Builder(builder: (context) {
|
|
final controller = currentEditingKey == e.key
|
|
? currentController
|
|
: TextEditingController(text: value.toString());
|
|
return FocusedOutlinedTextField(
|
|
label: switch (e.key) {
|
|
"IndexNumber" => "Episode Number",
|
|
"ParentIndexNumber" => "Season Number",
|
|
_ => keyLabel,
|
|
},
|
|
controller: controller,
|
|
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
|
onFocus: (focused) {
|
|
if (focused) {
|
|
currentController = controller;
|
|
currentEditingKey = e.key;
|
|
} else {
|
|
currentController = null;
|
|
currentEditingKey = null;
|
|
}
|
|
},
|
|
onSubmitted: (value) {
|
|
final newYear = int.tryParse(value);
|
|
if (newYear != null) {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, newYear),
|
|
);
|
|
}
|
|
},
|
|
keyboardType: TextInputType.number,
|
|
onChanged: (value) {
|
|
if (currentEditingKey != e.key) {
|
|
currentEditingKey = e.key;
|
|
currentController = controller;
|
|
}
|
|
final newYear = int.tryParse(value);
|
|
if (newYear != null) {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, newYear),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
}),
|
|
double value => Builder(builder: (context) {
|
|
final controller = currentEditingKey == e.key
|
|
? currentController
|
|
: TextEditingController(text: value.toString());
|
|
return FocusedOutlinedTextField(
|
|
label: keyLabel,
|
|
controller: controller,
|
|
onFocus: (focused) {
|
|
if (focused) {
|
|
currentController = controller;
|
|
currentEditingKey = e.key;
|
|
} else {
|
|
currentController = null;
|
|
currentEditingKey = null;
|
|
}
|
|
},
|
|
onSubmitted: (newValue) {
|
|
final newRating = double.tryParse(newValue);
|
|
if (newRating != null) {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, newRating),
|
|
);
|
|
} else {
|
|
controller?.text = value.toString();
|
|
}
|
|
currentController = null;
|
|
},
|
|
keyboardType: TextInputType.number,
|
|
);
|
|
}),
|
|
DateTime _ => Row(
|
|
children: [
|
|
Flexible(
|
|
child: FocusedOutlinedTextField(
|
|
label: keyLabel,
|
|
onTap: () async {
|
|
FocusScope.of(context).requestFocus(FocusNode());
|
|
final newDate = await showAdaptiveDatePicker(
|
|
context,
|
|
initialDateTime: e.value,
|
|
);
|
|
if (newDate == null) return;
|
|
ref
|
|
.read(editItemProvider.notifier)
|
|
.updateField(MapEntry(e.key, newDate.toIso8601String()));
|
|
},
|
|
controller:
|
|
TextEditingController(text: DateFormat.yMMMEd().format((e.value as DateTime))),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
IconButton(
|
|
onPressed: () async {
|
|
final newDate = await showDatePicker(
|
|
context: context,
|
|
currentDate: DateTime.now(),
|
|
initialDate: e.value,
|
|
firstDate: DateTime(1950),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
if (newDate == null) return;
|
|
ref
|
|
.read(editItemProvider.notifier)
|
|
.updateField(MapEntry(e.key, newDate.toIso8601String()));
|
|
},
|
|
icon: const Icon(IconsaxOutline.calendar_2))
|
|
],
|
|
),
|
|
DisplayOrder _ => Builder(builder: (context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SettingsListTile(
|
|
label: Text(keyLabel),
|
|
trailing: EnumBox(
|
|
current: (e.value as DisplayOrder).value.toUpperCaseSplit(),
|
|
itemBuilder: (context) => DisplayOrder.values
|
|
.map(
|
|
(mapEntry) => PopupMenuItem(
|
|
child: Text(mapEntry.value.toUpperCaseSplit()),
|
|
onTap: () => ref
|
|
.read(editItemProvider.notifier)
|
|
.updateField(MapEntry(e.key, mapEntry.value)),
|
|
),
|
|
)
|
|
.toList(),
|
|
),
|
|
),
|
|
const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
|
child: Text("Order episodes by air date, DVD order, or absolute numbering."),
|
|
)
|
|
],
|
|
);
|
|
}),
|
|
ShowStatus _ => Builder(builder: (context) {
|
|
return SettingsListTile(
|
|
label: Text(keyLabel),
|
|
trailing: EnumBox(
|
|
current: (e.value as ShowStatus).value,
|
|
itemBuilder: (context) => ShowStatus.values
|
|
.map(
|
|
(mapEntry) => PopupMenuItem(
|
|
child: Text(mapEntry.value),
|
|
onTap: () => ref
|
|
.read(editItemProvider.notifier)
|
|
.updateField(MapEntry(e.key, mapEntry.value)),
|
|
),
|
|
)
|
|
.toList(),
|
|
),
|
|
);
|
|
}),
|
|
bool _ => SettingsListTile(
|
|
label: Text(keyLabel),
|
|
trailing: Switch.adaptive(
|
|
value: e.value as bool,
|
|
onChanged: (value) =>
|
|
ref.read(editItemProvider.notifier).updateField(MapEntry(e.key, value)),
|
|
),
|
|
),
|
|
Duration _ => Builder(builder: (context) {
|
|
final valueInMinutes = (e.value as Duration).inMinutes.toString();
|
|
final controller = currentEditingKey == e.key
|
|
? currentController
|
|
: TextEditingController(text: valueInMinutes);
|
|
return FocusedOutlinedTextField(
|
|
label: keyLabel,
|
|
controller: controller,
|
|
onFocus: (focused) {
|
|
if (focused) {
|
|
currentController = controller;
|
|
currentEditingKey = e.key;
|
|
} else {
|
|
currentController = null;
|
|
currentEditingKey = null;
|
|
}
|
|
},
|
|
keyboardType: TextInputType.number,
|
|
onSubmitted: (value) {
|
|
final newMinutes = int.tryParse(value);
|
|
if (newMinutes != null) {
|
|
ref.read(editItemProvider.notifier).updateField(
|
|
MapEntry(e.key, Duration(minutes: newMinutes).inMilliseconds * 10000),
|
|
);
|
|
} else {
|
|
controller?.text = valueInMinutes;
|
|
}
|
|
},
|
|
);
|
|
}),
|
|
Map<EditorLockedFields, bool> _ => Builder(builder: (context) {
|
|
final map = e.value as Map<EditorLockedFields, bool>;
|
|
return Card(
|
|
child: InkWell(
|
|
onTap: () => setState(() => expandedKeys = expandedKeys.toggle(e.key)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(keyLabel, style: Theme.of(context).textTheme.titleLarge),
|
|
const SizedBox(height: 6),
|
|
const Text(
|
|
"Uncheck a field to lock it and prevent its data from being changed.",
|
|
),
|
|
const SizedBox(height: 6),
|
|
Column(
|
|
children: map.entries
|
|
.map((values) => Row(
|
|
children: [
|
|
Text(values.key.value),
|
|
const Spacer(),
|
|
Switch.adaptive(
|
|
value: !values.value,
|
|
onChanged: (value) {
|
|
final newEntries = map;
|
|
newEntries.update(values.key, (value) => !value);
|
|
final newValues = newEntries.entries
|
|
.where((element) => element.value == true)
|
|
.map((e) => e.key.value);
|
|
ref
|
|
.read(editItemProvider.notifier)
|
|
.updateField(MapEntry(e.key, newValues.toList()));
|
|
},
|
|
)
|
|
],
|
|
))
|
|
.toList(),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
String value => Builder(builder: (context) {
|
|
final controller =
|
|
currentEditingKey == e.key ? currentController : TextEditingController(text: value);
|
|
return FocusedOutlinedTextField(
|
|
label: keyLabel,
|
|
maxLines: e.key == "Overview" ? 5 : 1,
|
|
controller: controller,
|
|
onFocus: (focused) {
|
|
if (focused) {
|
|
currentEditingKey = e.key;
|
|
currentController = controller;
|
|
} else {
|
|
currentController = null;
|
|
currentEditingKey = null;
|
|
}
|
|
},
|
|
onSubmitted: (value) =>
|
|
ref.read(editItemProvider.notifier).updateField(MapEntry(e.key, value)),
|
|
onChanged: (value) {
|
|
if (currentEditingKey != e.key) {
|
|
currentEditingKey = e.key;
|
|
currentController = controller;
|
|
}
|
|
return ref.read(editItemProvider.notifier).updateField(MapEntry(e.key, value));
|
|
},
|
|
);
|
|
}),
|
|
_ => Text("Not supported ${e.value.runtimeType}: ${e.value}"),
|
|
},
|
|
);
|
|
},
|
|
)
|
|
else
|
|
const Padding(
|
|
padding: EdgeInsets.all(8.0),
|
|
child: Center(
|
|
child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|