Compare commits

..

3 Commits

Author SHA1 Message Date
Max 5ccb5adb79 style: format code with line length 120 2026-01-07 20:53:52 +08:00
Max 0d58276ff6 fix: 修复选择@成员点击返回时的空类型错误
- Navigator.push 在用户取消时返回 null
- 将 selectedAtMemberList 类型改为可空
- 添加 null 检查避免 TypeError
2026-01-07 20:47:49 +08:00
Max 61aee808d0 fix: 空关键词搜索时不调用 API,避免错误 6017
- searchFriendByKey 添加空关键词检查
- searchGroupByKey 添加空关键词检查
- searchMsgByKey 添加空关键词检查
- 当关键词为空时直接清理数据,不请求 SDK
2026-01-07 20:45:51 +08:00
5 changed files with 91 additions and 28 deletions

26
.editorconfig Normal file
View File

@ -0,0 +1,26 @@
# 这是一个顶级配置文件,停止向父目录查找
root = true
# === 全局通用设置 (所有文件) ===
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# === Dart 文件专用设置 ===
[*.dart]
# SDK 原始代码使用较长的行宽,设置为 120 以保持一致
max_line_length = 120
indent_size = 2
# === YAML/JSON 文件 (配置文件) ===
[*.{yaml,yml,json}]
indent_size = 2
# === Markdown 文件 (文档) ===
[*.md]
trim_trailing_whitespace = false
max_line_length = 0

View File

