music bug fix
@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '13.0'
|
||||
platform :ios, '13.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
@ -64,7 +64,7 @@ post_install do |installer|
|
||||
## 6. 禁用 Siri/助手 (NSSiriUsageDescription)
|
||||
'PERMISSION_ASSISTANT=0',
|
||||
|
||||
## 7. 禁用 媒体库 (NSAppleMusicUsageDescription)
|
||||
## 7. 启用 媒体库 (NSAppleMusicUsageDescription)
|
||||
'PERMISSION_MEDIA_LIBRARY=1',
|
||||
|
||||
## 8. 禁用 蓝牙 (如果你没用到蓝牙,建议也关掉)
|
||||
|
||||
@ -11,11 +11,47 @@ PODS:
|
||||
- GoogleUtilities/Environment (~> 8.0)
|
||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||
- PromisesObjC (~> 2.4)
|
||||
- audio_session (0.0.1):
|
||||
- Flutter
|
||||
- audioplayers_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- DKImagePickerController/Core (4.3.9):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
- DKImagePickerController/ImageDataManager (4.3.9)
|
||||
- DKImagePickerController/PhotoGallery (4.3.9):
|
||||
- DKImagePickerController/Core
|
||||
- DKPhotoGallery
|
||||
- DKImagePickerController/Resource (4.3.9)
|
||||
- DKPhotoGallery (0.0.19):
|
||||
- DKPhotoGallery/Core (= 0.0.19)
|
||||
- DKPhotoGallery/Model (= 0.0.19)
|
||||
- DKPhotoGallery/Preview (= 0.0.19)
|
||||
- DKPhotoGallery/Resource (= 0.0.19)
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Core (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Preview
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Model (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Preview (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Resource
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Resource (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Firebase/Auth (11.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseAuth (~> 11.15.0)
|
||||
@ -141,6 +177,9 @@ PODS:
|
||||
- in_app_purchase_storekit (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- just_audio (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- libpag (4.5.52)
|
||||
- libwebp (1.5.0):
|
||||
- libwebp/demux (= 1.5.0)
|
||||
@ -170,6 +209,9 @@ PODS:
|
||||
- pag (0.0.1):
|
||||
- Flutter
|
||||
- libpag
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- PromisesObjC (2.4.0)
|
||||
@ -191,6 +233,7 @@ PODS:
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- SwiftyBeaver (1.9.5)
|
||||
- SwiftyGif (5.4.5)
|
||||
- tancent_vap (0.0.1):
|
||||
- Flutter
|
||||
- tencent_cloud_chat_sdk (8.0.0):
|
||||
@ -223,8 +266,10 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/darwin`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
|
||||
@ -235,9 +280,11 @@ DEPENDENCIES:
|
||||
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
||||
- just_audio (from `.symlinks/plugins/just_audio/darwin`)
|
||||
- on_audio_query_ios (from `.symlinks/plugins/on_audio_query_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- pag (from `.symlinks/plugins/pag/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
||||
@ -255,6 +302,8 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- AppAuth
|
||||
- AppCheckCore
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- Firebase
|
||||
- FirebaseAppCheckInterop
|
||||
- FirebaseAuth
|
||||
@ -282,6 +331,7 @@ SPEC REPOS:
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- SwiftyBeaver
|
||||
- SwiftyGif
|
||||
- TOCropViewController
|
||||
- TXCustomBeautyProcesserPlugin
|
||||
- TXIMSDK_Plus_iOS_XCFramework
|
||||
@ -290,10 +340,14 @@ SPEC REPOS:
|
||||
EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: ".symlinks/plugins/app_links/ios"
|
||||
audio_session:
|
||||
:path: ".symlinks/plugins/audio_session/ios"
|
||||
audioplayers_darwin:
|
||||
:path: ".symlinks/plugins/audioplayers_darwin/darwin"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
firebase_auth:
|
||||
:path: ".symlinks/plugins/firebase_auth/ios"
|
||||
firebase_core:
|
||||
@ -314,12 +368,16 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
in_app_purchase_storekit:
|
||||
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
|
||||
just_audio:
|
||||
:path: ".symlinks/plugins/just_audio/darwin"
|
||||
on_audio_query_ios:
|
||||
:path: ".symlinks/plugins/on_audio_query_ios/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
pag:
|
||||
:path: ".symlinks/plugins/pag/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
shared_preferences_foundation:
|
||||
@ -349,8 +407,12 @@ SPEC CHECKSUMS:
|
||||
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
|
||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
|
||||
audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5
|
||||
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
|
||||
firebase_auth: 50af8366c87bb88c80ebeae62eb60189c7246b9b
|
||||
firebase_core: 995454a784ff288be5689b796deb9e9fa3601818
|
||||
@ -378,6 +440,7 @@ SPEC CHECKSUMS:
|
||||
image_cropper: 655b3ba703c9e15e3111e79151624d6154288774
|
||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||
in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
|
||||
just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed
|
||||
libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4
|
||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
@ -385,6 +448,7 @@ SPEC CHECKSUMS:
|
||||
on_audio_query_ios: 28a780e2d0d85d92d500ba6e12c6c8167022b2fa
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
pag: f01aa9017ab0e04a83ba4a20d6070a50f0ac9da8
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||
@ -395,6 +459,7 @@ SPEC CHECKSUMS:
|
||||
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
tancent_vap: fa8ad93814a9f950514a7074c662d2c937084c68
|
||||
tencent_cloud_chat_sdk: 55e5fffe20f6b7937a26a674ccccb639563a9790
|
||||
tencent_rtc_sdk: f77558b6b436a149d378557c2a837f73c09061bc
|
||||
@ -408,6 +473,6 @@ SPEC CHECKSUMS:
|
||||
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||
webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
|
||||
|
||||
PODFILE CHECKSUM: a6f49a93e5f85201a2efdadcd7cf184b0e310894
|
||||
PODFILE CHECKSUM: b724feb0f01aec7228c34cc4763485e911aa9322
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 58 KiB |
@ -106,7 +106,7 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
||||
if (success) {
|
||||
userProvider?.updateUserAvatar(url);
|
||||
}
|
||||
});
|
||||
}, allowGif: true);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 15.w),
|
||||
@ -559,24 +559,6 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
||||
return primary ?? fallback;
|
||||
}
|
||||
|
||||
bool _isBrokenLocalMediaUrl(String? url) {
|
||||
return (url ?? "").contains("/external/oss/local/");
|
||||
}
|
||||
|
||||
String? _preferUsableAvatar(String? primary, String? fallback) {
|
||||
if (primary != null &&
|
||||
primary.isNotEmpty &&
|
||||
!_isBrokenLocalMediaUrl(primary)) {
|
||||
return primary;
|
||||
}
|
||||
if (fallback != null &&
|
||||
fallback.isNotEmpty &&
|
||||
!_isBrokenLocalMediaUrl(fallback)) {
|
||||
return fallback;
|
||||
}
|
||||
return _preferNonEmpty(primary, fallback);
|
||||
}
|
||||
|
||||
num? _preferNonZero(num? primary, num? fallback) {
|
||||
if (primary != null && primary != 0) {
|
||||
return primary;
|
||||
@ -661,10 +643,6 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
||||
if (submittedProfile != null) {
|
||||
final mergedProfile = (user.userProfile ?? SocialChatUserProfile())
|
||||
.copyWith(
|
||||
userAvatar: _preferUsableAvatar(
|
||||
user.userProfile?.userAvatar,
|
||||
submittedProfile.userAvatar,
|
||||
),
|
||||
userNickname: _preferNonEmpty(
|
||||
user.userProfile?.userNickname,
|
||||
submittedProfile.userNickname,
|
||||
|
||||
@ -558,31 +558,37 @@ class _SwipeDeleteTileState extends State<_SwipeDeleteTile> {
|
||||
duration: const Duration(milliseconds: 160),
|
||||
curve: Curves.easeOutCubic,
|
||||
transform: Matrix4.translationValues(_dragOffset, 0, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: constraints.maxWidth, child: widget.child),
|
||||
SizedBox(
|
||||
width: actionWidth,
|
||||
height: 56.w,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: widget.onDelete,
|
||||
child: Container(
|
||||
margin: EdgeInsetsDirectional.only(start: 4.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFE64A4A),
|
||||
borderRadius: BorderRadius.circular(8.w),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Image.asset(
|
||||
"sc_images/room/sc_music_material_delete.png",
|
||||
width: 22.w,
|
||||
height: 22.w,
|
||||
child: SizedBox(
|
||||
width: constraints.maxWidth + actionWidth,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: constraints.maxWidth,
|
||||
child: widget.child,
|
||||
),
|
||||
SizedBox(
|
||||
width: actionWidth,
|
||||
height: 56.w,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: widget.onDelete,
|
||||
child: Container(
|
||||
margin: EdgeInsetsDirectional.only(start: 4.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFE64A4A),
|
||||
borderRadius: BorderRadius.circular(8.w),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Image.asset(
|
||||
"sc_images/room/sc_music_material_delete.png",
|
||||
width: 22.w,
|
||||
height: 22.w,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -330,9 +330,11 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
||||
OverlayManager().removeRoom(roomId: currentRoomId);
|
||||
SCRoomEffectScheduler().clearDeferredTasks(reason: 'voice_room_suspend');
|
||||
SCGiftVapSvgaManager().stopPlayback();
|
||||
unawaited(
|
||||
Provider.of<RoomMusicManager>(context, listen: false).stopForRoomExit(),
|
||||
);
|
||||
if (!SCFloatIchart().isShow()) {
|
||||
unawaited(
|
||||
Provider.of<RoomMusicManager>(context, listen: false).stopForRoomExit(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildRoomStartupLayer() {
|
||||
|
||||
@ -75,21 +75,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
||||
return primary ?? fallback;
|
||||
}
|
||||
|
||||
bool _isBrokenLocalMediaUrl(String? url) {
|
||||
return (url ?? "").contains("/external/oss/local/");
|
||||
}
|
||||
|
||||
String? _preferUsableAvatar(String? primary, String? fallback) {
|
||||
if ((primary ?? "").trim().isNotEmpty && !_isBrokenLocalMediaUrl(primary)) {
|
||||
return primary;
|
||||
}
|
||||
if ((fallback ?? "").trim().isNotEmpty &&
|
||||
!_isBrokenLocalMediaUrl(fallback)) {
|
||||
return fallback;
|
||||
}
|
||||
return _preferNonEmpty(primary, fallback);
|
||||
}
|
||||
|
||||
DateTime? _birthdayFromProfile([SocialChatUserProfile? profile]) {
|
||||
final targetProfile =
|
||||
profile ?? AccountStorage().getCurrentUser()?.userProfile;
|
||||
@ -158,7 +143,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
||||
}
|
||||
|
||||
void _syncLocalProfileState(SocialChatUserProfile profile) {
|
||||
userCover = _preferUsableAvatar(profile.userAvatar, userCover);
|
||||
userCover = profile.userAvatar;
|
||||
nickName = profile.userNickname ?? nickName;
|
||||
autograph = profile.autograph ?? autograph;
|
||||
hobby = profile.hobby ?? hobby;
|
||||
@ -239,11 +224,9 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
||||
String url,
|
||||
) {
|
||||
if (success) {
|
||||
userCover = url;
|
||||
setState(() {});
|
||||
submitAvatarOnly();
|
||||
submit(userAvatarValue: url);
|
||||
}
|
||||
});
|
||||
}, allowGif: true);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 3.w),
|
||||
@ -404,13 +387,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
||||
return eighteenYearsAgo;
|
||||
}
|
||||
|
||||
void submitAvatarOnly() {
|
||||
if ((userCover ?? "").trim().isEmpty) {
|
||||
return;
|
||||
}
|
||||
submit(userAvatarValue: userCover);
|
||||
}
|
||||
|
||||
void submit({
|
||||
Object? userAvatarValue = _noChange,
|
||||
Object? userNicknameValue = _noChange,
|
||||
@ -473,12 +449,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
||||
: autographValue as String?,
|
||||
);
|
||||
final mergedProfile = updatedProfile.copyWith(
|
||||
userAvatar: _preferUsableAvatar(
|
||||
updatedProfile.userAvatar,
|
||||
identical(userAvatarValue, _noChange)
|
||||
? userCover
|
||||
: userAvatarValue as String?,
|
||||
),
|
||||
userNickname: _preferNonEmpty(
|
||||
updatedProfile.userNickname,
|
||||
identical(userNicknameValue, _noChange)
|
||||
|
||||
@ -887,6 +887,8 @@ class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: 300.w,
|
||||
fit: BoxFit.cover,
|
||||
gifFit: BoxFit.cover,
|
||||
gifAlignment: Alignment.topCenter,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@ -2024,6 +2026,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
|
||||
},
|
||||
aspectRatio: ScreenUtil().screenWidth / 300.w,
|
||||
backOriginalFile: false,
|
||||
allowGif: true,
|
||||
);
|
||||
}
|
||||
|
||||
@ -2040,19 +2043,8 @@ class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
|
||||
final updatedProfile = await SCAccountRepository().updateUserInfo(
|
||||
backgroundPhotos: [imageUrl],
|
||||
);
|
||||
final returnedPhotos = updatedProfile.backgroundPhotos ?? [];
|
||||
final hasUploadedPhoto = returnedPhotos.any(
|
||||
(photo) => (photo.url ?? "").trim() == imageUrl,
|
||||
);
|
||||
final backgroundPhotos =
|
||||
hasUploadedPhoto
|
||||
? returnedPhotos
|
||||
: [PersonPhoto(url: imageUrl, status: 1)];
|
||||
final mergedProfile = updatedProfile.copyWith(
|
||||
backgroundPhotos: backgroundPhotos,
|
||||
);
|
||||
ref.userProfile = mergedProfile;
|
||||
ref.syncCurrentUserProfile(mergedProfile);
|
||||
ref.userProfile = updatedProfile;
|
||||
ref.syncCurrentUserProfile(updatedProfile);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ import '../../ui_kit/widgets/room/rocket/room_rocket_pag_effect_overlay.dart';
|
||||
import '../../ui_kit/widgets/room/rocket/room_rocket_api_mapper.dart';
|
||||
|
||||
typedef OnSoundVoiceChange = Function(num index, int volum);
|
||||
typedef RoomMusicSessionExitListener = Future<void> Function();
|
||||
typedef RtcProvider = RealTimeCommunicationManager;
|
||||
|
||||
enum RoomStartupStatus { idle, loading, ready, failed }
|
||||
@ -468,6 +469,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
RoomRtcEngineAdapter? _roomRtcEngineAdapter;
|
||||
RoomMusicMixingStateListener? _roomMusicMixingStateListener;
|
||||
RoomMusicMicRouteChangedListener? _roomMusicMicRouteChangedListener;
|
||||
RoomMusicSessionExitListener? _roomMusicSessionExitListener;
|
||||
BuildContext? context;
|
||||
RtmProvider? rtmProvider;
|
||||
|
||||
@ -2849,7 +2851,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
if (currenRoom != null) {
|
||||
_switchAwayFromCurrentRoomForNewEntry();
|
||||
await _switchAwayFromCurrentRoomForNewEntry();
|
||||
}
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
@ -4854,8 +4856,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
exitRtmProvider?.cleanRoomData();
|
||||
} catch (e) {}
|
||||
|
||||
await _notifyRoomMusicSessionExit();
|
||||
_clearData(clearPersistedRoomMarker: false);
|
||||
if (!isLogout && navigationContext != null) {
|
||||
if (!isLogout && navigationContext != null && navigationContext.mounted) {
|
||||
VoiceRoomRoute.popVoiceRoomToPrevious(navigationContext);
|
||||
}
|
||||
|
||||
@ -4875,7 +4878,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void _switchAwayFromCurrentRoomForNewEntry() {
|
||||
Future<void> _switchAwayFromCurrentRoomForNewEntry() async {
|
||||
_stopRoomStatePolling();
|
||||
final groupId = currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "";
|
||||
final roomId = currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
||||
@ -4889,6 +4892,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
exitRtmProvider?.cleanRoomData();
|
||||
} catch (e) {}
|
||||
|
||||
await _notifyRoomMusicSessionExit();
|
||||
_clearData(clearPersistedRoomMarker: false);
|
||||
final roomRtcLeaveTask = _leaveRoomRtcForExit();
|
||||
_pendingRoomSwitchRtcLeaveTask = roomRtcLeaveTask;
|
||||
@ -6028,6 +6032,20 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
_roomMusicMicRouteChangedListener = listener;
|
||||
}
|
||||
|
||||
void setRoomMusicSessionExitListener(RoomMusicSessionExitListener? listener) {
|
||||
_roomMusicSessionExitListener = listener;
|
||||
}
|
||||
|
||||
Future<void> _notifyRoomMusicSessionExit() async {
|
||||
final listener = _roomMusicSessionExitListener;
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await listener();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
void updateRoomMusicPublishingState({
|
||||
required String? userId,
|
||||
required bool publishing,
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:on_audio_query/on_audio_query.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:yumi/shared/data_sources/models/sc_music_folder_mode.dart';
|
||||
import 'package:yumi/shared/data_sources/models/sc_music_mode.dart';
|
||||
import 'package:yumi/shared/tools/sc_permission_utils.dart';
|
||||
@ -15,6 +19,10 @@ class LocalMusicScanner {
|
||||
Future<List<SCMusicFolderMode>> scanMp3Folders({
|
||||
List<SCMusicMode> addedSongs = const [],
|
||||
}) async {
|
||||
if (Platform.isIOS) {
|
||||
return _pickIosMp3Files(addedSongs: addedSongs);
|
||||
}
|
||||
|
||||
final hasPermission = await SCPermissionUtils.checkAudioPermission();
|
||||
if (!hasPermission) {
|
||||
return const <SCMusicFolderMode>[];
|
||||
@ -96,6 +104,98 @@ class LocalMusicScanner {
|
||||
return List<SCMusicFolderMode>.unmodifiable(folders);
|
||||
}
|
||||
|
||||
Future<List<SCMusicFolderMode>> _pickIosMp3Files({
|
||||
required List<SCMusicMode> addedSongs,
|
||||
}) async {
|
||||
final FilePickerResult? result;
|
||||
try {
|
||||
result = await FilePicker.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: const ['mp3'],
|
||||
allowMultiple: true,
|
||||
withData: false,
|
||||
withReadStream: false,
|
||||
);
|
||||
} catch (_) {
|
||||
return const <SCMusicFolderMode>[];
|
||||
}
|
||||
if (result == null || result.files.isEmpty) {
|
||||
return const <SCMusicFolderMode>[];
|
||||
}
|
||||
|
||||
final addedIds = addedSongs.map((item) => item.id).toSet();
|
||||
final addedPaths = addedSongs.map((item) => item.localPath).toSet();
|
||||
final storageDir = await _iosRoomMusicDirectory();
|
||||
final player = AudioPlayer();
|
||||
final songs = <SCMusicMode>[];
|
||||
final seen = <String>{};
|
||||
|
||||
try {
|
||||
for (final file in result.files) {
|
||||
final sourcePath = (file.path ?? '').trim();
|
||||
if (sourcePath.isEmpty ||
|
||||
!_isMp3FileName(file.name) ||
|
||||
!_fileExists(sourcePath)) {
|
||||
continue;
|
||||
}
|
||||
final sourceId = _firstNonEmpty([
|
||||
file.identifier,
|
||||
'${file.name}:${file.size}',
|
||||
sourcePath,
|
||||
]);
|
||||
if (sourceId.isEmpty || !seen.add(sourceId)) {
|
||||
continue;
|
||||
}
|
||||
final storedPath = await _copyIosPickedFile(
|
||||
sourcePath: sourcePath,
|
||||
fileName: file.name,
|
||||
sourceId: sourceId,
|
||||
storageDir: storageDir,
|
||||
);
|
||||
final durationMs = await _readLocalMp3DurationMs(player, storedPath);
|
||||
final storedFile = File(storedPath);
|
||||
final safeFileName = p.basename(storedPath);
|
||||
songs.add(
|
||||
SCMusicMode(
|
||||
id: sourceId,
|
||||
title: p.basenameWithoutExtension(file.name).trim(),
|
||||
artist: '',
|
||||
durationMs: durationMs,
|
||||
localPath: storedPath,
|
||||
contentUri: '',
|
||||
folderPath: storageDir.path,
|
||||
fileName: safeFileName,
|
||||
size: storedFile.existsSync() ? storedFile.lengthSync() : file.size,
|
||||
addedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await player.dispose();
|
||||
}
|
||||
|
||||
if (songs.isEmpty) {
|
||||
return const <SCMusicFolderMode>[];
|
||||
}
|
||||
songs.sort(
|
||||
(a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()),
|
||||
);
|
||||
final addedCount =
|
||||
songs.where((song) {
|
||||
return addedIds.contains(song.id) ||
|
||||
addedPaths.contains(song.localPath);
|
||||
}).length;
|
||||
return <SCMusicFolderMode>[
|
||||
SCMusicFolderMode(
|
||||
id: 'ios_file_picker',
|
||||
name: 'Selected Music',
|
||||
path: 'Files',
|
||||
songs: List<SCMusicMode>.unmodifiable(songs),
|
||||
addedCount: addedCount,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
bool _isMp3(SongModel song) {
|
||||
final extension = _songString(
|
||||
song,
|
||||
@ -108,6 +208,72 @@ class LocalMusicScanner {
|
||||
_songString(song, "_data").toLowerCase().endsWith(".mp3");
|
||||
}
|
||||
|
||||
bool _isMp3FileName(String fileName) {
|
||||
return fileName.toLowerCase().endsWith('.mp3');
|
||||
}
|
||||
|
||||
Future<Directory> _iosRoomMusicDirectory() async {
|
||||
final supportDir = await getApplicationSupportDirectory();
|
||||
final directory = Directory(p.join(supportDir.path, 'room_music'));
|
||||
if (!directory.existsSync()) {
|
||||
await directory.create(recursive: true);
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
Future<String> _copyIosPickedFile({
|
||||
required String sourcePath,
|
||||
required String fileName,
|
||||
required String sourceId,
|
||||
required Directory storageDir,
|
||||
}) async {
|
||||
if (p.isWithin(storageDir.path, sourcePath)) {
|
||||
return sourcePath;
|
||||
}
|
||||
final sourceFile = File(sourcePath);
|
||||
final targetFile = File(
|
||||
p.join(storageDir.path, _iosStoredFileName(fileName, sourceId)),
|
||||
);
|
||||
if (targetFile.existsSync() &&
|
||||
targetFile.lengthSync() == sourceFile.lengthSync()) {
|
||||
return targetFile.path;
|
||||
}
|
||||
await sourceFile.copy(targetFile.path);
|
||||
return targetFile.path;
|
||||
}
|
||||
|
||||
String _iosStoredFileName(String fileName, String sourceId) {
|
||||
final baseName = p.basenameWithoutExtension(fileName).trim();
|
||||
final safeBaseName =
|
||||
baseName
|
||||
.replaceAll(RegExp(r'[^A-Za-z0-9._ -]+'), '_')
|
||||
.replaceAll(RegExp(r'\s+'), ' ')
|
||||
.trim();
|
||||
final token = base64Url.encode(utf8.encode(sourceId)).replaceAll('=', '');
|
||||
final suffix = token.length > 12 ? token.substring(0, 12) : token;
|
||||
return '${safeBaseName.isEmpty ? 'music' : safeBaseName}_$suffix.mp3';
|
||||
}
|
||||
|
||||
Future<int> _readLocalMp3DurationMs(AudioPlayer player, String path) async {
|
||||
try {
|
||||
final duration = await player.setFilePath(path);
|
||||
await player.stop();
|
||||
return duration?.inMilliseconds ?? 0;
|
||||
} catch (_) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String _firstNonEmpty(List<String?> values) {
|
||||
for (final value in values) {
|
||||
final text = value?.trim() ?? '';
|
||||
if (text.isNotEmpty) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
bool _fileExists(String path) {
|
||||
try {
|
||||
return File(path).existsSync();
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:audio_session/audio_session.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||
import 'package:yumi/services/audio/room_rtc_types.dart';
|
||||
@ -86,9 +87,11 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
}
|
||||
_rtcProvider?.setRoomMusicMixingStateListener(null);
|
||||
_rtcProvider?.setRoomMusicMicRouteChangedListener(null);
|
||||
_rtcProvider?.setRoomMusicSessionExitListener(null);
|
||||
_rtcProvider = provider;
|
||||
provider.setRoomMusicMixingStateListener(_handleMixingStateChanged);
|
||||
provider.setRoomMusicMicRouteChangedListener(_handleMicRouteChanged);
|
||||
provider.setRoomMusicSessionExitListener(stopForRoomExit);
|
||||
_syncPublishingState(force: true);
|
||||
}
|
||||
|
||||
@ -364,6 +367,7 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
}
|
||||
_isStarting = true;
|
||||
try {
|
||||
await _configureIosAudioSessionIfNeeded();
|
||||
await _waitForStartInterval();
|
||||
_lastLoopback = !rtcProvider.isOnMai();
|
||||
await rtcProvider.startRoomMusicAudioMixing(
|
||||
@ -404,6 +408,31 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _configureIosAudioSessionIfNeeded() async {
|
||||
if (!Platform.isIOS) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final session = await AudioSession.instance;
|
||||
await session.configure(
|
||||
AudioSessionConfiguration(
|
||||
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
|
||||
avAudioSessionCategoryOptions:
|
||||
AVAudioSessionCategoryOptions.mixWithOthers |
|
||||
AVAudioSessionCategoryOptions.defaultToSpeaker |
|
||||
AVAudioSessionCategoryOptions.allowBluetooth |
|
||||
AVAudioSessionCategoryOptions.allowBluetoothA2dp,
|
||||
avAudioSessionMode: AVAudioSessionMode.voiceChat,
|
||||
avAudioSessionRouteSharingPolicy:
|
||||
AVAudioSessionRouteSharingPolicy.defaultPolicy,
|
||||
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
// TRTC can still manage the iOS audio session if this best-effort setup fails.
|
||||
}
|
||||
}
|
||||
|
||||
void _startPositionTimer() {
|
||||
_positionTimer?.cancel();
|
||||
_positionTimer = Timer.periodic(const Duration(milliseconds: 800), (_) {
|
||||
@ -554,6 +583,7 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
_publishingStateHeartbeatTimer?.cancel();
|
||||
_rtcProvider?.setRoomMusicMixingStateListener(null);
|
||||
_rtcProvider?.setRoomMusicMicRouteChangedListener(null);
|
||||
_rtcProvider?.setRoomMusicSessionExitListener(null);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||
@ -11,6 +10,8 @@ import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
||||
|
||||
class SCPickUtils {
|
||||
static final ImagePicker _pkr = ImagePicker();
|
||||
static const int _defaultMaxImageBytes = 4000000;
|
||||
static const int _gifOriginalMaxImageBytes = 10 * 1024 * 1024;
|
||||
|
||||
static void pickImage(
|
||||
BuildContext context,
|
||||
@ -18,27 +19,51 @@ class SCPickUtils {
|
||||
double? aspectRatio,
|
||||
bool? backOriginalFile = true,
|
||||
bool? neeCrop = true,
|
||||
bool allowGif = false,
|
||||
bool skipCropForGif = true,
|
||||
}) async {
|
||||
try {
|
||||
final XFile? pickedFile = await _pkr.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 90,
|
||||
maxWidth: 1920,
|
||||
maxHeight: 1080,
|
||||
);
|
||||
final XFile? pickedFile =
|
||||
allowGif
|
||||
? await _pkr.pickImage(source: ImageSource.gallery)
|
||||
: await _pkr.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 90,
|
||||
maxWidth: 1920,
|
||||
maxHeight: 1080,
|
||||
);
|
||||
|
||||
if (pickedFile == null) return;
|
||||
if (!SCPathUtils.fileTypeIsPic(pickedFile.path)) {
|
||||
SCTts.show("Please select sc_images in .jpg, .jpeg, .png format.");
|
||||
final isGif = await _igif(pickedFile);
|
||||
if (!context.mounted) {
|
||||
onUpLoadCallBack(false, "");
|
||||
return;
|
||||
}
|
||||
// 检查是否为 GIF 图像
|
||||
if (await _igif(pickedFile)) {
|
||||
SCTts.show("GIF sc_images are not supported");
|
||||
if (isGif) {
|
||||
if (!allowGif) {
|
||||
SCTts.show("GIF sc_images are not supported");
|
||||
return;
|
||||
}
|
||||
} else if (!SCPathUtils.fileTypeIsPic(pickedFile.path)) {
|
||||
SCTts.show(
|
||||
allowGif
|
||||
? "Please select sc_images in .jpg, .jpeg, .png, .gif format."
|
||||
: "Please select sc_images in .jpg, .jpeg, .png format.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final File imageFile = File(pickedFile.path);
|
||||
if (isGif && skipCropForGif) {
|
||||
await _uploadOriginalImage(
|
||||
context,
|
||||
imageFile,
|
||||
onUpLoadCallBack,
|
||||
maxBytes: _gifOriginalMaxImageBytes,
|
||||
oversizeMessage: "The GIF image size cannot exceed 10M",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (neeCrop ?? false) {
|
||||
cropImage(
|
||||
imageFile,
|
||||
@ -48,28 +73,45 @@ class SCPickUtils {
|
||||
onUpLoadCallBack,
|
||||
);
|
||||
} else {
|
||||
if (imageFile.lengthSync() > 4000000) {
|
||||
SCTts.show(SCAppLocalizations.of(context)!.theImageSizeCannotExceed);
|
||||
onUpLoadCallBack?.call(false, "");
|
||||
return;
|
||||
}
|
||||
SCLoadingManager.show(context: context);
|
||||
try {
|
||||
String fileUrl = await SCGeneralRepositoryImp().upload(imageFile);
|
||||
SCLoadingManager.hide();
|
||||
onUpLoadCallBack?.call(true, fileUrl);
|
||||
} catch (e) {
|
||||
SCTts.show("upload fail $e");
|
||||
onUpLoadCallBack?.call(false, "");
|
||||
SCLoadingManager.hide();
|
||||
}
|
||||
await _uploadOriginalImage(
|
||||
context,
|
||||
imageFile,
|
||||
onUpLoadCallBack,
|
||||
maxBytes: _defaultMaxImageBytes,
|
||||
oversizeMessage:
|
||||
SCAppLocalizations.of(context)!.theImageSizeCannotExceed,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
onUpLoadCallBack?.call(false, "");
|
||||
onUpLoadCallBack(false, "");
|
||||
SCTts.show("Failed to select image");
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _uploadOriginalImage(
|
||||
BuildContext context,
|
||||
File imageFile,
|
||||
OnUpLoadCallBack onUpLoadCallBack, {
|
||||
required int maxBytes,
|
||||
required String oversizeMessage,
|
||||
}) async {
|
||||
if (imageFile.lengthSync() > maxBytes) {
|
||||
SCTts.show(oversizeMessage);
|
||||
onUpLoadCallBack(false, "");
|
||||
return;
|
||||
}
|
||||
SCLoadingManager.show(context: context);
|
||||
try {
|
||||
String fileUrl = await SCGeneralRepositoryImp().upload(imageFile);
|
||||
SCLoadingManager.hide();
|
||||
onUpLoadCallBack(true, fileUrl);
|
||||
} catch (e) {
|
||||
SCTts.show("upload fail $e");
|
||||
onUpLoadCallBack(false, "");
|
||||
SCLoadingManager.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查图像是否为 GIF 格式
|
||||
static Future<bool> _igif(XFile file) async {
|
||||
try {
|
||||
@ -165,7 +207,12 @@ class SCPickUtils {
|
||||
if (pickedFile == null) return;
|
||||
|
||||
// 相机拍摄的照片不会是 GIF,但为了安全也可以检查
|
||||
if (await _igif(pickedFile)) {
|
||||
final isGif = await _igif(pickedFile);
|
||||
if (!context.mounted) {
|
||||
onUpLoadCallBack(false, "");
|
||||
return;
|
||||
}
|
||||
if (isGif) {
|
||||
SCTts.show("Unsupported image format");
|
||||
return;
|
||||
}
|
||||
@ -209,9 +256,6 @@ class SCPickUtils {
|
||||
OnUpLoadCallBack onUpLoadCallBack, {
|
||||
bool needUpload = true,
|
||||
}) async {
|
||||
if (originalImage == null) {
|
||||
return;
|
||||
}
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
|
||||
@ -49,6 +49,7 @@ Widget head({
|
||||
String? headdressCover,
|
||||
BoxShape shape = BoxShape.circle,
|
||||
BoxFit fit = BoxFit.cover,
|
||||
BoxFit gifFit = BoxFit.contain,
|
||||
BorderRadius? borderRadius,
|
||||
bool showDefault = true,
|
||||
bool isRoom = false,
|
||||
@ -85,6 +86,7 @@ Widget head({
|
||||
width: avatarWidth,
|
||||
height: avatarHeight,
|
||||
fit: fit,
|
||||
gifFit: gifFit,
|
||||
noDefaultImg: !showDefault,
|
||||
defaultImg: "sc_images/general/sc_icon_avar_defalt.png",
|
||||
),
|
||||
@ -221,19 +223,28 @@ Widget netImage({
|
||||
double? height,
|
||||
BorderRadius? borderRadius,
|
||||
BoxFit? fit,
|
||||
BoxFit? gifFit,
|
||||
AlignmentGeometry? gifAlignment,
|
||||
BoxShape? shape,
|
||||
Border? border,
|
||||
Widget? loadingWidget,
|
||||
Widget? errorWidget,
|
||||
}) {
|
||||
final isGif = SCPathUtils.getFileExtension(url) == ".gif";
|
||||
final resolvedHeight = height ?? width;
|
||||
final resolvedFit =
|
||||
isGif ? (gifFit ?? fit ?? BoxFit.cover) : (fit ?? BoxFit.cover);
|
||||
final resolvedAlignment =
|
||||
isGif ? (gifAlignment ?? Alignment.center) : Alignment.center;
|
||||
return ExtendedImage.network(
|
||||
url,
|
||||
width: width,
|
||||
height: height ?? width,
|
||||
cacheWidth: _resolveImageCacheDimension(width),
|
||||
cacheHeight: _resolveImageCacheDimension(height ?? width),
|
||||
height: resolvedHeight,
|
||||
cacheWidth: isGif ? null : _resolveImageCacheDimension(width),
|
||||
cacheHeight: isGif ? null : _resolveImageCacheDimension(resolvedHeight),
|
||||
headers: buildNetworkImageHeaders(url),
|
||||
fit: fit ?? BoxFit.cover,
|
||||
fit: resolvedFit,
|
||||
alignment: resolvedAlignment,
|
||||
cache: true,
|
||||
shape: shape ?? BoxShape.rectangle,
|
||||
borderRadius: borderRadius,
|
||||
@ -246,7 +257,10 @@ Widget netImage({
|
||||
if (state.extendedImageLoadState == LoadState.completed) {
|
||||
return ExtendedRawImage(
|
||||
image: state.extendedImageInfo?.image,
|
||||
fit: fit ?? BoxFit.cover,
|
||||
width: width,
|
||||
height: resolvedHeight,
|
||||
fit: resolvedFit,
|
||||
alignment: resolvedAlignment,
|
||||
);
|
||||
} else if (state.extendedImageLoadState == LoadState.failed) {
|
||||
if (errorWidget != null) {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:yumi/app_localizations.dart';
|
||||
@ -10,7 +8,6 @@ import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||
import 'package:yumi/modules/room/voice_room_route.dart';
|
||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||
import 'package:yumi/services/gift/gift_animation_manager.dart';
|
||||
import 'package:yumi/services/music/room_music_manager.dart';
|
||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
||||
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
||||
@ -88,7 +85,6 @@ class _ExitMinRoomPageState extends State<ExitMinRoomPage> {
|
||||
reason: 'room_minimize',
|
||||
);
|
||||
SCGiftVapSvgaManager().stopPlayback();
|
||||
unawaited(context.read<RoomMusicManager>().stopForRoomExit());
|
||||
giftAnimationManager.cleanupAnimationResources();
|
||||
SCFloatIchart().show();
|
||||
VoiceRoomRoute.popVoiceRoomToPrevious(context);
|
||||
|
||||
112
pubspec.lock
@ -65,6 +65,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.13.1"
|
||||
audio_session:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: audio_session
|
||||
sha256: "7217b229db57cc4dc577a8abb56b7429a5a212b978517a5be578704bfe5e568b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.3"
|
||||
audioplayers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -209,14 +217,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -393,6 +393,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: f13a03000d942e476bc1ff0a736d2e9de711d2f89a95cd4c1d88f861c3348387
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -660,14 +668,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "8.2.14"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -724,14 +724,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -947,6 +939,30 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
just_audio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio
|
||||
sha256: "9694e4734f515f2a052493d1d7e0d6de219ee0427c7c29492e246ff32a219908"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.10.5"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
sha256: "2532c8d6702528824445921c5ff10548b518b13f808c2e34c2fd54793b999a6a"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.6.0"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.4.16"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -979,14 +995,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
marquee:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1035,14 +1043,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.17.6"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1051,14 +1051,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "9.3.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1177,13 +1169,13 @@ packages:
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
version: "2.4.2"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1320,14 +1312,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
pull_to_refresh:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1344,14 +1328,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
record_use:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_use
|
||||
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1846,4 +1822,4 @@ packages:
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.11.0 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
flutter: ">=3.38.0"
|
||||
|
||||
22
pubspec.yaml
@ -136,15 +136,23 @@ dependencies:
|
||||
flutter_svg: ^2.3.0
|
||||
pag:
|
||||
path: ./local_packages/pag-1.0.7-patched
|
||||
just_audio: ^0.10.5
|
||||
audio_session: ^0.2.3
|
||||
file_picker: ^11.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_launcher_icons: ^0.14.4
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
flutter_launcher_icons: ^0.14.4
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
dependency_overrides:
|
||||
# path_provider_foundation 2.6.0 pulls in objective_c native assets that are
|
||||
# currently packaged as simulator frameworks and rejected by App Store upload.
|
||||
path_provider_foundation: 2.4.2
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||