Files
jiu/client/lib/widgets/ds/m_search_row.dart
T
wangjia be9afcf68d feat(client): 移动壳基建——底部 5 tab + 我的 hub + 统一底部 sheet + 移动组件四件套
对齐移动原型体系(m-*.html)的 Stage 0 共享基建,桌面形态零改动:

- 导航:router 新增 branch 9(/me 我的 + /me/license 授权屏);窄屏 tab 根
  (库存/入库/出库/财务/我的)出底部 MTabBar,二级屏隐藏 tabbar + 顶栏返回箭头
  (PopScope 拦系统返回键防退 app);窄屏 Drawer/汉堡删除;窄屏顶栏对齐 m-top
  (标题 + 主题/通知铃/头像→我的)
- sheet:showMSheet(grip/标题/86vh/键盘避让)+ showAdaptiveSheet;六个共享
  弹层呈现函数加窄屏 sheet 分支(订单/往来/财务往来/商品编辑抽屉 + 退单/登记收支
  dialog 的 asSheet 形态)
- 徽章:DsBadge/StatusPill/StatusBadge 加图标变体(默认圆点零漂移)+
  status_icon_map 转录原型 BADGE_ICON 28 词
- 组件:MKpiGrid(2×2 可点 KPI)/ MSearchRow(搜索+状态钮+详搜钮)/
  MHubGroup(hub 入口组)/ showThemeSheet(主题外观 sheet)
- 授权:_LicensePanel/_SettingsCard 抽取为公共 LicensePanel/SettingsCard,
  窄屏授权跳转 /me/license
- golden:app_shell_mobile + me 各三主题(390×844@2x);全量 259 测试绿,
  check_ds_code / check-l1-sync 闸过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 11:56:04 +08:00

152 lines
5.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// widgets/ds/m_search_row.dart — 移动搜索行(镜像原型 m-stock-*-list 搜索区终态):
// 搜索框(.m-search 42h, flex) + 纯文字状态钮(选中转 primary)+ 图标详细搜索钮(激活转 primary)。
// 状态/详搜钮为可选;不传对应回调即不渲染(如库存屏只有搜索框)。
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../core/theme/app_dims.g.dart';
import '../../core/theme/context_tokens.dart';
class MSearchRow extends StatelessWidget {
final TextEditingController? controller;
final String hint;
final ValueChanged<String>? onChanged;
final ValueChanged<String>? onSubmitted;
/// 状态筛选钮当前文案(如「全部」「待审核」);null 不渲染该钮。
final String? statusLabel;
/// 状态钮是否处于筛选中(非「全部」)→ primary 高亮。
final bool statusActive;
final VoidCallback? onStatusTap;
/// 详细搜索钮是否有生效条件 → primary 高亮;[onFilterTap] null 不渲染该钮。
final bool filterActive;
final VoidCallback? onFilterTap;
const MSearchRow({
super.key,
this.controller,
this.hint = '',
this.onChanged,
this.onSubmitted,
this.statusLabel,
this.statusActive = false,
this.onStatusTap,
this.filterActive = false,
this.onFilterTap,
});
@override
Widget build(BuildContext context) {
final t = context.tokens;
return Row(children: [
// .m-search42h / surface / border / r-md / 放大镜 17
// Material 自带(TextField 需 Material 祖先 + golden 直挂无 Scaffold 场景)
Expanded(
child: Material(
color: t.surface,
shape: RoundedRectangleBorder(
side: BorderSide(color: t.border),
borderRadius: BorderRadius.circular(AppDims.rMd),
),
child: Container(
height: 42,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(children: [
Icon(LucideIcons.search, size: 17, color: t.muted),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: controller,
onChanged: onChanged,
onSubmitted: onSubmitted,
style: TextStyle(fontSize: AppDims.fsBody, color: t.text),
decoration: InputDecoration(
isCollapsed: true,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
filled: false,
hintText: hint,
hintStyle:
TextStyle(fontSize: AppDims.fsBody, color: t.faint),
),
),
),
if (controller != null)
ValueListenableBuilder<TextEditingValue>(
valueListenable: controller!,
builder: (context, value, _) {
if (value.text.isEmpty) return const SizedBox.shrink();
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
controller!.clear();
onChanged?.call('');
onSubmitted?.call('');
},
child: Padding(
padding: const EdgeInsets.only(left: 6),
child: Icon(LucideIcons.x, size: 15, color: t.muted),
),
);
},
),
]),
),
),
),
// 状态筛选钮(纯文字,无箭头;筛选中转 primary)
if (statusLabel != null) ...[
const SizedBox(width: 8),
_pillBtn(
t,
active: statusActive,
onTap: onStatusTap,
child: Text(statusLabel!,
style: TextStyle(
fontSize: AppDims.fsBody,
fontWeight: statusActive ? FontWeight.w600 : FontWeight.w500,
color: statusActive ? t.primary : t.text)),
),
],
// 详细搜索钮(图标;有生效条件转 primary)
if (onFilterTap != null) ...[
const SizedBox(width: 8),
_pillBtn(
t,
active: filterActive,
onTap: onFilterTap,
child: Icon(LucideIcons.listFilter,
size: 18, color: filterActive ? t.primary : t.text),
),
],
]);
}
Widget _pillBtn(dynamic t,
{required bool active, VoidCallback? onTap, required Widget child}) {
// Material 自带(InkWell 水波 + golden 直挂无 Scaffold 场景)
return Material(
color: active ? t.brand50 : t.surface,
borderRadius: BorderRadius.circular(AppDims.rMd),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(AppDims.rMd),
child: Container(
height: 42,
padding: const EdgeInsets.symmetric(horizontal: 13),
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: active ? t.primary : t.border),
borderRadius: BorderRadius.circular(AppDims.rMd),
),
child: child,
),
),
);
}
}