♻️ Refactored cloud file picker, stickers

This commit is contained in:
2025-10-12 02:21:39 +08:00
parent c660a419e2
commit bbb07d574a
5 changed files with 485 additions and 482 deletions

View File

@@ -43,7 +43,6 @@ import 'package:island/screens/chat/search_messages.dart';
import 'package:island/screens/creators/hub.dart'; import 'package:island/screens/creators/hub.dart';
import 'package:island/screens/creators/posts/post_manage_list.dart'; import 'package:island/screens/creators/posts/post_manage_list.dart';
import 'package:island/screens/creators/stickers/stickers.dart'; import 'package:island/screens/creators/stickers/stickers.dart';
import 'package:island/screens/creators/stickers/pack_detail.dart';
import 'package:island/screens/stickers/sticker_marketplace.dart'; import 'package:island/screens/stickers/sticker_marketplace.dart';
import 'package:island/screens/stickers/pack_detail.dart'; import 'package:island/screens/stickers/pack_detail.dart';
import 'package:island/screens/discovery/feeds/feed_marketplace.dart'; import 'package:island/screens/discovery/feeds/feed_marketplace.dart';
@@ -232,49 +231,6 @@ final routerProvider = Provider<GoRouter>((ref) {
return StickersScreen(pubName: name); return StickersScreen(pubName: name);
}, },
), ),
GoRoute(
name: 'creatorStickerPackNew',
path: ':name/stickers/new',
builder: (context, state) {
final name = state.pathParameters['name']!;
return NewStickerPacksScreen(pubName: name);
},
),
GoRoute(
name: 'creatorStickerPackEdit',
path: ':name/stickers/:packId/edit',
builder: (context, state) {
final name = state.pathParameters['name']!;
final packId = state.pathParameters['packId']!;
return EditStickerPacksScreen(pubName: name, packId: packId);
},
),
GoRoute(
name: 'creatorStickerPackDetail',
path: ':name/stickers/:packId',
builder: (context, state) {
final name = state.pathParameters['name']!;
final packId = state.pathParameters['packId']!;
return StickerPackDetailScreen(pubName: name, id: packId);
},
),
GoRoute(
name: 'creatorStickerNew',
path: ':name/stickers/:packId/new',
builder: (context, state) {
final packId = state.pathParameters['packId']!;
return NewStickersScreen(packId: packId);
},
),
GoRoute(
name: 'creatorStickerEdit',
path: ':name/stickers/:packId/:id/edit',
builder: (context, state) {
final packId = state.pathParameters['packId']!;
final id = state.pathParameters['id']!;
return EditStickersScreen(id: id, packId: packId);
},
),
GoRoute( GoRoute(
name: 'creatorNew', name: 'creatorNew',
path: 'new', path: 'new',

View File

@@ -8,13 +8,14 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart';
import 'package:island/models/sticker.dart'; import 'package:island/models/sticker.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/screens/creators/stickers/stickers.dart'; import 'package:island/screens/creators/stickers/stickers.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_file_picker.dart'; import 'package:island/widgets/content/cloud_file_picker.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -33,13 +34,13 @@ Future<List<SnSticker>> stickerPackContent(Ref ref, String packId) async {
.toList(); .toList();
} }
class StickerPackDetailScreen extends HookConsumerWidget { class StickerPackDetailContent extends HookConsumerWidget {
final String id; final String id;
final String pubName; final String pubName;
const StickerPackDetailScreen({ const StickerPackDetailContent({
super.key, super.key,
required this.pubName,
required this.id, required this.id,
required this.pubName,
}); });
@override @override
@@ -67,35 +68,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
} }
} }
return AppScaffold( return pack.when(
isNoBackground: false,
appBar: AppBar(
title: Text(pack.value?.name ?? 'loading'.tr()),
actions: [
IconButton(
icon: const Icon(Symbols.add_circle),
onPressed: () {
context
.pushNamed(
'creatorStickerNew',
pathParameters: {'name': pubName, 'packId': id},
)
.then((value) {
if (value != null) {
ref.invalidate(stickerPackContentProvider(id));
}
});
},
),
_StickerPackActionMenu(
pubName: pubName,
packId: id,
iconShadow: Shadow(),
),
const Gap(8),
],
),
body: pack.when(
data: data:
(pack) => Column( (pack) => Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -125,10 +98,13 @@ class StickerPackDetailScreen extends HookConsumerWidget {
spacing: 4, spacing: 4,
children: [ children: [
const Icon(Symbols.tag, size: 16), const Icon(Symbols.tag, size: 16),
SelectableText( Flexible(
child: SelectableText(
pack.id, pack.id,
maxLines: 1,
style: GoogleFonts.robotoMono(), style: GoogleFonts.robotoMono(),
), ),
),
], ],
).opacity(0.85), ).opacity(0.85),
], ],
@@ -149,7 +125,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
), ),
gridDelegate: gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent( const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 48, maxCrossAxisExtent: 80,
mainAxisSpacing: 8, mainAxisSpacing: 8,
crossAxisSpacing: 8, crossAxisSpacing: 8,
), ),
@@ -167,7 +143,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
Clipboard.setData( Clipboard.setData(
ClipboardData( ClipboardData(
text: text:
':${pack.prefix}${sticker.slug}:', ':${pack.prefix}+${sticker.slug}:',
), ),
); );
}, },
@@ -177,21 +153,21 @@ class StickerPackDetailScreen extends HookConsumerWidget {
title: 'edit'.tr(), title: 'edit'.tr(),
image: MenuImage.icon(Symbols.edit), image: MenuImage.icon(Symbols.edit),
callback: () { callback: () {
context showModalBottomSheet(
.pushNamed( context: context,
'creatorStickerEdit', isScrollControlled: true,
pathParameters: { builder:
'name': pubName, (context) => SheetScaffold(
'packId': id, titleText: 'editSticker'.tr(),
'id': sticker.id, child: StickerForm(
}, packId: id,
) id: sticker.id,
.then((value) { ),
),
).then((value) {
if (value != null) { if (value != null) {
ref.invalidate( ref.invalidate(
stickerPackContentProvider( stickerPackContentProvider(id),
id,
),
); );
} }
}); });
@@ -223,6 +199,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
), ),
child: CloudImageWidget( child: CloudImageWidget(
fileId: sticker.imageId, fileId: sticker.imageId,
fit: BoxFit.contain,
), ),
), ),
), ),
@@ -244,17 +221,17 @@ class StickerPackDetailScreen extends HookConsumerWidget {
(err, _) => (err, _) =>
Text('Error: $err').textAlignment(TextAlign.center).center(), Text('Error: $err').textAlignment(TextAlign.center).center(),
loading: () => const CircularProgressIndicator().center(), loading: () => const CircularProgressIndicator().center(),
),
); );
} }
} }
class _StickerPackActionMenu extends HookConsumerWidget { class StickerPackActionMenu extends HookConsumerWidget {
final String pubName; final String pubName;
final String packId; final String packId;
final Shadow iconShadow; final Shadow iconShadow;
const _StickerPackActionMenu({ const StickerPackActionMenu({
super.key,
required this.pubName, required this.pubName,
required this.packId, required this.packId,
required this.iconShadow, required this.iconShadow,
@@ -268,7 +245,22 @@ class _StickerPackActionMenu extends HookConsumerWidget {
(context) => [ (context) => [
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
context.push('/creators/$pubName/stickers/$packId/edit'); showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'editStickerPack'.tr(),
child: StickerPackForm(
pubName: pubName,
packId: packId,
),
),
).then((value) {
if (value != null) {
ref.invalidate(stickerPackProvider(packId));
}
});
}, },
child: Row( child: Row(
children: [ children: [
@@ -311,42 +303,10 @@ class _StickerPackActionMenu extends HookConsumerWidget {
} }
} }
@freezed class StickerForm extends HookConsumerWidget {
sealed class StickerWithPackQuery with _$StickerWithPackQuery {
const factory StickerWithPackQuery({
required String packId,
required String id,
}) = _StickerWithPackQuery;
}
@riverpod
Future<SnSticker?> stickerPackSticker(
Ref ref,
StickerWithPackQuery? query,
) async {
if (query == null) return null;
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get(
'/sphere/stickers/${query.packId}/content/${query.id}',
);
if (resp.data == null) return null;
return SnSticker.fromJson(resp.data);
}
class NewStickersScreen extends StatelessWidget {
final String packId;
const NewStickersScreen({super.key, required this.packId});
@override
Widget build(BuildContext context) {
return EditStickersScreen(packId: packId, id: null);
}
}
class EditStickersScreen extends HookConsumerWidget {
final String packId; final String packId;
final String? id; final String? id;
const EditStickersScreen({super.key, required this.packId, required this.id}); const StickerForm({super.key, required this.packId, this.id});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -396,17 +356,16 @@ class EditStickersScreen extends HookConsumerWidget {
} }
} }
return AppScaffold( return Column(
isNoBackground: false,
appBar: AppBar(
title: Text(id == null ? 'createSticker' : 'editSticker').tr(),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
SizedBox( SizedBox(
height: 96, height: 80,
width: 96, width: 80,
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8)), borderRadius: BorderRadius.all(Radius.circular(8)),
child: Container( child: Container(
@@ -421,45 +380,39 @@ class EditStickersScreen extends HookConsumerWidget {
), ),
), ),
), ),
const Gap(16), IconButton.filledTonal(
Form( onPressed: () {
key: formKey,
child: Column(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: imageController,
decoration: InputDecoration(
labelText: 'stickerImage'.tr(),
border: const UnderlineInputBorder(),
suffix: InkWell(
onTap: () {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => CloudFilePicker(), builder:
(context) => CloudFilePicker(
allowedTypes: {UniversalFileType.image},
),
).then((value) { ).then((value) {
if (value == null) return; if (value == null) return;
image.value = value[0].id; image.value = value[0].id;
imageController.text = image.value!; imageController.text = image.value!;
}); });
}, },
borderRadius: BorderRadius.all(Radius.circular(8)), icon: const Icon(Symbols.cloud_upload),
child: const Icon(
Symbols.cloud_upload,
).padding(horizontal: 4),
), ),
],
), ),
readOnly: true, const Gap(16),
onTapOutside: Form(
(_) => FocusManager.instance.primaryFocus?.unfocus(), key: formKey,
), child: Column(
spacing: 12,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField( TextFormField(
controller: slugController, controller: slugController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'stickerSlug'.tr(), labelText: 'stickerSlug'.tr(),
helperText: 'stickerSlugHint'.tr(), helperText: 'stickerSlugHint'.tr(),
border: const UnderlineInputBorder(), border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
), ),
onTapOutside: onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(), (_) => FocusManager.instance.primaryFocus?.unfocus(),
@@ -477,7 +430,28 @@ class EditStickersScreen extends HookConsumerWidget {
), ),
), ),
], ],
).padding(horizontal: 24, vertical: 24), ).padding(horizontal: 24, vertical: 16);
);
} }
} }
@freezed
sealed class StickerWithPackQuery with _$StickerWithPackQuery {
const factory StickerWithPackQuery({
required String packId,
required String id,
}) = _StickerWithPackQuery;
}
@riverpod
Future<SnSticker?> stickerPackSticker(
Ref ref,
StickerWithPackQuery? query,
) async {
if (query == null) return null;
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get(
'/sphere/stickers/${query.packId}/content/${query.id}',
);
if (resp.data == null) return null;
return SnSticker.fromJson(resp.data);
}

