Skip to content

Commit e2ea380

Browse files
committed
breaking(TSwitch): separate loading from content variant
1 parent ef1b090 commit e2ea380

11 files changed

Lines changed: 175 additions & 25 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# 验收记录
2+
3+
## 验证环境
4+
5+
- 分支:rss1102/style/switch-miniprogram-alignment
6+
- 提交:本 Spec 与 breaking API 实现一并提交
7+
- Flutter/Dart:Flutter 3.32.0 / Dart 3.8.0;latest 待 CNB CI 验证
8+
9+
## 自动化验证
10+
11+
| 命令 | 结果 | 备注 |
12+
| --- | --- | --- |
13+
| `flutter test test/components/switch/t_switch_test.dart --coverage` | 通过 | 15 项;Switch 生产目录 341/358,95.25% |
14+
| `flutter test test/switch_page_test.dart --coverage` | 通过 | 4 项;Demo 49/49,100%;light/dark Golden 非更新模式通过 |
15+
| `flutter analyze`(tdesign-component) | 通过 | 0 issues |
16+
| `flutter analyze`(example) | 通过 | 0 issues |
17+
| `dart run tool/generate_example_code.dart --check --verbose` | 通过 | 所有片段已同步 |
18+
| `node tool/generate_api.mjs` | 通过 | Switch API 文档已同步 |
19+
| `git diff --check` | 通过 | 无空白错误 |
20+
21+
## 人工验收
22+
23+
- [ ] Android 实机确认 Switch 加载状态与分组展示
24+
25+
## 未覆盖项与后续工作
26+
27+
- Flutter latest 依赖 CNB CI 验证;本地未安装第二套 SDK。
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# 实施方案
2+
3+
## 技术方案
4+
5+
在 TSwitch 上新增必选默认值参数 `loading = false`,并从
6+
`TSwitchVariant` 删除 `loading`。构建时先根据 `loading` 决定交互能力和
7+
加载指示器,再由 `variant` 处理非加载内容形态,避免两个加载状态源。
8+
9+
## 影响范围
10+
11+
| 范围 | 文件或模块 | 影响 |
12+
| --- | --- | --- |
13+
| 组件 | switch/t_switch.dart、t_switch_types.dart | breaking API 收敛 |
14+
| 测试 | t_switch_test.dart、switch_page_test.dart | 更新加载态和回归覆盖 |
15+
| 示例 | t_switch_page.dart、Golden、生成片段 | 迁移到 loading 参数 |
16+
| 文档 | Switch API 产物、Spec | 记录新契约和迁移方式 |
17+
18+
## API 变化
19+
20+
- 新增 `TSwitch.loading`,默认 `false`
21+
- 删除 `TSwitchVariant.loading`
22+
- 迁移:`variant: TSwitchVariant.loading` 改为 `loading: true`
23+
24+
## 风险与取舍
25+
26+
- 删除枚举值是 breaking change,不保留兼容别名,以免形成双状态源。
27+
- loading 与 variant 同时传入时,loading 负责当前状态并覆盖滑块内容;
28+
loading 结束后恢复 variant 指定的内容形态。
29+
30+
## 验证策略
31+
32+
- 单元测试:加载渲染、禁用交互、variant 恢复和 API 形态。
33+
- 集成或 Widget 测试:Demo 状态矩阵、交互及 light/dark Golden。
34+
- 静态检查:组件包和 Example 包 flutter analyze。
35+
- 人工验收:Android 实机查看加载、禁用、尺寸分组。
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# TSwitch 加载状态 API 收敛
2+
3+
## 背景
4+
5+
`TSwitchVariant` 同时包含 `filled``text``icon` 三种内容形态和
6+
`loading` 状态,导致同一枚举混合展示形态与交互状态。加载状态需要成为
7+
独立且唯一的公开状态源。
8+
9+
## 目标
10+
11+
-`final bool loading` 作为 TSwitch 加载状态的唯一入口。
12+
- `TSwitchVariant` 只保留 `filled``text``icon` 三种内容形态。
13+
- 加载中显示加载指示器并禁用交互。
14+
- 同步示例、测试、Golden、API 文档和迁移说明。
15+
16+
## 非目标
17+
18+
- 不新增 `disabled`,禁用仍由 `onChanged == null` 表达。
19+
- 不引入非受控值或自定义值类型。
20+
- 不改变尺寸、颜色和主题优先级。
21+
22+
## 范围
23+
24+
### 涉及
25+
26+
- TSwitch 构造参数、加载渲染与交互语义。
27+
- TSwitchVariant 枚举。
28+
- Switch 组件测试、示例测试、示例代码和 Golden。
29+
- 生成的 Switch API 文档与代码片段。
30+
31+
### 不涉及
32+
33+
- TCupertinoSwitch 公开契约。
34+
- ExampleItem、ExamplePage 或公共 Example 抽象。
35+
36+
## 行为契约
37+
38+
- `loading` 默认为 `false`
39+
- `loading == true` 时,滑块内容显示加载指示器,`onChanged` 不会触发,
40+
语义节点为不可交互。
41+
- `loading == false` 时,`variant` 决定 `filled``text``icon` 内容形态。
42+
- `TSwitchVariant.loading` 被删除;迁移方式为
43+
`variant: TSwitchVariant.loading``loading: true`
44+
- `onChanged == null` 仍是普通禁用状态的唯一入口。
45+
46+
## 验收标准
47+
48+
- [ ] TSwitchVariant 仅保留 filled、text、icon。
49+
- [ ] TSwitch 仅通过 loading 表达加载状态,默认值为 false。
50+
- [ ] 加载态渲染、禁用交互和语义测试通过。
51+
- [ ] 示例、代码片段、API 文档与 Golden 同步。
52+
- [ ] Switch 生产目录覆盖率不低于 95%。
53+
- [ ] Flutter 3.32.0 与 latest 的 analyze/test 门禁通过。
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# 实施任务
2+
3+
- [x] DONE 明确需求和验收边界
4+
- [x] DONE 完成实现
5+
- [x] DONE 补充或更新测试
6+
- [x] DONE 更新示例和 API 文档
7+
- [x] DONE 执行验证并记录结果
8+
- [x] DONE 完成 Review

