✨ Basic sticker management
This commit is contained in:
parent
ea434815cf
commit
03f2470dae
122
lib/models/stickers.dart
Normal file
122
lib/models/stickers.dart
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import 'package:solian/models/account.dart';
|
||||||
|
import 'package:solian/models/attachment.dart';
|
||||||
|
|
||||||
|
class Sticker {
|
||||||
|
int id;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
String alias;
|
||||||
|
String name;
|
||||||
|
int attachmentId;
|
||||||
|
Attachment attachment;
|
||||||
|
int packId;
|
||||||
|
StickerPack? pack;
|
||||||
|
int accountId;
|
||||||
|
Account account;
|
||||||
|
|
||||||
|
Sticker({
|
||||||
|
required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.attachmentId,
|
||||||
|
required this.attachment,
|
||||||
|
required this.packId,
|
||||||
|
required this.pack,
|
||||||
|
required this.accountId,
|
||||||
|
required this.account,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Sticker.fromJson(Map<String, dynamic> json) => Sticker(
|
||||||
|
id: json['id'],
|
||||||
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
|
deletedAt: json['deleted_at'] != null
|
||||||
|
? DateTime.parse(json['deleted_at'])
|
||||||
|
: json['deleted_at'],
|
||||||
|
alias: json['alias'],
|
||||||
|
name: json['name'],
|
||||||
|
attachmentId: json['attachment_id'],
|
||||||
|
attachment: Attachment.fromJson(json['attachment']),
|
||||||
|
packId: json['pack_id'],
|
||||||
|
pack: json['pack'] != null ? StickerPack.fromJson(json['pack']) : null,
|
||||||
|
accountId: json['account_id'],
|
||||||
|
account: Account.fromJson(json['account']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'created_at': createdAt.toIso8601String(),
|
||||||
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': deletedAt?.toIso8601String(),
|
||||||
|
'alias': alias,
|
||||||
|
'name': name,
|
||||||
|
'attachment_id': attachmentId,
|
||||||
|
'attachment': attachment.toJson(),
|
||||||
|
'pack_id': packId,
|
||||||
|
'account_id': accountId,
|
||||||
|
'account': account.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class StickerPack {
|
||||||
|
int id;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
String prefix;
|
||||||
|
String name;
|
||||||
|
String description;
|
||||||
|
List<Sticker>? stickers;
|
||||||
|
int accountId;
|
||||||
|
Account account;
|
||||||
|
|
||||||
|
StickerPack({
|
||||||
|
required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.prefix,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.stickers,
|
||||||
|
required this.accountId,
|
||||||
|
required this.account,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory StickerPack.fromJson(Map<String, dynamic> json) => StickerPack(
|
||||||
|
id: json['id'],
|
||||||
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
|
deletedAt: json['deleted_at'] != null
|
||||||
|
? DateTime.parse(json['deleted_at'])
|
||||||
|
: json['deleted_at'],
|
||||||
|
prefix: json['prefix'],
|
||||||
|
name: json['name'],
|
||||||
|
description: json['description'],
|
||||||
|
stickers: json['stickers'] == null
|
||||||
|
? []
|
||||||
|
: List<Sticker>.from(
|
||||||
|
json['stickers']!.map((x) => Sticker.fromJson(x))),
|
||||||
|
accountId: json['account_id'],
|
||||||
|
account: Account.fromJson(json['account']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'created_at': createdAt.toIso8601String(),
|
||||||
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': deletedAt?.toIso8601String(),
|
||||||
|
'prefix': prefix,
|
||||||
|
'name': name,
|
||||||
|
'description': description,
|
||||||
|
'stickers': stickers == null
|
||||||
|
? []
|
||||||
|
: List<dynamic>.from(stickers!.map((x) => x.toJson())),
|
||||||
|
'account_id': accountId,
|
||||||
|
'account': account.toJson(),
|
||||||
|
};
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
|
import 'package:solian/models/stickers.dart';
|
||||||
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
import 'package:solian/widgets/stickers/sticker_uploader.dart';
|
import 'package:solian/widgets/stickers/sticker_uploader.dart';
|
||||||
@ -14,13 +17,92 @@ class StickerScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _StickerScreenState extends State<StickerScreen> {
|
class _StickerScreenState extends State<StickerScreen> {
|
||||||
final PagingController<int, dynamic> _pagingController =
|
final PagingController<int, StickerPack> _pagingController =
|
||||||
PagingController(firstPageKey: 0);
|
PagingController(firstPageKey: 0);
|
||||||
|
|
||||||
Future<bool?> _promptUploadSticker() {
|
Future<bool> _promptDelete(Sticker item, String prefix) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) return false;
|
||||||
|
|
||||||
|
final confirm = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text('stickerDeletionConfirm'.tr),
|
||||||
|
content: Text(
|
||||||
|
'stickerDeletionConfirmCaption'.trParams({
|
||||||
|
'name': ':${'$prefix${item.alias}'.camelCase}:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text('cancel'.tr),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, true),
|
||||||
|
child: Text('confirm'.tr),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (confirm != true) return false;
|
||||||
|
|
||||||
|
final client = auth.configureClient('files');
|
||||||
|
final resp = await client.delete('/stickers/${item.id}');
|
||||||
|
|
||||||
|
return resp.statusCode == 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> _promptUploadSticker({Sticker? edit}) {
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const StickerUploadDialog(),
|
builder: (context) => StickerUploadDialog(
|
||||||
|
edit: edit,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmoteEntry(Sticker item, String prefix) {
|
||||||
|
final imageUrl = ServiceFinder.buildUrl(
|
||||||
|
'files',
|
||||||
|
'/attachments/${item.attachmentId}',
|
||||||
|
);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(item.name),
|
||||||
|
subtitle: Text(':${'$prefix${item.alias}'.camelCase}:'),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 16, right: 14),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.edit_square),
|
||||||
|
onPressed: () {
|
||||||
|
_promptUploadSticker(edit: item).then((value) {
|
||||||
|
if (value == true) _pagingController.refresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () {
|
||||||
|
_promptDelete(item, prefix).then((value) {
|
||||||
|
if (value == true) _pagingController.refresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
leading: PlatformInfo.canCacheImage
|
||||||
|
? CachedNetworkImage(
|
||||||
|
imageUrl: imageUrl,
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
)
|
||||||
|
: Image.network(
|
||||||
|
imageUrl,
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,13 +112,12 @@ class _StickerScreenState extends State<StickerScreen> {
|
|||||||
final name = auth.userProfile.value!['name'];
|
final name = auth.userProfile.value!['name'];
|
||||||
_pagingController.addPageRequestListener((pageKey) async {
|
_pagingController.addPageRequestListener((pageKey) async {
|
||||||
final client = ServiceFinder.configureClient('files');
|
final client = ServiceFinder.configureClient('files');
|
||||||
final resp =
|
final resp = await client.get(
|
||||||
await client.get('/stickers?take=10&offset=$pageKey&author=$name');
|
'/stickers/manifest?take=10&offset=$pageKey&author=$name',
|
||||||
|
);
|
||||||
if (resp.statusCode == 200) {
|
if (resp.statusCode == 200) {
|
||||||
final result = PaginationResult.fromJson(resp.body);
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
final out = result.data
|
final out = result.data?.map((e) => StickerPack.fromJson(e)).toList();
|
||||||
?.map((e) => e) // TODO transform object
|
|
||||||
.toList();
|
|
||||||
if (out != null && result.data!.length >= 10) {
|
if (out != null && result.data!.length >= 10) {
|
||||||
_pagingController.appendPage(out, pageKey + out.length);
|
_pagingController.appendPage(out, pageKey + out.length);
|
||||||
} else if (out != null) {
|
} else if (out != null) {
|
||||||
@ -70,11 +151,22 @@ class _StickerScreenState extends State<StickerScreen> {
|
|||||||
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
PagedSliverList(
|
PagedSliverList<int, StickerPack>(
|
||||||
pagingController: _pagingController,
|
pagingController: _pagingController,
|
||||||
builderDelegate: PagedChildBuilderDelegate(
|
builderDelegate: PagedChildBuilderDelegate(
|
||||||
itemBuilder: (BuildContext context, item, int index) {
|
itemBuilder: (BuildContext context, item, int index) {
|
||||||
return const SizedBox();
|
return ExpansionTile(
|
||||||
|
title: Text(item.name),
|
||||||
|
subtitle: Text(
|
||||||
|
item.description,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
children: item.stickers
|
||||||
|
?.map((x) => _buildEmoteEntry(x, item.prefix))
|
||||||
|
.toList() ??
|
||||||
|
List.empty(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -163,9 +163,11 @@ const i18nEnglish = {
|
|||||||
'attachmentAutoUpload': 'Auto Upload',
|
'attachmentAutoUpload': 'Auto Upload',
|
||||||
'attachmentUploadQueue': 'Upload Queue',
|
'attachmentUploadQueue': 'Upload Queue',
|
||||||
'attachmentUploadQueueStart': 'Start All',
|
'attachmentUploadQueueStart': 'Start All',
|
||||||
'attachmentUploadInProgress': 'There are attachments being uploaded. Please wait until all attachments have been uploaded before proceeding...',
|
'attachmentUploadInProgress':
|
||||||
|
'There are attachments being uploaded. Please wait until all attachments have been uploaded before proceeding...',
|
||||||
'attachmentAttached': 'Exists Files',
|
'attachmentAttached': 'Exists Files',
|
||||||
'attachmentUploadBlocked': 'Upload blocked, there is currently a task in progress...',
|
'attachmentUploadBlocked':
|
||||||
|
'Upload blocked, there is currently a task in progress...',
|
||||||
'attachmentAdd': 'Attach attachments',
|
'attachmentAdd': 'Attach attachments',
|
||||||
'attachmentAddGalleryPhoto': 'Gallery photo',
|
'attachmentAddGalleryPhoto': 'Gallery photo',
|
||||||
'attachmentAddGalleryVideo': 'Gallery video',
|
'attachmentAddGalleryVideo': 'Gallery video',
|
||||||
@ -174,7 +176,8 @@ const i18nEnglish = {
|
|||||||
'attachmentAddClipboard': 'Paste file',
|
'attachmentAddClipboard': 'Paste file',
|
||||||
'attachmentAddFile': 'Attach file',
|
'attachmentAddFile': 'Attach file',
|
||||||
'attachmentAddLink': 'Link attachments',
|
'attachmentAddLink': 'Link attachments',
|
||||||
'attachmentAddLinkHint': 'Enter attachment serial number to link that attachment',
|
'attachmentAddLinkHint':
|
||||||
|
'Enter attachment serial number to link that attachment',
|
||||||
'attachmentAddLinkInput': 'Serial number',
|
'attachmentAddLinkInput': 'Serial number',
|
||||||
'attachmentSetting': 'Adjust attachment',
|
'attachmentSetting': 'Adjust attachment',
|
||||||
'attachmentAlt': 'Alternative text',
|
'attachmentAlt': 'Alternative text',
|
||||||
@ -318,8 +321,10 @@ const i18nEnglish = {
|
|||||||
'bsCheckForUpdate': 'Checking For Updates',
|
'bsCheckForUpdate': 'Checking For Updates',
|
||||||
'bsCheckForUpdateFailed': 'Unable to Check Updates',
|
'bsCheckForUpdateFailed': 'Unable to Check Updates',
|
||||||
'bsCheckForUpdateNew': 'Found New Version',
|
'bsCheckForUpdateNew': 'Found New Version',
|
||||||
'bsCheckForUpdateDescApple': 'Please head to TestFlight and update your app to latest version to prevent error happens and get latest functions.',
|
'bsCheckForUpdateDescApple':
|
||||||
'bsCheckForUpdateDescCommon': 'Please head to our website download and install latest version of application to prevent error happens and get latest functions.',
|
'Please head to TestFlight and update your app to latest version to prevent error happens and get latest functions.',
|
||||||
|
'bsCheckForUpdateDescCommon':
|
||||||
|
'Please head to our website download and install latest version of application to prevent error happens and get latest functions.',
|
||||||
'bsCheckingServer': 'Checking Server Status',
|
'bsCheckingServer': 'Checking Server Status',
|
||||||
'bsCheckingServerFail':
|
'bsCheckingServerFail':
|
||||||
'Unable connect to server, check your network connection',
|
'Unable connect to server, check your network connection',
|
||||||
@ -337,6 +342,9 @@ const i18nEnglish = {
|
|||||||
'themeColorMiku': 'Miku Blue',
|
'themeColorMiku': 'Miku Blue',
|
||||||
'themeColorKagamine': 'Kagamine Yellow',
|
'themeColorKagamine': 'Kagamine Yellow',
|
||||||
'themeColorLuka': 'Luka Pink',
|
'themeColorLuka': 'Luka Pink',
|
||||||
|
'stickerDeletionConfirm': 'Confirm sticker delete',
|
||||||
|
'stickerDeletionConfirmCaption':
|
||||||
|
'Are you sure to delete sticker @name? This action cannot be undo.',
|
||||||
'themeColorApplied': 'Global theme color has been applied.',
|
'themeColorApplied': 'Global theme color has been applied.',
|
||||||
'attachmentSaved': 'Attachment saved to your system album.',
|
'attachmentSaved': 'Attachment saved to your system album.',
|
||||||
'cropImage': 'Crop Image',
|
'cropImage': 'Crop Image',
|
||||||
@ -344,9 +352,12 @@ const i18nEnglish = {
|
|||||||
'stickerUploaderAttachmentNew': 'Upload new attachment',
|
'stickerUploaderAttachmentNew': 'Upload new attachment',
|
||||||
'stickerUploaderAttachment': 'Attachment serial number',
|
'stickerUploaderAttachment': 'Attachment serial number',
|
||||||
'stickerUploaderPack': 'Sticker pack serial number',
|
'stickerUploaderPack': 'Sticker pack serial number',
|
||||||
'stickerUploaderPackHint': 'Don\'t have pack id? Head to creator platform and create one!',
|
'stickerUploaderPackHint':
|
||||||
|
'Don\'t have pack id? Head to creator platform and create one!',
|
||||||
'stickerUploaderAlias': 'Alias',
|
'stickerUploaderAlias': 'Alias',
|
||||||
'stickerUploaderAliasHint': 'Will be used as a placeholder with the sticker pack prefix when entered.',
|
'stickerUploaderAliasHint':
|
||||||
|
'Will be used as a placeholder with the sticker pack prefix when entered.',
|
||||||
'stickerUploaderName': 'Name',
|
'stickerUploaderName': 'Name',
|
||||||
'stickerUploaderNameHint': 'A human-friendly name given to the user in the sticker selection interface.',
|
'stickerUploaderNameHint':
|
||||||
|
'A human-friendly name given to the user in the sticker selection interface.',
|
||||||
};
|
};
|
||||||
|
@ -315,6 +315,8 @@ const i18nSimplifiedChinese = {
|
|||||||
'themeColorKagamine': '镜音黄',
|
'themeColorKagamine': '镜音黄',
|
||||||
'themeColorLuka': '流音粉',
|
'themeColorLuka': '流音粉',
|
||||||
'themeColorApplied': '全局主题颜色已应用',
|
'themeColorApplied': '全局主题颜色已应用',
|
||||||
|
'stickerDeletionConfirm': '确认删除贴图',
|
||||||
|
'stickerDeletionConfirmCaption': '你确认要删除贴图 @name 吗?该操作不可撤销。',
|
||||||
'attachmentSaved': '附件已保存到系统相册',
|
'attachmentSaved': '附件已保存到系统相册',
|
||||||
'cropImage': '裁剪图片',
|
'cropImage': '裁剪图片',
|
||||||
'stickerUploader': '上传贴图',
|
'stickerUploader': '上传贴图',
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
|
import 'package:solian/models/stickers.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
||||||
|
|
||||||
class StickerUploadDialog extends StatefulWidget {
|
class StickerUploadDialog extends StatefulWidget {
|
||||||
const StickerUploadDialog({super.key});
|
final Sticker? edit;
|
||||||
|
|
||||||
|
const StickerUploadDialog({super.key, this.edit});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StickerUploadDialog> createState() => _StickerUploadDialogState();
|
State<StickerUploadDialog> createState() => _StickerUploadDialogState();
|
||||||
@ -56,13 +59,27 @@ class _StickerUploadDialogState extends State<StickerUploadDialog> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
Response resp;
|
||||||
final client = auth.configureClient('files');
|
final client = auth.configureClient('files');
|
||||||
final resp = await client.post('/stickers', {
|
if (widget.edit == null) {
|
||||||
|
resp = await client.post('/stickers', {
|
||||||
'name': _nameController.text,
|
'name': _nameController.text,
|
||||||
'alias': _aliasController.text,
|
'alias': _aliasController.text,
|
||||||
'pack_id': int.tryParse(_packController.text),
|
'pack_id': int.tryParse(_packController.text),
|
||||||
'attachment_id': int.tryParse(_attachmentController.text),
|
'attachment_id': int.tryParse(_attachmentController.text),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
resp = await client.put('/stickers/${widget.edit!.id}', {
|
||||||
|
'name': _nameController.text,
|
||||||
|
'alias': _aliasController.text,
|
||||||
|
'pack_id': int.tryParse(_packController.text),
|
||||||
|
'attachment_id': int.tryParse(_attachmentController.text),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
|
||||||
if (resp.statusCode != 200) {
|
if (resp.statusCode != 200) {
|
||||||
context.showErrorDialog(resp.bodyString);
|
context.showErrorDialog(resp.bodyString);
|
||||||
@ -71,6 +88,17 @@ class _StickerUploadDialogState extends State<StickerUploadDialog> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.edit != null) {
|
||||||
|
_attachmentController.text = widget.edit!.attachmentId.toString();
|
||||||
|
_packController.text = widget.edit!.packId.toString();
|
||||||
|
_aliasController.text = widget.edit!.alias;
|
||||||
|
_nameController.text = widget.edit!.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_attachmentController.dispose();
|
_attachmentController.dispose();
|
||||||
@ -164,7 +192,7 @@ class _StickerUploadDialogState extends State<StickerUploadDialog> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor:
|
foregroundColor:
|
||||||
|
Loading…
Reference in New Issue
Block a user