Merge branch 'main' of gitea.haiyihy.com:hy/chatapp3-flutter

This commit is contained in:
ZuoZuo 2026-04-14 14:50:37 +08:00
commit 905e48592e
9 changed files with 719 additions and 559 deletions

View File

@ -29,6 +29,14 @@
"certificate_hash": "9ab21924bfbe2c56555860ebabf23c4099fd7412"
}
},
{
"client_id": "980005024266-vrrp2us89svbqgc93oe7q4ad7uoh8fl6.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.org.yumi",
"certificate_hash": "c180afbf6e1ff2f449587287a2bc5407e7d2d9a3"
}
},
{
"client_id": "980005024266-dgtthe3q98k8tk873rfdrsnu5ot61p09.apps.googleusercontent.com",
"client_type": 3
@ -52,4 +60,4 @@
}
],
"configuration_version": "1"
}
}

View File

@ -8,6 +8,7 @@ import 'package:yumi/shared/business_logic/models/res/sc_edit_room_info_res.dart
import 'package:yumi/shared/business_logic/models/res/my_room_res.dart';
import 'package:yumi/shared/business_logic/models/res/sc_room_contribute_level_res.dart';
class SocialChatRoomManager extends ChangeNotifier {
MyRoomRes? myRoom;
SCRoomContributeLevelRes? roomContributeLevelRes;
@ -20,8 +21,15 @@ class SocialChatRoomManager extends ChangeNotifier {
void updateMyRoomInfo(SCEditRoomInfoRes roomInfo) {
if (myRoom != null && myRoom!.id == roomInfo.id) {
myRoom?.setRoomName = roomInfo.roomName ?? "";
myRoom?.setRoomDesc = roomInfo.roomDesc ?? "";
if (roomInfo.roomCover != null) {
myRoom?.setRoomCover = roomInfo.roomCover!;
}
if (roomInfo.roomName != null) {
myRoom?.setRoomName = roomInfo.roomName!;
}
if (roomInfo.roomDesc != null) {
myRoom?.setRoomDesc = roomInfo.roomDesc!;
}
notifyListeners();
}
}
@ -31,10 +39,15 @@ class SocialChatRoomManager extends ChangeNotifier {
// 使
if (customName != null) {
// TODO:
print('创建房间使用自定义名称: $customName');
debugPrint('创建房间使用自定义名称: $customName');
}
await SCAccountRepository().createRoom();
myRoom = await SCAccountRepository().myProfile();
if (!context.mounted) {
notifyListeners();
SCLoadingManager.hide();
return;
}
SCTts.show(SCAppLocalizations.of(context)!.createRoomSuccsess);
notifyListeners();
SCLoadingManager.hide();
@ -44,7 +57,7 @@ class SocialChatRoomManager extends ChangeNotifier {
if (forceRefresh) {
roomContributeLevelRes = null;
}
SCChatRoomRepository().roomContributionActivity(roomId).then((value){
SCChatRoomRepository().roomContributionActivity(roomId).then((value) {
roomContributeLevelRes = value;
notifyListeners();
});

View File

@ -6,24 +6,22 @@ import 'package:yumi/shared/business_logic/models/res/login_res.dart';
/// roomProfile : {"countryCode":"","countryName":"","event":"","hotRoom":false,"id":"0","layoutKey":"","nationalFlag":"","roomAccount":"","roomBadgeIcons":[""],"roomCover":"","roomDesc":"","roomGameIcon":"","roomName":"","sysOrigin":"","userId":"0","userProfile":{"account":"","accountStatus":"","age":0,"bornDay":0,"bornMonth":0,"bornYear":0,"countryCode":"","countryId":0,"countryName":"","createTime":0,"del":false,"freezingTime":0,"id":"s","originSys":"","ownSpecialId":{"account":"","expiredTime":0},"sameRegion":false,"sysOriginChild":"","useProps":[{"expireTime":0,"propsResources":{"amount":0.0,"code":"","cover":"","expand":"","id":0,"name":"","sourceUrl":"","type":""},"userId":0}],"userAvatar":"","userNickname":"","userSex":0,"wearBadge":[{"animationUrl":"","badgeKey":"","badgeLevel":0,"badgeName":"","expireTime":0,"id":0,"milestone":0,"notSelectUrl":"","selectUrl":"","type":"","userId":0}]},"userSVipLevel":""}
class FollowRoomRes {
FollowRoomRes({
String? id,
RoomProfile? roomProfile,}){
FollowRoomRes({String? id, RoomProfile? roomProfile}) {
_id = id;
_roomProfile = roomProfile;
}
}
FollowRoomRes.fromJson(dynamic json) {
_id = json['id'];
_roomProfile = json['roomProfile'] != null ? RoomProfile.fromJson(json['roomProfile']) : null;
_roomProfile =
json['roomProfile'] != null
? RoomProfile.fromJson(json['roomProfile'])
: null;
}
String? _id;
RoomProfile? _roomProfile;
FollowRoomRes copyWith({ String? id,
RoomProfile? roomProfile,
}) => FollowRoomRes( id: id ?? _id,
roomProfile: roomProfile ?? _roomProfile,
);
FollowRoomRes copyWith({String? id, RoomProfile? roomProfile}) =>
FollowRoomRes(id: id ?? _id, roomProfile: roomProfile ?? _roomProfile);
String? get id => _id;
RoomProfile? get roomProfile => _roomProfile;
@ -35,7 +33,6 @@ FollowRoomRes copyWith({ String? id,
}
return map;
}
}
/// countryCode : ""
@ -58,23 +55,25 @@ FollowRoomRes copyWith({ String? id,
class RoomProfile {
RoomProfile({
String? countryCode,
String? countryName,
String? event,
bool? hotRoom,
String? id,
String? layoutKey,
String? nationalFlag,
String? roomAccount,
List<String>? roomBadgeIcons,
String? roomCover,
String? roomDesc,
String? roomGameIcon,
String? roomName,
String? sysOrigin,
String? userId,
SocialChatUserProfile? userProfile,
String? userSVipLevel,ExtValues? extValues,}){
String? countryCode,
String? countryName,
String? event,
bool? hotRoom,
String? id,
String? layoutKey,
String? nationalFlag,
String? roomAccount,
List<String>? roomBadgeIcons,
String? roomCover,
String? roomDesc,
String? roomGameIcon,
String? roomName,
String? sysOrigin,
String? userId,
SocialChatUserProfile? userProfile,
String? userSVipLevel,
ExtValues? extValues,
}) {
_countryCode = countryCode;
_countryName = countryName;
_event = event;
@ -93,7 +92,7 @@ class RoomProfile {
_userProfile = userProfile;
_userSVipLevel = userSVipLevel;
_extValues = extValues;
}
}
RoomProfile.fromJson(dynamic json) {
_countryCode = json['countryCode'];
@ -104,19 +103,25 @@ class RoomProfile {
_layoutKey = json['layoutKey'];
_nationalFlag = json['nationalFlag'];
_roomAccount = json['roomAccount'];
_roomBadgeIcons = json['roomBadgeIcons'] != null ? json['roomBadgeIcons'].cast<String>() : [];
_roomCover = json['roomCover'];
_roomBadgeIcons =
json['roomBadgeIcons'] != null
? json['roomBadgeIcons'].cast<String>()
: [];
_roomCover = json['roomCover'] ?? json['cover'];
_roomDesc = json['roomDesc'];
_roomGameIcon = json['roomGameIcon'];
_roomName = json['roomName'];
_sysOrigin = json['sysOrigin'];
_userId = json['userId'];
_userProfile = json['userProfile'] != null ? SocialChatUserProfile.fromJson(json['userProfile']) : null;
_userProfile =
json['userProfile'] != null
? SocialChatUserProfile.fromJson(json['userProfile'])
: null;
_userSVipLevel = json['userSVipLevel'];
_extValues =
json['extValues'] != null
? ExtValues.fromJson(json['extValues'])
: null;
json['extValues'] != null
? ExtValues.fromJson(json['extValues'])
: null;
}
String? _countryCode;
String? _countryName;
@ -136,43 +141,45 @@ class RoomProfile {
SocialChatUserProfile? _userProfile;
String? _userSVipLevel;
ExtValues? _extValues;
RoomProfile copyWith({ String? countryCode,
String? countryName,
String? event,
bool? hotRoom,
String? id,
String? layoutKey,
String? nationalFlag,
String? roomAccount,
List<String>? roomBadgeIcons,
String? roomCover,
String? roomDesc,
String? roomGameIcon,
String? roomName,
String? sysOrigin,
String? userId,
SocialChatUserProfile? userProfile,
String? userSVipLevel,
ExtValues? extValues,
}) => RoomProfile( countryCode: countryCode ?? _countryCode,
countryName: countryName ?? _countryName,
event: event ?? _event,
hotRoom: hotRoom ?? _hotRoom,
id: id ?? _id,
layoutKey: layoutKey ?? _layoutKey,
nationalFlag: nationalFlag ?? _nationalFlag,
roomAccount: roomAccount ?? _roomAccount,
roomBadgeIcons: roomBadgeIcons ?? _roomBadgeIcons,
roomCover: roomCover ?? _roomCover,
roomDesc: roomDesc ?? _roomDesc,
roomGameIcon: roomGameIcon ?? _roomGameIcon,
roomName: roomName ?? _roomName,
sysOrigin: sysOrigin ?? _sysOrigin,
userId: userId ?? _userId,
userProfile: userProfile ?? _userProfile,
userSVipLevel: userSVipLevel ?? _userSVipLevel,
extValues: extValues ?? _extValues,
);
RoomProfile copyWith({
String? countryCode,
String? countryName,
String? event,
bool? hotRoom,
String? id,
String? layoutKey,
String? nationalFlag,
String? roomAccount,
List<String>? roomBadgeIcons,
String? roomCover,
String? roomDesc,
String? roomGameIcon,
String? roomName,
String? sysOrigin,
String? userId,
SocialChatUserProfile? userProfile,
String? userSVipLevel,
ExtValues? extValues,
}) => RoomProfile(
countryCode: countryCode ?? _countryCode,
countryName: countryName ?? _countryName,
event: event ?? _event,
hotRoom: hotRoom ?? _hotRoom,
id: id ?? _id,
layoutKey: layoutKey ?? _layoutKey,
nationalFlag: nationalFlag ?? _nationalFlag,
roomAccount: roomAccount ?? _roomAccount,
roomBadgeIcons: roomBadgeIcons ?? _roomBadgeIcons,
roomCover: roomCover ?? _roomCover,
roomDesc: roomDesc ?? _roomDesc,
roomGameIcon: roomGameIcon ?? _roomGameIcon,
roomName: roomName ?? _roomName,
sysOrigin: sysOrigin ?? _sysOrigin,
userId: userId ?? _userId,
userProfile: userProfile ?? _userProfile,
userSVipLevel: userSVipLevel ?? _userSVipLevel,
extValues: extValues ?? _extValues,
);
String? get countryCode => _countryCode;
String? get countryName => _countryName;
String? get event => _event;
@ -218,7 +225,6 @@ RoomProfile copyWith({ String? countryCode,
}
return map;
}
}
/// account : ""
@ -246,17 +252,18 @@ RoomProfile copyWith({ String? countryCode,
class WearBadge {
WearBadge({
String? animationUrl,
String? badgeKey,
num? badgeLevel,
String? badgeName,
num? expireTime,
num? id,
num? milestone,
String? notSelectUrl,
String? selectUrl,
String? type,
num? userId,}){
String? animationUrl,
String? badgeKey,
num? badgeLevel,
String? badgeName,
num? expireTime,
num? id,
num? milestone,
String? notSelectUrl,
String? selectUrl,
String? type,
num? userId,
}) {
_animationUrl = animationUrl;
_badgeKey = badgeKey;
_badgeLevel = badgeLevel;
@ -268,7 +275,7 @@ class WearBadge {
_selectUrl = selectUrl;
_type = type;
_userId = userId;
}
}
WearBadge.fromJson(dynamic json) {
_animationUrl = json['animationUrl'];
@ -294,29 +301,31 @@ class WearBadge {
String? _selectUrl;
String? _type;
num? _userId;
WearBadge copyWith({ String? animationUrl,
String? badgeKey,
num? badgeLevel,
String? badgeName,
num? expireTime,
num? id,
num? milestone,
String? notSelectUrl,
String? selectUrl,
String? type,
num? userId,
}) => WearBadge( animationUrl: animationUrl ?? _animationUrl,
badgeKey: badgeKey ?? _badgeKey,
badgeLevel: badgeLevel ?? _badgeLevel,
badgeName: badgeName ?? _badgeName,
expireTime: expireTime ?? _expireTime,
id: id ?? _id,
milestone: milestone ?? _milestone,
notSelectUrl: notSelectUrl ?? _notSelectUrl,
selectUrl: selectUrl ?? _selectUrl,
type: type ?? _type,
userId: userId ?? _userId,
);
WearBadge copyWith({
String? animationUrl,
String? badgeKey,
num? badgeLevel,
String? badgeName,
num? expireTime,
num? id,
num? milestone,
String? notSelectUrl,
String? selectUrl,
String? type,
num? userId,
}) => WearBadge(
animationUrl: animationUrl ?? _animationUrl,
badgeKey: badgeKey ?? _badgeKey,
badgeLevel: badgeLevel ?? _badgeLevel,
badgeName: badgeName ?? _badgeName,
expireTime: expireTime ?? _expireTime,
id: id ?? _id,
milestone: milestone ?? _milestone,
notSelectUrl: notSelectUrl ?? _notSelectUrl,
selectUrl: selectUrl ?? _selectUrl,
type: type ?? _type,
userId: userId ?? _userId,
);
String? get animationUrl => _animationUrl;
String? get badgeKey => _badgeKey;
num? get badgeLevel => _badgeLevel;
@ -344,7 +353,6 @@ WearBadge copyWith({ String? animationUrl,
map['userId'] = _userId;
return map;
}
}
/// expireTime : 0
@ -352,30 +360,32 @@ WearBadge copyWith({ String? animationUrl,
/// userId : 0
class UseProps {
UseProps({
num? expireTime,
PropsResources? propsResources,
num? userId,}){
UseProps({num? expireTime, PropsResources? propsResources, num? userId}) {
_expireTime = expireTime;
_propsResources = propsResources;
_userId = userId;
}
}
UseProps.fromJson(dynamic json) {
_expireTime = json['expireTime'];
_propsResources = json['propsResources'] != null ? PropsResources.fromJson(json['propsResources']) : null;
_propsResources =
json['propsResources'] != null
? PropsResources.fromJson(json['propsResources'])
: null;
_userId = json['userId'];
}
num? _expireTime;
PropsResources? _propsResources;
num? _userId;
UseProps copyWith({ num? expireTime,
PropsResources? propsResources,
num? userId,
}) => UseProps( expireTime: expireTime ?? _expireTime,
propsResources: propsResources ?? _propsResources,
userId: userId ?? _userId,
);
UseProps copyWith({
num? expireTime,
PropsResources? propsResources,
num? userId,
}) => UseProps(
expireTime: expireTime ?? _expireTime,
propsResources: propsResources ?? _propsResources,
userId: userId ?? _userId,
);
num? get expireTime => _expireTime;
PropsResources? get propsResources => _propsResources;
num? get userId => _userId;
@ -389,7 +399,6 @@ UseProps copyWith({ num? expireTime,
map['userId'] = _userId;
return map;
}
}
/// amount : 0.0
@ -403,14 +412,15 @@ UseProps copyWith({ num? expireTime,
class PropsResources {
PropsResources({
num? amount,
String? code,
String? cover,
String? expand,
num? id,
String? name,
String? sourceUrl,
String? type,}){
num? amount,
String? code,
String? cover,
String? expand,
num? id,
String? name,
String? sourceUrl,
String? type,
}) {
_amount = amount;
_code = code;
_cover = cover;
@ -419,7 +429,7 @@ class PropsResources {
_name = name;
_sourceUrl = sourceUrl;
_type = type;
}
}
PropsResources.fromJson(dynamic json) {
_amount = json['amount'];
@ -439,23 +449,25 @@ class PropsResources {
String? _name;
String? _sourceUrl;
String? _type;
PropsResources copyWith({ num? amount,
String? code,
String? cover,
String? expand,
num? id,
String? name,
String? sourceUrl,
String? type,
}) => PropsResources( amount: amount ?? _amount,
code: code ?? _code,
cover: cover ?? _cover,
expand: expand ?? _expand,
id: id ?? _id,
name: name ?? _name,
sourceUrl: sourceUrl ?? _sourceUrl,
type: type ?? _type,
);
PropsResources copyWith({
num? amount,
String? code,
String? cover,
String? expand,
num? id,
String? name,
String? sourceUrl,
String? type,
}) => PropsResources(
amount: amount ?? _amount,
code: code ?? _code,
cover: cover ?? _cover,
expand: expand ?? _expand,
id: id ?? _id,
name: name ?? _name,
sourceUrl: sourceUrl ?? _sourceUrl,
type: type ?? _type,
);
num? get amount => _amount;
String? get code => _code;
String? get cover => _cover;
@ -477,19 +489,16 @@ PropsResources copyWith({ num? amount,
map['type'] = _type;
return map;
}
}
/// account : ""
/// expiredTime : 0
class OwnSpecialId {
OwnSpecialId({
String? account,
num? expiredTime,}){
OwnSpecialId({String? account, num? expiredTime}) {
_account = account;
_expiredTime = expiredTime;
}
}
OwnSpecialId.fromJson(dynamic json) {
_account = json['account'];
@ -497,11 +506,10 @@ class OwnSpecialId {
}
String? _account;
num? _expiredTime;
OwnSpecialId copyWith({ String? account,
num? expiredTime,
}) => OwnSpecialId( account: account ?? _account,
expiredTime: expiredTime ?? _expiredTime,
);
OwnSpecialId copyWith({String? account, num? expiredTime}) => OwnSpecialId(
account: account ?? _account,
expiredTime: expiredTime ?? _expiredTime,
);
String? get account => _account;
num? get expiredTime => _expiredTime;
@ -511,5 +519,4 @@ OwnSpecialId copyWith({ String? account,
map['expiredTime'] = _expiredTime;
return map;
}
}
}

View File

@ -696,7 +696,7 @@ class RoomProfile2 {
_langCode = json['langCode'];
_nationalFlag = json['nationalFlag'];
_roomAccount = json['roomAccount'];
_roomCover = json['roomCover'];
_roomCover = json['roomCover'] ?? json['cover'];
_roomDesc = json['roomDesc'];
_roomName = json['roomName'];
_sysOrigin = json['sysOrigin'];

View File

@ -43,7 +43,8 @@ class MyRoomRes {
num? activeTime,
RoomSetting? setting,
RoomCounter? counter,
List<UseBadges>? useBadges,}){
List<UseBadges>? useBadges,
}) {
_id = id;
_roomAccount = roomAccount;
_userId = userId;
@ -69,7 +70,7 @@ class MyRoomRes {
_id = json['id'];
_roomAccount = json['roomAccount'];
_userId = json['userId'];
_roomCover = json['roomCover'];
_roomCover = json['roomCover'] ?? json['cover'];
_roomName = json['roomName'];
_roomDesc = json['roomDesc'];
_event = json['event'];
@ -82,8 +83,10 @@ class MyRoomRes {
_createTime = json['createTime'];
_updateTime = json['updateTime'];
_activeTime = json['activeTime'];
_setting = json['setting'] != null ? RoomSetting.fromJson(json['setting']) : null;
_counter = json['counter'] != null ? RoomCounter.fromJson(json['counter']) : null;
_setting =
json['setting'] != null ? RoomSetting.fromJson(json['setting']) : null;
_counter =
json['counter'] != null ? RoomCounter.fromJson(json['counter']) : null;
if (json['useBadges'] != null) {
_useBadges = [];
json['useBadges'].forEach((v) {
@ -110,7 +113,8 @@ class MyRoomRes {
RoomSetting? _setting;
RoomCounter? _counter;
List<UseBadges>? _useBadges;
MyRoomRes copyWith({ String? id,
MyRoomRes copyWith({
String? id,
String? roomAccount,
String? userId,
String? roomCover,
@ -129,7 +133,8 @@ class MyRoomRes {
RoomSetting? setting,
RoomCounter? counter,
List<UseBadges>? useBadges,
}) => MyRoomRes( id: id ?? _id,
}) => MyRoomRes(
id: id ?? _id,
roomAccount: roomAccount ?? _roomAccount,
userId: userId ?? _userId,
roomCover: roomCover ?? _roomCover,
@ -201,7 +206,6 @@ class MyRoomRes {
}
return map;
}
}
/// id : 0
@ -228,7 +232,8 @@ class UseBadges {
String? selectUrl,
String? notSelectUrl,
String? animationUrl,
String? expireTime,}){
String? expireTime,
}) {
_id = id;
_userId = userId;
_badgeLevel = badgeLevel;
@ -266,7 +271,8 @@ class UseBadges {
String? _notSelectUrl;
String? _animationUrl;
String? _expireTime;
UseBadges copyWith({ num? id,
UseBadges copyWith({
num? id,
num? userId,
num? badgeLevel,
num? milestone,
@ -277,7 +283,8 @@ class UseBadges {
String? notSelectUrl,
String? animationUrl,
String? expireTime,
}) => UseBadges( id: id ?? _id,
}) => UseBadges(
id: id ?? _id,
userId: userId ?? _userId,
badgeLevel: badgeLevel ?? _badgeLevel,
milestone: milestone ?? _milestone,
@ -316,5 +323,4 @@ class UseBadges {
map['expireTime'] = _expireTime;
return map;
}
}

View File

@ -74,7 +74,7 @@ class SocialChatRoomRes {
.map((item) => item as String)
.toList()
: null;
_roomCover = json['roomCover'];
_roomCover = json['roomCover'] ?? json['cover'];
_roomDesc = json['roomDesc'];
_roomGameIcon = json['roomGameIcon'];
_roomName = json['roomName'];
@ -86,9 +86,9 @@ class SocialChatRoomRes {
: null;
_userSVipLevel = json['userSVipLevel'];
_extValues =
json['extValues'] != null
? ExtValues.fromJson(json['extValues'])
: null;
json['extValues'] != null
? ExtValues.fromJson(json['extValues'])
: null;
}
String? _countryCode;
@ -441,7 +441,8 @@ class RoomSetting {
bool? showHeartbeat,
String? roomSpecialMikeType,
num? roomSpecialMikeExpireTime,
String? roomSpecialMikeExpireType,}){
String? roomSpecialMikeExpireType,
}) {
_password = password;
_takeMicRole = takeMicRole;
_touristMike = touristMike;
@ -488,7 +489,8 @@ class RoomSetting {
String? _roomSpecialMikeType;
num? _roomSpecialMikeExpireTime;
String? _roomSpecialMikeExpireType;
RoomSetting copyWith({ String? password,
RoomSetting copyWith({
String? password,
String? takeMicRole,
bool? touristMike,
bool? touristMsg,
@ -502,7 +504,8 @@ class RoomSetting {
String? roomSpecialMikeType,
num? roomSpecialMikeExpireTime,
String? roomSpecialMikeExpireType,
}) => RoomSetting( password: password ?? _password,
}) => RoomSetting(
password: password ?? _password,
takeMicRole: takeMicRole ?? _takeMicRole,
touristMike: touristMike ?? _touristMike,
touristMsg: touristMsg ?? _touristMsg,
@ -514,8 +517,10 @@ class RoomSetting {
adminLockSeat: adminLockSeat ?? _adminLockSeat,
showHeartbeat: showHeartbeat ?? _showHeartbeat,
roomSpecialMikeType: roomSpecialMikeType ?? _roomSpecialMikeType,
roomSpecialMikeExpireTime: roomSpecialMikeExpireTime ?? _roomSpecialMikeExpireTime,
roomSpecialMikeExpireType: roomSpecialMikeExpireType ?? _roomSpecialMikeExpireType,
roomSpecialMikeExpireTime:
roomSpecialMikeExpireTime ?? _roomSpecialMikeExpireTime,
roomSpecialMikeExpireType:
roomSpecialMikeExpireType ?? _roomSpecialMikeExpireType,
);
String? get password => _password;
String? get takeMicRole => _takeMicRole;
@ -550,8 +555,8 @@ class RoomSetting {
map['roomSpecialMikeExpireType'] = _roomSpecialMikeExpireType;
return map;
}
}
/// expireTime : 0
/// propsResources : {"amount":0.0,"code":"","cover":"","expand":"","id":0,"name":"","sourceUrl":"","type":""}
/// userId : 0

View File

@ -1,159 +1,161 @@
/// id : 0
/// roomAccount : ""
/// userId : 0
/// roomCover : ""
/// roomName : ""
/// roomDesc : ""
/// event : ""
/// sysOrigin : ""
/// countryCode : ""
/// countryName : ""
/// nationalFlag : ""
/// langCode : ""
/// del : false
/// createTime : 0
/// updateTime : 0
/// activeTime : 0
class SCEditRoomInfoRes {
SCEditRoomInfoRes({
String? id,
String? roomAccount,
String? userId,
String? roomCover,
String? roomName,
String? roomDesc,
String? event,
String? sysOrigin,
String? countryCode,
String? countryName,
String? nationalFlag,
String? langCode,
bool? del,
num? createTime,
num? updateTime,
num? activeTime,}){
_id = id;
_roomAccount = roomAccount;
_userId = userId;
_roomCover = roomCover;
_roomName = roomName;
_roomDesc = roomDesc;
_event = event;
_sysOrigin = sysOrigin;
_countryCode = countryCode;
_countryName = countryName;
_nationalFlag = nationalFlag;
_langCode = langCode;
_del = del;
_createTime = createTime;
_updateTime = updateTime;
_activeTime = activeTime;
}
SCEditRoomInfoRes.fromJson(dynamic json) {
_id = json['id'];
_roomAccount = json['roomAccount'];
_userId = json['userId'];
_roomCover = json['roomCover'];
_roomName = json['roomName'];
_roomDesc = json['roomDesc'];
_event = json['event'];
_sysOrigin = json['sysOrigin'];
_countryCode = json['countryCode'];
_countryName = json['countryName'];
_nationalFlag = json['nationalFlag'];
_langCode = json['langCode'];
_del = json['del'];
_createTime = json['createTime'];
_updateTime = json['updateTime'];
_activeTime = json['activeTime'];
}
String? _id;
String? _roomAccount;
String? _userId;
String? _roomCover;
String? _roomName;
String? _roomDesc;
String? _event;
String? _sysOrigin;
String? _countryCode;
String? _countryName;
String? _nationalFlag;
String? _langCode;
bool? _del;
num? _createTime;
num? _updateTime;
num? _activeTime;
SCEditRoomInfoRes copyWith({ String? id,
String? roomAccount,
String? userId,
String? roomCover,
String? roomName,
String? roomDesc,
String? event,
String? sysOrigin,
String? countryCode,
String? countryName,
String? nationalFlag,
String? langCode,
bool? del,
num? createTime,
num? updateTime,
num? activeTime,
}) => SCEditRoomInfoRes( id: id ?? _id,
roomAccount: roomAccount ?? _roomAccount,
userId: userId ?? _userId,
roomCover: roomCover ?? _roomCover,
roomName: roomName ?? _roomName,
roomDesc: roomDesc ?? _roomDesc,
event: event ?? _event,
sysOrigin: sysOrigin ?? _sysOrigin,
countryCode: countryCode ?? _countryCode,
countryName: countryName ?? _countryName,
nationalFlag: nationalFlag ?? _nationalFlag,
langCode: langCode ?? _langCode,
del: del ?? _del,
createTime: createTime ?? _createTime,
updateTime: updateTime ?? _updateTime,
activeTime: activeTime ?? _activeTime,
);
String? get id => _id;
String? get roomAccount => _roomAccount;
String? get userId => _userId;
String? get roomCover => _roomCover;
String? get roomName => _roomName;
String? get roomDesc => _roomDesc;
String? get event => _event;
String? get sysOrigin => _sysOrigin;
String? get countryCode => _countryCode;
String? get countryName => _countryName;
String? get nationalFlag => _nationalFlag;
String? get langCode => _langCode;
bool? get del => _del;
num? get createTime => _createTime;
num? get updateTime => _updateTime;
num? get activeTime => _activeTime;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = _id;
map['roomAccount'] = _roomAccount;
map['userId'] = _userId;
map['roomCover'] = _roomCover;
map['roomName'] = _roomName;
map['roomDesc'] = _roomDesc;
map['event'] = _event;
map['sysOrigin'] = _sysOrigin;
map['countryCode'] = _countryCode;
map['countryName'] = _countryName;
map['nationalFlag'] = _nationalFlag;
map['langCode'] = _langCode;
map['del'] = _del;
map['createTime'] = _createTime;
map['updateTime'] = _updateTime;
map['activeTime'] = _activeTime;
return map;
}
}
// id : 0
// roomAccount : ""
// userId : 0
// roomCover : ""
// roomName : ""
// roomDesc : ""
// event : ""
// sysOrigin : ""
// countryCode : ""
// countryName : ""
// nationalFlag : ""
// langCode : ""
// del : false
// createTime : 0
// updateTime : 0
// activeTime : 0
class SCEditRoomInfoRes {
SCEditRoomInfoRes({
String? id,
String? roomAccount,
String? userId,
String? roomCover,
String? roomName,
String? roomDesc,
String? event,
String? sysOrigin,
String? countryCode,
String? countryName,
String? nationalFlag,
String? langCode,
bool? del,
num? createTime,
num? updateTime,
num? activeTime,
}) {
_id = id;
_roomAccount = roomAccount;
_userId = userId;
_roomCover = roomCover;
_roomName = roomName;
_roomDesc = roomDesc;
_event = event;
_sysOrigin = sysOrigin;
_countryCode = countryCode;
_countryName = countryName;
_nationalFlag = nationalFlag;
_langCode = langCode;
_del = del;
_createTime = createTime;
_updateTime = updateTime;
_activeTime = activeTime;
}
SCEditRoomInfoRes.fromJson(dynamic json) {
_id = json['id'];
_roomAccount = json['roomAccount'];
_userId = json['userId'];
_roomCover = json['roomCover'] ?? json['cover'];
_roomName = json['roomName'];
_roomDesc = json['roomDesc'];
_event = json['event'];
_sysOrigin = json['sysOrigin'];
_countryCode = json['countryCode'];
_countryName = json['countryName'];
_nationalFlag = json['nationalFlag'];
_langCode = json['langCode'];
_del = json['del'];
_createTime = json['createTime'];
_updateTime = json['updateTime'];
_activeTime = json['activeTime'];
}
String? _id;
String? _roomAccount;
String? _userId;
String? _roomCover;
String? _roomName;
String? _roomDesc;
String? _event;
String? _sysOrigin;
String? _countryCode;
String? _countryName;
String? _nationalFlag;
String? _langCode;
bool? _del;
num? _createTime;
num? _updateTime;
num? _activeTime;
SCEditRoomInfoRes copyWith({
String? id,
String? roomAccount,
String? userId,
String? roomCover,
String? roomName,
String? roomDesc,
String? event,
String? sysOrigin,
String? countryCode,
String? countryName,
String? nationalFlag,
String? langCode,
bool? del,
num? createTime,
num? updateTime,
num? activeTime,
}) => SCEditRoomInfoRes(
id: id ?? _id,
roomAccount: roomAccount ?? _roomAccount,
userId: userId ?? _userId,
roomCover: roomCover ?? _roomCover,
roomName: roomName ?? _roomName,
roomDesc: roomDesc ?? _roomDesc,
event: event ?? _event,
sysOrigin: sysOrigin ?? _sysOrigin,
countryCode: countryCode ?? _countryCode,
countryName: countryName ?? _countryName,
nationalFlag: nationalFlag ?? _nationalFlag,
langCode: langCode ?? _langCode,
del: del ?? _del,
createTime: createTime ?? _createTime,
updateTime: updateTime ?? _updateTime,
activeTime: activeTime ?? _activeTime,
);
String? get id => _id;
String? get roomAccount => _roomAccount;
String? get userId => _userId;
String? get roomCover => _roomCover;
String? get roomName => _roomName;
String? get roomDesc => _roomDesc;
String? get event => _event;
String? get sysOrigin => _sysOrigin;
String? get countryCode => _countryCode;
String? get countryName => _countryName;
String? get nationalFlag => _nationalFlag;
String? get langCode => _langCode;
bool? get del => _del;
num? get createTime => _createTime;
num? get updateTime => _updateTime;
num? get activeTime => _activeTime;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = _id;
map['roomAccount'] = _roomAccount;
map['userId'] = _userId;
map['roomCover'] = _roomCover;
map['roomName'] = _roomName;
map['roomDesc'] = _roomDesc;
map['event'] = _event;
map['sysOrigin'] = _sysOrigin;
map['countryCode'] = _countryCode;
map['countryName'] = _countryName;
map['nationalFlag'] = _nationalFlag;
map['langCode'] = _langCode;
map['del'] = _del;
map['createTime'] = _createTime;
map['updateTime'] = _updateTime;
map['activeTime'] = _activeTime;
return map;
}
}

View File

@ -1,212 +1,316 @@
import 'dart:ui' as ui;
import 'package:extended_text_field/extended_text_field.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/app/constants/sc_room_msg_type.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/business_logic/usecases/sc_case.dart';
import 'package:provider/provider.dart';
import 'package:yumi/app/constants/sc_screen.dart';
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/services/audio/rtm_manager.dart';
///
class RoomMsgInput extends StatefulWidget {
String? atTextContent;
RoomMsgInput({this.atTextContent});
@override
_RoomMsgInputState createState() => _RoomMsgInputState();
}
class _RoomMsgInputState extends State<RoomMsgInput> {
bool showSend = false;
FocusNode msgNode = FocusNode();
TextEditingController controller = TextEditingController();
@override
void initState() {
super.initState();
if (widget.atTextContent != null) {
controller.value = TextEditingValue(text: widget.atTextContent ?? "");
}
}
@override
Widget build(BuildContext context) {
// FocusScope.of(context).requestFocus(msgNode);
return Scaffold(
backgroundColor: Colors.transparent,
body: Column(
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
),
),
float(context),
],
),
);
}
Widget float(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.black),
padding: EdgeInsets.symmetric(horizontal: 25.w, vertical: 12.w),
height: 56.0,
child: Container(
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(height(5)),
),
child: Row(
children: [
Expanded(
child: ExtendedTextField(
controller: controller,
textDirection:
widget.atTextContent != null ? TextDirection.ltr : null,
specialTextSpanBuilder: AtTextSpanBuilder(),
focusNode: msgNode,
autofocus: true,
onSubmitted: (s) {
Navigator.pop(context);
if (controller.text.isNotEmpty) {
var currenRoom =
Provider.of<RtcProvider>(
context,
listen: false,
).currenRoom;
if (currenRoom != null) {
Provider.of<RtmProvider>(context, listen: false).dispatchMessage(
Msg(
groupId:
currenRoom
.roomProfile
?.roomProfile
?.roomAccount ??
"",
role:
Provider.of<RtcProvider>(
context,
listen: false,
).currenRoom?.entrants?.roles ??
"",
msg: controller.text,
type: SCRoomMsgType.text,
user: AccountStorage().getCurrentUser()?.userProfile,
),
);
}
}
},
onChanged: (s) {
setState(() {
showSend = controller.text.isNotEmpty;
});
},
decoration: InputDecoration(
hintText: SCAppLocalizations.of(context)!.pleaseChatFfriendly,
fillColor: Colors.transparent,
hintStyle: TextStyle(color: Colors.white60, fontSize: sp(14)),
contentPadding: const EdgeInsets.symmetric(horizontal: 15),
counterText: '',
isDense: true,
filled: false,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
),
style: TextStyle(
fontSize: ScreenUtil().setSp(14),
color: Colors.white,
),
),
),
GestureDetector(
onTap: () {
Navigator.pop(context);
if (controller.text.isNotEmpty) {
var currenRoom =
Provider.of<RtcProvider>(
context,
listen: false,
).currenRoom;
if (currenRoom != null) {
Provider.of<RtmProvider>(context, listen: false).dispatchMessage(
Msg(
groupId:
currenRoom.roomProfile?.roomProfile?.roomAccount ??
"",
role:
Provider.of<RtcProvider>(
context,
listen: false,
).currenRoom?.entrants?.roles ??
"",
msg: controller.text,
type: SCRoomMsgType.text,
user: AccountStorage().getCurrentUser()?.userProfile,
),
addLocal: true,
);
}
}
},
child: Container(
padding: EdgeInsets.all(4.w),
width: 30.w,
height: 30.w,
child: Image.asset("sc_images/room/sc_icon_room_message_send.png"),
),
),
SizedBox(width: 3.w),
],
),
),
);
}
}
class PopRoute extends PopupRoute {
final Duration _duration = Duration(milliseconds: 350);
Widget child;
PopRoute({required this.child});
@override
Color? get barrierColor => null;
@override
bool get barrierDismissible => true;
@override
String? get barrierLabel => null;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return child;
}
@override
Duration get transitionDuration => _duration;
}
import 'package:extended_text/extended_text.dart';
import 'package:extended_text_field/extended_text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
import 'package:yumi/app/config/business_logic_strategy.dart';
import 'package:yumi/app/constants/sc_emoji_datas.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/app/constants/sc_room_msg_type.dart';
import 'package:yumi/app/constants/sc_screen.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/services/audio/rtm_manager.dart';
import 'package:yumi/shared/business_logic/usecases/sc_case.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/tools/sc_keybord_util.dart';
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
///
class RoomMsgInput extends StatefulWidget {
final String? atTextContent;
const RoomMsgInput({super.key, this.atTextContent});
@override
State<RoomMsgInput> createState() => _RoomMsgInputState();
}
class _RoomMsgInputState extends State<RoomMsgInput> {
bool showSend = false;
bool showEmoji = false;
final FocusNode msgNode = FocusNode();
final TextEditingController controller = TextEditingController();
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
Color get _panelBackgroundColor => Colors.white24;
@override
void initState() {
super.initState();
if (widget.atTextContent != null) {
controller.value = TextEditingValue(text: widget.atTextContent ?? "");
showSend = controller.text.trim().isNotEmpty;
}
}
@override
void dispose() {
controller.dispose();
msgNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: Column(
children: <Widget>[
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.pop(context);
},
),
),
AnimatedPadding(
duration: const Duration(milliseconds: 220),
curve: Curves.easeOut,
padding: EdgeInsets.only(bottom: keyboardHeight),
child: _buildBottomPanel(context),
),
],
),
);
}
Widget _buildBottomPanel(BuildContext context) {
return Container(
color: Colors.black,
child: SafeArea(
top: false,
child: Padding(
padding: EdgeInsets.fromLTRB(25.w, 12.w, 25.w, 12.w),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: _toggleEmojiPanel,
child: SizedBox(
width: 24.w,
height: 24.w,
child: Image.asset(
showEmoji
? _strategy.getSCMessageChatPageChatKeyboardIcon()
: _strategy.getSCMessageChatPageEmojiIcon(),
color: Colors.white,
fit: BoxFit.contain,
),
),
),
SizedBox(width: 15.w),
Expanded(child: _buildInputBar(context)),
],
),
if (showEmoji) SizedBox(height: 12.w),
if (showEmoji) _buildEmojiPanel(),
],
),
),
),
);
}
Widget _buildInputBar(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: _panelBackgroundColor,
borderRadius: BorderRadius.circular(height(5)),
),
child: Row(
children: [
Expanded(
child: ExtendedTextField(
controller: controller,
textDirection:
widget.atTextContent != null ? TextDirection.ltr : null,
specialTextSpanBuilder: AtTextSpanBuilder(),
focusNode: msgNode,
autofocus: true,
textInputAction: TextInputAction.send,
onTap: () {
if (showEmoji) {
setState(() {
showEmoji = false;
});
}
},
onSubmitted: (_) {
_sendMessage();
},
onChanged: (s) {
setState(() {
showSend = controller.text.trim().isNotEmpty;
});
},
decoration: InputDecoration(
hintText: SCAppLocalizations.of(context)!.pleaseChatFfriendly,
fillColor: Colors.transparent,
hintStyle: TextStyle(color: Colors.white60, fontSize: sp(14)),
contentPadding: EdgeInsets.symmetric(horizontal: 15.w),
counterText: '',
isDense: true,
filled: false,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
),
style: TextStyle(
fontSize: ScreenUtil().setSp(14),
color: Colors.white,
),
),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: _sendMessage,
child: Opacity(
opacity: showSend ? 1 : 0.5,
child: Container(
padding: EdgeInsets.all(4.w),
width: 30.w,
height: 30.w,
child: Image.asset(
"sc_images/room/sc_icon_room_message_send.png",
),
),
),
),
SizedBox(width: 3.w),
],
),
);
}
Widget _buildEmojiPanel() {
return Container(
height: 150.w,
decoration: BoxDecoration(
color: _panelBackgroundColor,
borderRadius: BorderRadius.circular(height(5)),
),
child: GridView.builder(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 10.w),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8,
crossAxisSpacing: 8.w,
mainAxisSpacing: 8.w,
),
itemCount: SCEmojiDatas.smileys.length,
itemBuilder: (context, index) {
final emoji = SCEmojiDatas.smileys[index];
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_insertEmoji(emoji);
},
child: Center(
child: ExtendedText(emoji, style: TextStyle(fontSize: sp(15))),
),
);
},
),
);
}
void _toggleEmojiPanel() {
if (showEmoji) {
setState(() {
showEmoji = false;
});
FocusScope.of(context).requestFocus(msgNode);
return;
}
SCKeybordUtil.hide(context);
setState(() {
showEmoji = true;
});
}
void _insertEmoji(String emoji) {
final value = controller.value;
final selection = value.selection;
final fallbackOffset = value.text.length;
final start = selection.isValid ? selection.start : fallbackOffset;
final end = selection.isValid ? selection.end : fallbackOffset;
final safeStart = start < 0 ? fallbackOffset : start;
final safeEnd = end < 0 ? fallbackOffset : end;
final nextText = value.text.replaceRange(safeStart, safeEnd, emoji);
controller.value = value.copyWith(
text: nextText,
selection: TextSelection.collapsed(offset: safeStart + emoji.length),
composing: TextRange.empty,
);
setState(() {
showSend = controller.text.trim().isNotEmpty;
});
}
void _sendMessage() {
if (controller.text.trim().isEmpty) {
return;
}
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
final currenRoom = rtcProvider.currenRoom;
if (currenRoom == null) {
return;
}
Provider.of<RtmProvider>(context, listen: false).dispatchMessage(
Msg(
groupId: currenRoom.roomProfile?.roomProfile?.roomAccount ?? "",
role: rtcProvider.currenRoom?.entrants?.roles ?? "",
msg: controller.text,
type: SCRoomMsgType.text,
user: AccountStorage().getCurrentUser()?.userProfile,
),
);
Navigator.pop(context);
}
}
class PopRoute extends PopupRoute {
final Duration _duration = Duration(milliseconds: 350);
Widget child;
PopRoute({required this.child});
@override
Color? get barrierColor => null;
@override
bool get barrierDismissible => true;
@override
String? get barrierLabel => null;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return child;
}
@override
Duration get transitionDuration => _duration;
}

View File

@ -5,6 +5,7 @@
## 已完成模块
- 创建并持续维护进度跟踪文件。
- 已修复首页房间封面显示链路:兼容接口 `roomCover/cover` 双字段,并补齐编辑房间成功后的封面内存回写。
- 完成仓库结构、依赖引用、资源体积和 APK 组成的第一轮排查。
- 移除 4 个当前未在业务层直接使用的插件依赖:`loading_indicator_view_plus``social_sharing_plus``flutter_foreground_task``on_audio_query`,并清理相关平台声明。
- 修补 `image_cropper 5.0.1` Android 兼容问题,切换到本地 path 依赖以恢复构建。
@ -30,6 +31,7 @@
- 已定位 `FOREGROUND_SERVICE_MEDIA_PROJECTION` 的来源为 Agora `full-screen-sharing` 依赖,并开始从主 Manifest 覆盖移除。
- 已移除 `FOREGROUND_SERVICE_MEDIA_PROJECTION` 相关声明并重新打出正式 `AAB`
- 已按当前需求打出正式 `arm64-v8a` 单架构 `APK`
- 已将新的 `google-services.json` 替换进工程,准备重新打包验证。
## 进行中模块
- 评估 iOS 正式签名参数接入后导出 `.ipa` 的流程。
@ -48,12 +50,19 @@
## 已改动文件
- `需求进度.md`
- `lib/services/room/rc_room_manager.dart`
- `lib/shared/business_logic/models/res/room_res.dart`
- `lib/shared/business_logic/models/res/follow_room_res.dart`
- `lib/shared/business_logic/models/res/my_room_res.dart`
- `lib/shared/business_logic/models/res/sc_edit_room_info_res.dart`
- `lib/shared/business_logic/models/res/join_room_res.dart`
- `.gitignore`
- `android/key.properties`
- `pubspec.yaml`
- `pubspec.lock`
- `lib/ui_kit/components/dialog/dialog.dart`
- `android/app/src/main/AndroidManifest.xml`
- `android/app/google-services.json`
- `ios/Runner/Info.plist`
- `scripts/tinypng_batch.rb`
- `scripts/build_release.py`
@ -73,6 +82,7 @@
## 已验证结果
- 当前工作区存在用户已有未提交改动,后续处理均避开覆盖。
- 首页 `party/follow/history/mine` 以及房间详情使用的房间封面模型已兼容 `roomCover``cover` 两种返回字段,上传封面后不会再因为字段名不一致掉回空占位。
- 目录体积排查显示:`build/``4.7G``.dart_tool/``347M``ios/``250M``sc_images/``48M``local_packages/``6.1M`
- 当前包体过大的主要原因已经确认:
- universal APK 带入了 `arm64-v8a``armeabi-v7a``x86_64` 三套 ABI。
@ -160,6 +170,11 @@
- 本轮按需执行 `flutter build apk --release --split-per-abi --target-platform android-arm64 --split-debug-info=build/symbols/android` 已成功,产物为:
- `build/app/outputs/flutter-apk/app-arm64-v8a-release.apk`
- 当前 `arm64-v8a` APK 体积约 `128.7MB`
- 已核对新旧 `google-services.json`
- 原文件与新文件都匹配包名 `com.org.yumi`
- 下载文件已成功覆盖到 `android/app/google-services.json`
- 当前工程内文件 `SHA256` 为:
- `a1706496a01f74d27e6c60598144cecc978934b02a87567e1ced887b5b0185d5`
## 已知问题
- `ios/Podfile.lock` 还保留旧插件记录,因为本轮未执行 `pod install`;但 Flutter 当前依赖链路已经不再包含已移除插件。