View File

@@ -1,14 +1,16 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/sticker.dart'; import 'package:island/models/sticker.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:island/screens/creators/stickers/pack_detail.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -22,30 +24,50 @@ class StickersScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final content = SliverStickerPacksList(pubName: pubName);
return AppScaffold( return AppScaffold(
isNoBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: const Text('stickers').tr(), title: const Text('stickers').tr(),
actions: [ actions: [const Gap(8)],
IconButton( ),
floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () {
context showModalBottomSheet(
.pushNamed( context: context,
'creatorStickerPackNew', isScrollControlled: true,
pathParameters: {'name': pubName}, builder:
) (context) => SheetScaffold(
.then((value) { titleText: 'createStickerPack'.tr(),
child: StickerPackForm(pubName: pubName),
),
).then((value) {
if (value != null) { if (value != null) {
ref.invalidate(stickerPacksNotifierProvider(pubName)); ref.invalidate(stickerPacksNotifierProvider(pubName));
} }
}); });
}, },
icon: const Icon(Symbols.add_circle), child: const Icon(Symbols.add),
), ),
const Gap(8), body:
], isWideScreen(context)
? Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
), ),
body: SliverStickerPacksList(pubName: pubName), ),
margin: const EdgeInsets.only(top: 16),
child: content,
),
),
)
: content,
); );
} }
} }
@@ -71,13 +93,52 @@ class SliverStickerPacksList extends HookConsumerWidget {
final sticker = data.items[index]; final sticker = data.items[index];
return ListTile( return ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
title: Text(sticker.name), title: Text(sticker.name),
subtitle: Text(sticker.description), subtitle: Text(sticker.description),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
onTap: () { onTap: () {
context.pushNamed( showModalBottomSheet(
'creatorStickerPackDetail', context: context,
pathParameters: {'name': pubName, 'packId': sticker.id}, isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: sticker.name,
actions: [
IconButton(
icon: const Icon(Symbols.add_circle),
onPressed: () {
final id = sticker.id;
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'createSticker'.tr(),
child: StickerForm(packId: id),
),
).then((value) {
if (value != null) {
ref.invalidate(
stickerPackContentProvider(id),
);
}
});
},
),
StickerPackActionMenu(
pubName: pubName,
packId: sticker.id,
iconShadow: Shadow(),
),
],
child: StickerPackDetailContent(
id: sticker.id,
pubName: pubName,
),
),
); );
}, },
); );
@@ -136,20 +197,10 @@ Future<SnStickerPack?> stickerPack(Ref ref, String? packId) async {
return SnStickerPack.fromJson(resp.data); return SnStickerPack.fromJson(resp.data);
} }
class NewStickerPacksScreen extends HookConsumerWidget { class StickerPackForm extends HookConsumerWidget {
final String pubName;
const NewStickerPacksScreen({super.key, required this.pubName});
@override
Widget build(BuildContext context, WidgetRef ref) {
return EditStickerPacksScreen(pubName: pubName);
}
}
class EditStickerPacksScreen extends HookConsumerWidget {
final String pubName; final String pubName;
final String? packId; final String? packId;
const EditStickerPacksScreen({super.key, required this.pubName, this.packId}); const StickerPackForm({super.key, required this.pubName, this.packId});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -188,7 +239,7 @@ class EditStickerPacksScreen extends HookConsumerWidget {
options: Options(method: packId == null ? 'POST' : 'PATCH'), options: Options(method: packId == null ? 'POST' : 'PATCH'),
); );
if (!context.mounted) return; if (!context.mounted) return;
context.pop(SnStickerPack.fromJson(resp.data)); Navigator.of(context).pop(SnStickerPack.fromJson(resp.data));
} catch (err) { } catch (err) {
showErrorAlert(err); showErrorAlert(err);
} finally { } finally {
@@ -196,25 +247,21 @@ class EditStickerPacksScreen extends HookConsumerWidget {
} }
} }
return AppScaffold( return Column(
isNoBackground: false,
appBar: AppBar(
title:
Text(packId == null ? 'createStickerPack' : 'editStickerPack').tr(),
),
body: Column(
children: [ children: [
Form( Form(
key: formKey, key: formKey,
child: Column( child: Column(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
spacing: 16,
children: [ children: [
TextFormField( TextFormField(
controller: nameController, controller: nameController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'name'.tr(), labelText: 'name'.tr(),
border: const UnderlineInputBorder(), border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
), ),
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@@ -229,7 +276,9 @@ class EditStickerPacksScreen extends HookConsumerWidget {
controller: descriptionController, controller: descriptionController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'description'.tr(), labelText: 'description'.tr(),
border: const UnderlineInputBorder(), border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
alignLabelWithHint: true, alignLabelWithHint: true,
), ),
minLines: 3, minLines: 3,
@@ -241,8 +290,10 @@ class EditStickerPacksScreen extends HookConsumerWidget {
controller: prefixController, controller: prefixController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'stickerPackPrefix'.tr(), labelText: 'stickerPackPrefix'.tr(),
border: const UnderlineInputBorder(), border: const OutlineInputBorder(
helperText: 'deleteStickerHint'.tr(), borderRadius: BorderRadius.all(Radius.circular(12)),
),
helperText: 'stickerPackPrefixHint'.tr(),
), ),
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@@ -266,7 +317,6 @@ class EditStickerPacksScreen extends HookConsumerWidget {
), ),
), ),
], ],
).padding(horizontal: 24, vertical: 16), ).padding(horizontal: 24, vertical: 16);
);
} }
} }