tdesign-component/example/assets/api/switch_api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
| --- | --- | --- | --- |
77
| closeText | String? | - | text 形态的关闭文案。 |
88
| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
9+
| loading | bool | false | 是否处于加载状态;加载时显示指示器并禁用交互。 |
910
| onChanged | ValueChanged<bool>? | - | 开关状态变更回调;为 null 时禁用。 |
1011
| openText | String? | - | text 形态的开启文案。 |
1112
| size | TSwitchSize? | - | 开关尺寸;未传时读取 `TSwitchThemeData.defaultSize`|

tdesign-component/example/assets/code/switch._buildStatus.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ Widget _buildStatus(BuildContext context) => const Column(
77
cells: [
88
TCell(
99
title: Text('加载状态'),
10-
note: TSwitch(value: false, variant: TSwitchVariant.loading),
11-
),
12-
TCell(
13-
title: Text('加载状态'),
14-
note: TSwitch(value: true, variant: TSwitchVariant.loading),
10+
note: TSwitch(value: false, loading: true),
1511
),
12+
TCell(title: Text('加载状态'), note: TSwitch(value: true, loading: true)),
1613
],
1714
),
1815
_SwitchGroupLabel('禁用状态', top: 24),

tdesign-component/example/lib/page/t_switch_page.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,9 @@ class TSwitchPage extends StatelessWidget {
112112
cells: [
113113
TCell(
114114
title: Text('加载状态'),
115-
note: TSwitch(value: false, variant: TSwitchVariant.loading),
116-
),
117-
TCell(
118-
title: Text('加载状态'),
119-
note: TSwitch(value: true, variant: TSwitchVariant.loading),
115+
note: TSwitch(value: false, loading: true),
120116
),
117+
TCell(title: Text('加载状态'), note: TSwitch(value: true, loading: true)),
121118
],
122119
),
123120
_SwitchGroupLabel('禁用状态', top: 24),