@ -62,6 +62,12 @@ class TUISearchViewModel extends ChangeNotifier {
} }
void searchFriendByKey(String searchKey) async { void searchFriendByKey(String searchKey) async {
// API
if (searchKey.trim().isEmpty) {
friendList = [];
notifyListeners();
return;
}
final searchResult = final searchResult =
await _friendshipServices.searchFriends(searchParam: V2TimFriendSearchParam(keywordList: [searchKey])); await _friendshipServices.searchFriends(searchParam: V2TimFriendSearchParam(keywordList: [searchKey]));
friendList = searchResult; friendList = searchResult;
@ -69,6 +75,12 @@ class TUISearchViewModel extends ChangeNotifier {
} }
void searchGroupByKey(String searchKey) async { void searchGroupByKey(String searchKey) async {
// API
if (searchKey.trim().isEmpty) {
groupList = [];
notifyListeners();
return;
}
final searchResult = final searchResult =
await _groupServices.searchGroups(searchParam: V2TimGroupSearchParam(keywordList: [searchKey])); await _groupServices.searchGroups(searchParam: V2TimGroupSearchParam(keywordList: [searchKey]));
groupList = searchResult.data ?? []; groupList = searchResult.data ?? [];
@ -111,6 +123,14 @@ class TUISearchViewModel extends ChangeNotifier {
} }
void searchMsgByKey(String searchKey, bool isFirst) async { void searchMsgByKey(String searchKey, bool isFirst) async {
// API
if (searchKey.trim().isEmpty) {
msgPage = 0;
msgList = [];
totalMsgCount = 0;
notifyListeners();
return;
}
if (isFirst == true) { if (isFirst == true) {
msgPage = 0; msgPage = 0;
msgList = []; msgList = [];

View File

@ -683,7 +683,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
isAddingAtSearchWords = false; isAddingAtSearchWords = false;
} }
} else if (textLength > 0 && text[textLength - 1] == "@" && lastText.length < textLength) { } else if (textLength > 0 && text[textLength - 1] == "@" && lastText.length < textLength) {
List<V2TimGroupMemberFullInfo> selectedAtMemberList = await Navigator.push( List<V2TimGroupMemberFullInfo>? selectedAtMemberList = await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AtText( builder: (context) => AtText(
@ -695,6 +695,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
), ),
); );
if (selectedAtMemberList == null) return;
for (int i = 0; i < selectedAtMemberList.length; ++i) { for (int i = 0; i < selectedAtMemberList.length; ++i) {
V2TimGroupMemberFullInfo memberInfo = selectedAtMemberList[i]; V2TimGroupMemberFullInfo memberInfo = selectedAtMemberList[i];
final showName = _getShowName(memberInfo); final showName = _getShowName(memberInfo);

View File

@ -23,7 +23,6 @@ class TUIKitColumnMenu extends StatefulWidget {
} }
class TUIKitColumnMenuState extends TIMUIKitState<TUIKitColumnMenu> { class TUIKitColumnMenuState extends TIMUIKitState<TUIKitColumnMenu> {
List<Widget> renderMenuItems(TUITheme theme) { List<Widget> renderMenuItems(TUITheme theme) {
return widget.data return widget.data
.map( .map(
@ -39,10 +38,11 @@ class TUIKitColumnMenuState extends TIMUIKitState<TUIKitColumnMenu> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (item.icon != null) item.icon!, if (item.icon != null) item.icon!,
if (item.icon != null) const SizedBox( if (item.icon != null)
height: 4, const SizedBox(
width: 6, height: 4,
), width: 6,
),
Text( Text(
item.label, item.label,
style: TextStyle( style: TextStyle(
@ -73,18 +73,11 @@ class TUIKitColumnMenuState extends TIMUIKitState<TUIKitColumnMenu> {
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: min(MediaQuery.of(context).size.width * 0.7, 350), maxWidth: min(MediaQuery.of(context).size.width * 0.7, 350),
), ),
child: Table( child: Table(columnWidths: const <int, TableColumnWidth>{
columnWidths: const <int, TableColumnWidth>{ 0: IntrinsicColumnWidth(),
0: IntrinsicColumnWidth(), }, children: <TableRow>[
}, ...renderMenuItems(theme).map((e) => TableRow(children: <Widget>[e]))
children: <TableRow>[ ]),
...renderMenuItems(theme).map((e) => TableRow(
children: <Widget>[
e
]
))
]
),
), ),
); );
} }

View File

@ -16,7 +16,8 @@ import 'package:video_player/video_player.dart';
import 'center_play_button.dart'; import 'center_play_button.dart';
class VideoCustomControls extends StatefulWidget { class VideoCustomControls extends StatefulWidget {
const VideoCustomControls({required this.downloadFn, Key? key}) : super(key: key); const VideoCustomControls({required this.downloadFn, Key? key})
: super(key: key);
final Future<void> Function() downloadFn; final Future<void> Function() downloadFn;
@override @override
@ -25,7 +26,8 @@ class VideoCustomControls extends StatefulWidget {
} }
} }
class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with SingleTickerProviderStateMixin { class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls>
with SingleTickerProviderStateMixin {
late VideoPlayerValue _latestValue; late VideoPlayerValue _latestValue;
bool _hideStuff = true; bool _hideStuff = true;
Timer? _hideTimer; Timer? _hideTimer;
@ -74,11 +76,18 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: <Widget>[ children: <Widget>[
if (_latestValue.isBuffering) const Center(child: CircularProgressIndicator(color: Colors.white)) else _buildHitArea(), if (_latestValue.isBuffering)
const Center(
child: CircularProgressIndicator(color: Colors.white))
else
_buildHitArea(),
Positioned( Positioned(
bottom: 0, bottom: 0,
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
child: Column(children: [_buildVideoControlBar(context), _buildBottomBar()]), child: Column(children: [
_buildVideoControlBar(context),
_buildBottomBar()
]),
), ),
if (isLoading) if (isLoading)
Container( Container(
@ -198,8 +207,14 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
_buildPlayPause(controller, iconColor), _buildPlayPause(controller, iconColor),
if (chewieController.isLive) const Expanded(child: Text('LIVE')) else _buildPositionStart(iconColor), if (chewieController.isLive)
if (chewieController.isLive) const SizedBox() else _buildProgressBar(), const Expanded(child: Text('LIVE'))
else
_buildPositionStart(iconColor),
if (chewieController.isLive)
const SizedBox()
else
_buildProgressBar(),
if (!chewieController.isLive) _buildPositionEnd(iconColor), if (!chewieController.isLive) _buildPositionEnd(iconColor),
], ],
), ),
@ -235,7 +250,8 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with
)); ));
} }
GestureDetector _buildPlayPause(VideoPlayerController controller, Color color) { GestureDetector _buildPlayPause(
VideoPlayerController controller, Color color) {
return GestureDetector( return GestureDetector(
onTap: _playPause, onTap: _playPause,
child: Container( child: Container(
@ -311,7 +327,8 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with
_hideStuff = true; _hideStuff = true;
chewieController.toggleFullScreen(); chewieController.toggleFullScreen();
_showAfterExpandCollapseTimer = Timer(const Duration(milliseconds: 300), () { _showAfterExpandCollapseTimer =
Timer(const Duration(milliseconds: 300), () {
setState(() { setState(() {
_cancelAndRestartTimer(); _cancelAndRestartTimer();
}); });
@ -379,7 +396,12 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with
_startHideTimer(); _startHideTimer();
}, },
colors: chewieController.materialProgressColors ?? ChewieProgressColors(playedColor: Colors.white, handleColor: Colors.white, bufferedColor: Colors.white38, backgroundColor: Colors.white24), colors: chewieController.materialProgressColors ??
ChewieProgressColors(
playedColor: Colors.white,
handleColor: Colors.white,
bufferedColor: Colors.white38,
backgroundColor: Colors.white24),
), ),
), ),
); );
@ -402,7 +424,8 @@ class _PlaybackSpeedDialog extends TIMUIKitStatelessWidget {
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme; final TUITheme theme = value.theme;
final Color selectedColor = theme.primaryColor ?? Theme.of(context).primaryColor; final Color selectedColor =
theme.primaryColor ?? Theme.of(context).primaryColor;
return ListView.builder( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,