diff --git a/README.md b/README.md
index 4b1dfd1d3..970de55f7 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@
## feat
+- [x] 创建/编辑/删除收藏夹
- [x] 评论楼中楼查看对话
- [x] 评论楼中楼定位点击查看的评论
- [x] 评论楼中楼按热度/时间排序
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 3787de1f8..b8f7f1305 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -169,6 +169,11 @@
+
+
> internationalDialingPrefix = [
diff --git a/lib/http/api.dart b/lib/http/api.dart
index 9b3086a0b..880e3277a 100644
--- a/lib/http/api.dart
+++ b/lib/http/api.dart
@@ -172,6 +172,14 @@ class Api {
// https://api.bilibili.com/x/v3/fav/folder/created/list?pn=1&ps=10&up_mid=17340771
static const String userFavFolder = '/x/v3/fav/folder/created/list';
+ static const String folderInfo = '/x/v3/fav/folder/info';
+
+ static const String addFolder = '/x/v3/fav/folder/add';
+
+ static const String editFolder = '/x/v3/fav/folder/edit';
+
+ static const String deleteFolder = '/x/v3/fav/folder/del';
+
/// 收藏夹 详情
/// media_id 当前收藏夹id 搜索全部时为默认收藏夹id
/// pn int 当前页
@@ -664,6 +672,8 @@ class Api {
static const String uploadBfs = '/x/dynamic/feed/draw/upload_bfs';
+ static const String uploadImage = '/x/upload/web/image';
+
static const String videoRelation = '/x/web-interface/archive/relation';
static const String seasonFav = '/x/v3/fav/season/'; // + fav unfav
diff --git a/lib/http/msg.dart b/lib/http/msg.dart
index bd49dad15..2b385de72 100644
--- a/lib/http/msg.dart
+++ b/lib/http/msg.dart
@@ -2,6 +2,7 @@ import 'dart:io';
import 'dart:math';
import 'package:PiliPalaX/http/constants.dart';
import 'package:PiliPalaX/pages/dynamics/view.dart' show ReplyOption;
+import 'package:PiliPalaX/utils/storage.dart';
import 'package:dio/dio.dart';
import '../models/msg/account.dart';
@@ -205,6 +206,33 @@ class MsgHttp {
}
}
+ static Future uploadImage({
+ required dynamic path,
+ required String bucket,
+ required String dir,
+ }) async {
+ var res = await Request().post(
+ Api.uploadImage,
+ data: FormData.fromMap({
+ 'bucket': bucket,
+ 'file': await MultipartFile.fromFile(path),
+ 'dir': dir,
+ 'csrf': await Request.getCsrf(),
+ }),
+ );
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': res.data['data'],
+ };
+ } else {
+ return {
+ 'status': false,
+ 'msg': res.data['message'],
+ };
+ }
+ }
+
static Future uploadBfs(
dynamic path,
) async {
diff --git a/lib/http/user.dart b/lib/http/user.dart
index 90c9e333f..34af8bc09 100644
--- a/lib/http/user.dart
+++ b/lib/http/user.dart
@@ -1,4 +1,5 @@
import 'package:PiliPalaX/http/loading_state.dart';
+import 'package:PiliPalaX/utils/storage.dart';
import 'package:dio/dio.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import '../common/constants.dart';
@@ -65,6 +66,65 @@ class UserHttp {
}
}
+ static Future deleteFolder({
+ required List mediaIds,
+ }) async {
+ var res = await Request().post(Api.deleteFolder,
+ data: {
+ 'media_ids': mediaIds.join(','),
+ 'platform': 'web',
+ 'csrf': await Request.getCsrf(),
+ },
+ options: Options(
+ contentType: Headers.formUrlEncodedContentType,
+ ));
+ if (res.data['code'] == 0) {
+ return {'status': true, 'data': res.data['data']};
+ } else {
+ return {'status': false, 'msg': res.data['message']};
+ }
+ }
+
+ static Future addOrEditFolder({
+ required bool isAdd,
+ dynamic mediaId,
+ required String title,
+ required int privacy,
+ required String cover,
+ required String intro,
+ }) async {
+ var res = await Request().post(isAdd ? Api.addFolder : Api.editFolder,
+ data: {
+ 'title': title,
+ 'intro': intro,
+ 'privacy': privacy,
+ 'cover': cover.isNotEmpty ? Uri.encodeFull(cover) : cover,
+ 'csrf': await Request.getCsrf(),
+ if (mediaId != null) 'media_id': mediaId,
+ },
+ options: Options(
+ contentType: Headers.formUrlEncodedContentType,
+ ));
+ if (res.data['code'] == 0) {
+ return {'status': true, 'data': res.data['data']};
+ } else {
+ return {'status': false, 'msg': res.data['message']};
+ }
+ }
+
+ static Future folderInfo({
+ dynamic mediaId,
+ }) async {
+ var res = await Request().get(Api.folderInfo, data: {
+ 'media_id': mediaId,
+ });
+ if (res.data['code'] == 0) {
+ return {'status': true, 'data': res.data['data']};
+ } else {
+ return {'status': false, 'msg': res.data['message']};
+ }
+ }
+
static Future userFavFolderDetail(
{required int mediaId,
required int pn,
diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart
index 17f25cca0..8f29d8444 100644
--- a/lib/pages/bangumi/introduction/view.dart
+++ b/lib/pages/bangumi/introduction/view.dart
@@ -174,10 +174,10 @@ class _BangumiInfoState extends State
return DraggableScrollableSheet(
minChildSize: 0,
maxChildSize: 1,
- initialChildSize: 0.6,
+ initialChildSize: 0.7,
snap: true,
expand: false,
- snapSizes: const [0.6],
+ snapSizes: const [0.7],
builder: (BuildContext context, ScrollController scrollController) {
return FavPanel(
ctr: bangumiIntroController,
diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart
index 7fdb41c22..00800fee3 100644
--- a/lib/pages/fav_detail/controller.dart
+++ b/lib/pages/fav_detail/controller.dart
@@ -1,6 +1,7 @@
import 'package:PiliPalaX/http/loading_state.dart';
import 'package:PiliPalaX/http/user.dart';
import 'package:PiliPalaX/pages/common/common_controller.dart';
+import 'package:PiliPalaX/utils/storage.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:PiliPalaX/http/video.dart';
@@ -15,6 +16,8 @@ class FavDetailController extends CommonController {
RxString title = ''.obs;
RxString cover = ''.obs;
RxString name = ''.obs;
+ late int attr;
+ RxBool isOwner = false.obs;
@override
void onInit() {
@@ -49,6 +52,9 @@ class FavDetailController extends CommonController {
cover.value = response.response.info['cover'];
name.value = response.response.info['upper']['name'];
mediaCount = response.response.info['media_count'];
+ attr = response.response.info['attr'];
+ isOwner.value = response.response.info['mid'] ==
+ GStorage.userInfo.get('userInfoCache')?.mid;
}
List currentList = loadingState.value is Success
? (loadingState.value as Success).response
diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart
index e8fd590ac..2be517157 100644
--- a/lib/pages/fav_detail/view.dart
+++ b/lib/pages/fav_detail/view.dart
@@ -1,9 +1,12 @@
import 'dart:async';
import 'package:PiliPalaX/http/loading_state.dart';
+import 'package:PiliPalaX/http/user.dart';
import 'package:PiliPalaX/pages/fav_search/view.dart' show SearchType;
+import 'package:PiliPalaX/utils/utils.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:PiliPalaX/common/skeleton/video_card_h.dart';
import 'package:PiliPalaX/common/widgets/http_error.dart';
@@ -113,6 +116,39 @@ class _FavDetailPageState extends State {
// onPressed: () {},
// icon: const Icon(Icons.more_vert),
// ),
+ Obx(
+ () => _favDetailController.isOwner.value
+ ? PopupMenuButton(
+ icon: const Icon(Icons.more_vert),
+ itemBuilder: (context) => [
+ PopupMenuItem(
+ onTap: () {
+ Get.toNamed(
+ '/createFav',
+ parameters: {'mediaId': mediaId},
+ );
+ },
+ child: Text('编辑信息'),
+ ),
+ if (!Utils.isDefault(_favDetailController.attr))
+ PopupMenuItem(
+ onTap: () {
+ UserHttp.deleteFolder(mediaIds: [mediaId])
+ .then((data) {
+ if (data['status']) {
+ SmartDialog.showToast('删除成功');
+ Get.back();
+ } else {
+ SmartDialog.showToast(data['msg']);
+ }
+ });
+ },
+ child: Text('删除'),
+ ),
+ ],
+ )
+ : const SizedBox.shrink(),
+ ),
const SizedBox(width: 6),
],
flexibleSpace: FlexibleSpaceBar(
diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart
index c4d0bea86..2fd27c9e2 100644
--- a/lib/pages/video/detail/introduction/view.dart
+++ b/lib/pages/video/detail/introduction/view.dart
@@ -180,10 +180,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin {
return DraggableScrollableSheet(
minChildSize: 0,
maxChildSize: 1,
- initialChildSize: 0.6,
+ initialChildSize: 0.7,
snap: true,
expand: false,
- snapSizes: const [0.6],
+ snapSizes: const [0.7],
builder: (BuildContext context, ScrollController scrollController) {
return FavPanel(
ctr: videoIntroController,
diff --git a/lib/pages/video/detail/introduction/widgets/create_fav_page.dart b/lib/pages/video/detail/introduction/widgets/create_fav_page.dart
new file mode 100644
index 000000000..9fcf4df1c
--- /dev/null
+++ b/lib/pages/video/detail/introduction/widgets/create_fav_page.dart
@@ -0,0 +1,384 @@
+import 'package:PiliPalaX/common/widgets/http_error.dart';
+import 'package:PiliPalaX/http/msg.dart';
+import 'package:PiliPalaX/http/user.dart';
+import 'package:PiliPalaX/utils/utils.dart';
+import 'package:easy_debounce/easy_throttle.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:image_cropper/image_cropper.dart';
+import 'package:image_picker/image_picker.dart';
+
+class CreateFavPage extends StatefulWidget {
+ const CreateFavPage({super.key});
+
+ @override
+ State createState() => _CreateFavPageState();
+}
+
+class _CreateFavPageState extends State {
+ dynamic _mediaId;
+ late final _titleController = TextEditingController();
+ late final _introController = TextEditingController();
+ String? _cover;
+ bool _isPublic = true;
+ late final _imagePicker = ImagePicker();
+ String? _errMsg;
+ int? _attr;
+
+ @override
+ void initState() {
+ super.initState();
+ _mediaId = Get.parameters['mediaId'];
+ if (_mediaId != null) {
+ _getFolderInfo();
+ }
+ }
+
+ void _getFolderInfo() {
+ UserHttp.folderInfo(mediaId: _mediaId).then((data) {
+ if (data['status']) {
+ _titleController.text = data['data']['title'];
+ _introController.text = data['data']['intro'];
+ _isPublic = Utils.isPublic(data['data']['attr']);
+ _cover = data['data']['cover'];
+ _attr = data['data']['attr'];
+ } else {
+ _errMsg = data['msg'];
+ }
+ setState(() {});
+ });
+ }
+
+ @override
+ void dispose() {
+ _titleController.dispose();
+ _introController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(_mediaId != null ? '编辑' : '创建'),
+ actions: [
+ TextButton(
+ onPressed: () {
+ if (_titleController.text.isEmpty) {
+ SmartDialog.showToast('名称不能为空');
+ return;
+ }
+ UserHttp.addOrEditFolder(
+ isAdd: _mediaId == null,
+ mediaId: _mediaId,
+ title: _titleController.text,
+ privacy: _isPublic ? 0 : 1,
+ cover: _cover ?? '',
+ intro: _introController.text,
+ ).then((data) {
+ if (data['status']) {
+ Get.back(result: data['data']);
+ SmartDialog.showToast('${_mediaId != null ? '编辑' : '创建'}成功');
+ } else {
+ SmartDialog.showToast(data['msg']);
+ }
+ });
+ },
+ child: const Text('完成'),
+ ),
+ const SizedBox(width: 16),
+ ],
+ ),
+ body: _mediaId != null
+ ? _titleController.text.isNotEmpty
+ ? _buildBody
+ : _errMsg?.isNotEmpty == true
+ ? Center(
+ child: CustomScrollView(
+ shrinkWrap: true,
+ slivers: [
+ HttpError(
+ errMsg: _errMsg,
+ fn: _getFolderInfo,
+ ),
+ ],
+ ),
+ )
+ : Center(child: CircularProgressIndicator())
+ : _buildBody,
+ );
+ }
+
+ void _pickImg() async {
+ XFile? pickedFile = await _imagePicker.pickImage(
+ source: ImageSource.gallery,
+ imageQuality: 100,
+ );
+ if (pickedFile != null && mounted) {
+ CroppedFile? croppedFile = await ImageCropper().cropImage(
+ sourcePath: pickedFile.path,
+ uiSettings: [
+ AndroidUiSettings(
+ toolbarTitle: '裁剪',
+ toolbarColor: Theme.of(context).colorScheme.primary,
+ toolbarWidgetColor: Colors.white,
+ aspectRatioPresets: [
+ CropAspectRatioPreset.ratio16x9,
+ ],
+ lockAspectRatio: true,
+ hideBottomControls: true,
+ initAspectRatio: CropAspectRatioPreset.ratio16x9,
+ ),
+ IOSUiSettings(
+ title: '裁剪',
+ aspectRatioPresets: [
+ CropAspectRatioPreset.ratio16x9,
+ ],
+ aspectRatioLockEnabled: true,
+ resetAspectRatioEnabled: false,
+ aspectRatioPickerButtonHidden: true,
+ ),
+ ],
+ );
+ if (croppedFile != null) {
+ MsgHttp.uploadImage(
+ path: croppedFile.path,
+ bucket: 'medialist',
+ dir: 'cover',
+ ).then((data) {
+ if (data['status']) {
+ _cover = data['data']['location'];
+ setState(() {});
+ } else {
+ SmartDialog.showToast(data['msg']);
+ }
+ });
+ }
+ }
+ }
+
+ dynamic leadingStyle = TextStyle(fontSize: 14);
+
+ Widget get _buildBody => SingleChildScrollView(
+ child: Column(
+ children: [
+ if (_attr == null || !Utils.isDefault(_attr!)) ...[
+ ListTile(
+ tileColor: Theme.of(context).colorScheme.onInverseSurface,
+ onTap: () {
+ EasyThrottle.throttle(
+ 'imagePicker', const Duration(milliseconds: 500),
+ () async {
+ if (_cover?.isNotEmpty == true) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ clipBehavior: Clip.hardEdge,
+ contentPadding:
+ const EdgeInsets.fromLTRB(0, 12, 0, 12),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ListTile(
+ dense: true,
+ onTap: () {
+ Get.back();
+ _pickImg();
+ },
+ title: const Text(
+ '替换封面',
+ style: TextStyle(fontSize: 14),
+ ),
+ ),
+ ListTile(
+ dense: true,
+ onTap: () {
+ Get.back();
+ _cover = null;
+ setState(() {});
+ },
+ title: const Text(
+ '移除封面',
+ style: TextStyle(fontSize: 14),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ } else {
+ _pickImg();
+ }
+ });
+ },
+ leading: Text(
+ '封面',
+ style: leadingStyle,
+ ),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ if (_cover?.isNotEmpty == true)
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10),
+ child: LayoutBuilder(
+ builder: (_, constraints) {
+ return ClipRRect(
+ borderRadius: BorderRadius.circular(6),
+ child: Image.network(
+ _cover!,
+ height: constraints.maxHeight,
+ width: constraints.maxHeight * 16 / 9,
+ fit: BoxFit.cover,
+ ),
+ );
+ },
+ ),
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.keyboard_arrow_right,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ListTile(
+ tileColor: Theme.of(context).colorScheme.onInverseSurface,
+ leading: Text.rich(
+ style: TextStyle(
+ height: 1,
+ fontSize: 14,
+ ),
+ TextSpan(
+ children: [
+ TextSpan(
+ text: '*',
+ style: TextStyle(
+ fontSize: 14,
+ height: 1,
+ color: Theme.of(context).colorScheme.error,
+ ),
+ ),
+ TextSpan(
+ text: '名称',
+ style: TextStyle(
+ height: 1,
+ fontSize: 14,
+ ),
+ ),
+ ],
+ ),
+ ),
+ title: TextField(
+ readOnly: _attr != null && Utils.isDefault(_attr!),
+ controller: _titleController,
+ style: TextStyle(
+ fontSize: 14,
+ color: _attr != null && Utils.isDefault(_attr!)
+ ? Theme.of(context).colorScheme.outline
+ : null,
+ ),
+ inputFormatters: [
+ LengthLimitingTextInputFormatter(21),
+ ],
+ decoration: InputDecoration(
+ isDense: true,
+ hintText: '名称',
+ hintStyle: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ border: OutlineInputBorder(
+ borderSide: BorderSide.none,
+ gapPadding: 0,
+ ),
+ contentPadding: EdgeInsets.all(0),
+ ),
+ ),
+ ),
+ const SizedBox(height: 16),
+ if (_attr == null || !Utils.isDefault(_attr!)) ...[
+ ListTile(
+ tileColor: Theme.of(context).colorScheme.onInverseSurface,
+ title: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text.rich(
+ TextSpan(
+ children: [
+ TextSpan(
+ text: '简介',
+ style: leadingStyle,
+ ),
+ TextSpan(
+ text: '*',
+ style: TextStyle(color: Colors.transparent),
+ )
+ ],
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: TextField(
+ minLines: 6,
+ maxLines: 6,
+ controller: _introController,
+ style: TextStyle(fontSize: 14),
+ inputFormatters: [
+ LengthLimitingTextInputFormatter(201),
+ ],
+ decoration: InputDecoration(
+ isDense: true,
+ hintText: '简介',
+ hintStyle: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ border: OutlineInputBorder(
+ borderSide: BorderSide.none,
+ gapPadding: 0,
+ ),
+ contentPadding: EdgeInsets.all(0),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ListTile(
+ onTap: () {
+ setState(() {
+ _isPublic = !_isPublic;
+ });
+ },
+ tileColor: Theme.of(context).colorScheme.onInverseSurface,
+ leading: Text(
+ '公开',
+ style: leadingStyle,
+ ),
+ trailing: Transform.scale(
+ alignment: Alignment.centerRight,
+ scale: 0.8,
+ child: Switch(
+ value: _isPublic,
+ onChanged: (value) {
+ setState(() {
+ _isPublic = value;
+ });
+ }),
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ );
+}
diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart
index 28a01ffa4..f923eb2f7 100644
--- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart
+++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart
@@ -1,3 +1,4 @@
+import 'package:PiliPalaX/models/user/fav_folder.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:PiliPalaX/common/widgets/http_error.dart';
@@ -62,7 +63,22 @@ class _FavPanelState extends State {
actions: [
TextButton.icon(
onPressed: () {
- // TODO
+ Get.toNamed('/createFav')?.then((data) {
+ (widget.ctr?.favFolderData.value as FavFolderData?)
+ ?.list
+ ?.insert(
+ 1,
+ FavFolderItemData(
+ id: data['id'],
+ fid: data['fid'],
+ attr: data['attr'],
+ title: data['title'],
+ favState: data['fav_state'],
+ mediaCount: data['media_count'],
+ ),
+ );
+ widget.ctr?.favFolderData.refresh();
+ });
},
icon: Icon(
Icons.add,
diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart
index 1a303407c..1b641da4d 100644
--- a/lib/router/app_pages.dart
+++ b/lib/router/app_pages.dart
@@ -2,6 +2,7 @@
import 'package:PiliPalaX/pages/member/new/member_page.dart';
import 'package:PiliPalaX/pages/setting/sponsor_block_page.dart';
+import 'package:PiliPalaX/pages/video/detail/introduction/widgets/create_fav_page.dart';
import 'package:PiliPalaX/pages/webview/webview_page.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -186,6 +187,7 @@ class Routes {
// 弹幕屏蔽管理
CustomGetPage(name: '/danmakuBlock', page: () => const DanmakuBlockPage()),
CustomGetPage(name: '/sponsorBlock', page: () => const SponsorBlockPage()),
+ CustomGetPage(name: '/createFav', page: () => const CreateFavPage()),
];
}
diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart
index 270ac2348..6cc95b878 100644
--- a/lib/utils/storage.dart
+++ b/lib/utils/storage.dart
@@ -2,7 +2,6 @@ import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:PiliPalaX/common/widgets/pair.dart';
-import 'package:PiliPalaX/main.dart';
import 'package:PiliPalaX/models/common/theme_type.dart';
import 'package:PiliPalaX/pages/video/detail/controller.dart'
show SegmentType, SegmentTypeExt, SkipType;
diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart
index 23d5ac518..0d69fa49d 100644
--- a/lib/utils/utils.dart
+++ b/lib/utils/utils.dart
@@ -28,6 +28,10 @@ import '../models/github/latest.dart';
class Utils {
static final Random random = Random();
+ static bool isDefault(int attr) {
+ return (attr & 2) == 0;
+ }
+
static bool isPublic(int attr) {
return (attr & 1) == 0;
}
@@ -119,10 +123,10 @@ class Utils {
return DraggableScrollableSheet(
minChildSize: 0,
maxChildSize: 1,
- initialChildSize: 0.6,
+ initialChildSize: 0.7,
snap: true,
expand: false,
- snapSizes: const [0.6],
+ snapSizes: const [0.7],
builder: (BuildContext context,
ScrollController scrollController) {
return GroupPanel(
diff --git a/pubspec.lock b/pubspec.lock
index fa17293d6..3daffaac1 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -949,6 +949,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.3.0"
+ image_cropper:
+ dependency: "direct main"
+ description:
+ name: image_cropper
+ sha256: fe37d9a129411486e0d93089b61bd326d05b89e78ad4981de54b560725bf5bd5
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.0.2"
+ image_cropper_for_web:
+ dependency: transitive
+ description:
+ name: image_cropper_for_web
+ sha256: "34256c8fb7fcb233251787c876bb37271744459b593a948a2db73caa323034d0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.2"
+ image_cropper_platform_interface:
+ dependency: transitive
+ description:
+ name: image_cropper_platform_interface
+ sha256: e8e9d2ca36360387aee39295ce49029362ae4df3071f23e8e71f2b81e40b7531
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.0"
image_picker:
dependency: "direct main"
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 501ed7283..8e62ccaf0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -165,6 +165,7 @@ dependencies:
intl: ^0.19.0
grpc: ^4.0.1
flutter_svg: ^2.0.10+1
+ image_cropper: ^8.0.2
dependency_overrides:
screen_brightness: ^2.0.0+2