View File

@@ -15,7 +15,16 @@ import 'package:styled_widget/styled_widget.dart';
class CloudFilePicker extends HookConsumerWidget { class CloudFilePicker extends HookConsumerWidget {
final bool allowMultiple; final bool allowMultiple;
const CloudFilePicker({super.key, this.allowMultiple = false}); final Set<UniversalFileType> allowedTypes;
const CloudFilePicker({
super.key,
this.allowMultiple = false,
this.allowedTypes = const {
UniversalFileType.image,
UniversalFileType.video,
UniversalFileType.file,
},
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -71,7 +80,7 @@ class CloudFilePicker extends HookConsumerWidget {
void pickFile() async { void pickFile() async {
showLoadingModal(context); showLoadingModal(context);
final result = await FilePickerIO().pickFiles( final result = await FilePicker.platform.pickFiles(
allowMultiple: allowMultiple, allowMultiple: allowMultiple,
); );
if (result == null) { if (result == null) {
@@ -80,9 +89,13 @@ class CloudFilePicker extends HookConsumerWidget {
} }
final newFiles = final newFiles =
result.files result.files.map((e) {
.map((e) => UniversalFile(data: e, type: UniversalFileType.file)) final xfile =
.toList(); e.bytes != null
? XFile.fromData(e.bytes!, name: e.name)
: XFile(e.path!);
return UniversalFile(data: xfile, type: UniversalFileType.file);
}).toList();
if (!allowMultiple) { if (!allowMultiple) {
files.value = newFiles; files.value = newFiles;
@@ -99,23 +112,23 @@ class CloudFilePicker extends HookConsumerWidget {
void pickImage() async { void pickImage() async {
showLoadingModal(context); showLoadingModal(context);
final result = final result = await FilePicker.platform.pickFiles(
allowMultiple allowMultiple: allowMultiple,
? await ref.read(imagePickerProvider).pickMultiImage() type: FileType.image,
: [ );
await ref if (result == null || result.files.isEmpty) {
.read(imagePickerProvider)
.pickImage(source: ImageSource.gallery),
];
if (result.isEmpty) {
if (context.mounted) hideLoadingModal(context); if (context.mounted) hideLoadingModal(context);
return; return;
} }
final newFiles = final newFiles =
result result.files.map((e) {
.map((e) => UniversalFile(data: e, type: UniversalFileType.image)) final xfile =
.toList(); e.bytes != null
? XFile.fromData(e.bytes!, name: e.name)
: XFile(e.path!);
return UniversalFile(data: xfile, type: UniversalFileType.image);
}).toList();
if (!allowMultiple) { if (!allowMultiple) {
files.value = newFiles; files.value = newFiles;
@@ -132,21 +145,26 @@ class CloudFilePicker extends HookConsumerWidget {
void pickVideo() async { void pickVideo() async {
showLoadingModal(context); showLoadingModal(context);
final result = await ref final result = await FilePicker.platform.pickFiles(
.read(imagePickerProvider) allowMultiple: allowMultiple,
.pickVideo(source: ImageSource.gallery); type: FileType.video,
if (result == null) { );
if (result == null || result.files.isEmpty) {
if (context.mounted) hideLoadingModal(context); if (context.mounted) hideLoadingModal(context);
return; return;
} }
final newFile = UniversalFile( final newFiles =
data: result, result.files.map((e) {
type: UniversalFileType.video, final xfile =
); e.bytes != null
? XFile.fromData(e.bytes!, name: e.name)
: XFile(e.path!);
return UniversalFile(data: xfile, type: UniversalFileType.video);
}).toList();
if (!allowMultiple) { if (!allowMultiple) {
files.value = [newFile]; files.value = newFiles;
if (context.mounted) { if (context.mounted) {
hideLoadingModal(context); hideLoadingModal(context);
startUpload(); startUpload();
@@ -154,7 +172,7 @@ class CloudFilePicker extends HookConsumerWidget {
return; return;
} }
files.value = [...files.value, newFile]; files.value = [...files.value, ...newFiles];
if (context.mounted) hideLoadingModal(context); if (context.mounted) hideLoadingModal(context);
} }
@@ -252,6 +270,7 @@ class CloudFilePicker extends HookConsumerWidget {
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
child: Column( child: Column(
children: [ children: [
if (allowedTypes.contains(UniversalFileType.image))
ListTile( ListTile(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@@ -260,6 +279,7 @@ class CloudFilePicker extends HookConsumerWidget {
title: Text('addPhoto'.tr()), title: Text('addPhoto'.tr()),
onTap: () => pickImage(), onTap: () => pickImage(),
), ),
if (allowedTypes.contains(UniversalFileType.video))
ListTile( ListTile(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@@ -268,6 +288,7 @@ class CloudFilePicker extends HookConsumerWidget {
title: Text('addVideo'.tr()), title: Text('addVideo'.tr()),
onTap: () => pickVideo(), onTap: () => pickVideo(),
), ),
if (allowedTypes.contains(UniversalFileType.file))
ListTile( ListTile(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),

View File

@@ -30,6 +30,8 @@ class SheetScaffold extends StatelessWidget {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
letterSpacing: -0.5, letterSpacing: -0.5,
), ),
maxLines: 1,
overflow: TextOverflow.ellipsis,
); );
return Container( return Container(
@@ -43,7 +45,7 @@ class SheetScaffold extends StatelessWidget {
padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12), padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12),
child: Row( child: Row(
children: [ children: [
titleWidget, Expanded(child: titleWidget),
const Spacer(), const Spacer(),
...actions, ...actions,
IconButton( IconButton(