feat(app): 完善播放协议搜索与空状态

This commit is contained in:
Zhou Weidong committed 2026-10-23 12:38:47 +08:00
1 parent 671e0ce143
commit ef29fc5f15
85 files changed
+2976 -768

No files matched your search

+61 -26
View File
@@ -20,6 +20,11 @@ import 'discover_binding.dart';
import 'discover_logic.dart';
import 'widgets/search_result_card.dart';
const double _clearHit = 20;
const double _clearIconSize = 16;
const double _clearEndGap = 10;
const double _loadingInset = 2;
class SearchPage extends StatefulWidget {
const SearchPage({super.key});
@@ -119,11 +124,10 @@ class _SearchPageState extends State<SearchPage> {
}
Widget _field(AppLocalizations l10n) {
final hasText = _logic.search.text.isNotEmpty;
return Container(
key: const Key('search-field'),
height: AppDimens.searchHeight,
padding: const EdgeInsets.symmetric(horizontal: AppDimens.spaceMd),
padding: const EdgeInsets.only(left: AppDimens.spaceMd),
decoration: BoxDecoration(
color: AppColors.contentSurface,
borderRadius: BorderRadius.circular(AppDimens.searchRadius),
@@ -143,7 +147,9 @@ class _SearchPageState extends State<SearchPage> {
focusNode: _logic.searchFocus,
autofocus: true,
onChanged: (_) => _logic.onSearchChanged(),
onTapOutside: (_) => _logic.searchFocus.unfocus(),
textInputAction: TextInputAction.search,
textAlignVertical: TextAlignVertical.center,
style: const TextStyle(
color: AppColors.textPrimary,
fontSize: 14,
@@ -163,35 +169,64 @@ class _SearchPageState extends State<SearchPage> {
scrollPadding: EdgeInsets.zero,
),
),
if (_logic.searchLoading)
const SizedBox.square(
dimension: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: AppColors.textPrimary,
),
)
else if (hasText)
IconButton(
key: const Key('search-clear'),
padding: EdgeInsets.zero,
constraints: const BoxConstraints.tightFor(width: 24, height: 24),
onPressed: () {
_logic.search.clear();
_logic.onSearchChanged();
_logic.searchFocus.requestFocus();
},
icon: const Icon(
Icons.close_rounded,
color: AppColors.textSecondary,
size: 16,
),
),
_fieldTrailing(),
],
),
);
}
/// 有内容时把清除按钮放在输入框内部右侧,并留出一点边距。
/// 刷新时不拿转圈替换叉号,避免宽度变化把按钮抖开。
Widget _fieldTrailing() {
final hasText = _logic.search.text.isNotEmpty;
if (!hasText && !_logic.searchLoading) {
return const SizedBox(width: AppDimens.spaceMd);
}
return SizedBox(
width: _clearHit + _clearEndGap,
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: _clearEndGap),
child: hasText ? _clearButton() : _loadingMark(),
),
),
);
}
Widget _clearButton() {
return GestureDetector(
key: const Key('search-clear'),
behavior: HitTestBehavior.opaque,
onTap: () {
_logic.search.clear();
_logic.onSearchChanged();
_logic.searchFocus.requestFocus();
},
child: const SizedBox.square(
dimension: _clearHit,
child: Icon(
Icons.close_rounded,
color: AppColors.textSecondary,
size: _clearIconSize,
),
),
);
}
Widget _loadingMark() {
return const SizedBox.square(
dimension: _clearHit,
child: Padding(
padding: EdgeInsets.all(_loadingInset),
child: CircularProgressIndicator(
strokeWidth: 2,
color: AppColors.textPrimary,
),
),
);
}
Widget _body(AppLocalizations l10n) {
if (!_logic.hasSearchQuery) {
return _stateScroll(