tdesign-component/example/test/switch_page_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void main() {
134134
final switches = tester.widgetList<TSwitch>(find.byType(TSwitch)).toList();
135135
final variants = switches.map((widget) => widget.variant).toSet();
136136
final loadingValues = switches
137-
.where((widget) => widget.variant == TSwitchVariant.loading)
137+
.where((widget) => widget.loading)
138138
.map((widget) => widget.value)
139139
.toSet();
140140
final disabledValues = switches

tdesign-component/lib/src/components/switch/t_switch.dart

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ export 't_switch_types.dart';
1111

1212
/// 严格受控的开关组件。
1313
///
14-
/// [value] 由父级持有;[onChanged] 为 null 时禁用。文字、图标和加载形态
15-
/// 无法由 Material Switch 完整表达,因此底层保留 TDesign 自定义开关实现。
14+
/// [value] 由父级持有;[onChanged] 为 null 时禁用;[loading] 为 true 时
15+
/// 显示加载指示器并禁用交互。文字、图标和加载内容无法由 Material Switch
16+
/// 完整表达,因此底层保留 TDesign 自定义开关实现。
1617
class TSwitch extends StatelessWidget {
1718
const TSwitch({
1819
super.key,
@@ -29,6 +30,9 @@ class TSwitch extends StatelessWidget {
2930
/// 开关内容形态;未传时读取 [TSwitchThemeData.defaultVariant]
3031
this.variant,
3132

33+
/// 是否处于加载状态;加载时显示指示器并禁用交互。
34+
this.loading = false,
35+
3236
/// text 形态的开启文案。
3337
this.openText,
3438

@@ -48,6 +52,9 @@ class TSwitch extends StatelessWidget {
4852
/// 开关内容形态。
4953
final TSwitchVariant? variant;
5054

55+
/// 是否处于加载状态;加载时显示指示器并禁用交互。
56+
final bool loading;
57+
5158
/// text 形态的开启文案。
5259
final String? openText;
5360

@@ -60,8 +67,7 @@ class TSwitch extends StatelessWidget {
6067
final resolvedSize = size ?? theme?.defaultSize ?? TSwitchSize.medium;
6168
final resolvedVariant =
6269
variant ?? theme?.defaultVariant ?? TSwitchVariant.filled;
63-
final enabled =
64-
onChanged != null && resolvedVariant != TSwitchVariant.loading;
70+
final enabled = onChanged != null && !loading;
6571
final resolved = TSwitchResolve.resolve(
6672
context: context,
6773
enabled: enabled,
@@ -79,6 +85,7 @@ class TSwitch extends StatelessWidget {
7985
thumbView: _buildThumb(
8086
resolved: resolved,
8187
variant: resolvedVariant,
88+
loading: loading,
8289
openText: openText,
8390
closeText: closeText,
8491
),
@@ -105,9 +112,17 @@ class TSwitch extends StatelessWidget {
105112
Widget? _buildThumb({
106113
required TSwitchResolvedStyle resolved,
107114
required TSwitchVariant variant,
115+
required bool loading,
108116
required String? openText,
109117
required String? closeText,
110118
}) {
119+
if (loading) {
120+
return TCircleIndicator(
121+
color: resolved.thumbContentOnColor,
122+
size: 16,
123+
lineWidth: 3,
124+
);
125+
}
111126
return switch (variant) {
112127
TSwitchVariant.text => SizedBox(
113128
width: 16,
@@ -130,11 +145,6 @@ class TSwitch extends StatelessWidget {
130145
),
131146
),
132147
),
133-
TSwitchVariant.loading => TCircleIndicator(
134-
color: resolved.thumbContentOnColor,
135-
size: 16,
136-
lineWidth: 3,
137-
),
138148
TSwitchVariant.icon => Icon(
139149
value ? TIcons.check : TIcons.close,
140150
size: 16,

tdesign-component/lib/src/components/switch/t_switch_types.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ enum TSwitchVariant {
1818
/// 滑块内显示开关文案。
1919
text,
2020

21-
/// 滑块内显示加载指示器,并禁用交互。
22-
loading,
23-
2421
/// 滑块内显示开关图标。
2522
icon,
2623
}

0 commit comments

Comments
 (0)