feat: 增强 HomeScreen 录音交互与 ChatBloc 状态管理

- 新增录音启动延迟处理,解决权限未就绪时的竞态问题
- 实现历史分页滚动位置保持,提升加载体验
- 添加文本输入框点击键盘显示与焦点管理
- 优化 ChatBloc provider 到 MultiBlocProvider 支持
- 修复 ApiException 429 错误详情解析(支持 JSON 字符串 body)
- 改进 LocalNotificationService 精确闹钟权限请求
- 优化 UiSchemaRenderer GridView children 生成
- 支持导航 action 的 replace 参数
- 移除 Agent router 速率限制逻辑(_allow_run_request, _allow_transcribe_request)
- 补充相关单元测试与集成测试
This commit is contained in:
qzl
2026-03-18 17:03:22 +08:00
parent b34697660d
commit 8539f05a66
13 changed files with 578 additions and 143 deletions
@@ -130,7 +130,9 @@ void main() {
expect(find.textContaining('无效 UI Schema'), findsOneWidget);
});
testWidgets('handles navigation action and jumps by path', (tester) async {
testWidgets('handles navigation action by pushing target page', (
tester,
) async {
final schema = {
'version': '2.0',
'root': {
@@ -174,6 +176,60 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('todo detail 123 from assistant'), findsOneWidget);
expect(router.canPop(), isTrue);
router.pop();
await tester.pumpAndSettle();
expect(find.text('查看待办'), findsOneWidget);
});
testWidgets('uses replace navigation when replace is true', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'plain',
'children': [
{
'type': 'button',
'label': '替换跳转',
'style': 'primary',
'action': {
'type': 'navigation',
'path': '/todo/456',
'replace': true,
},
},
],
},
};
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) =>
Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
GoRoute(
path: '/todo/:id',
builder: (context, state) =>
Text('todo detail ${state.pathParameters['id']}'),
),
],
);
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
await tester.tap(find.text('替换跳转'));
await tester.pumpAndSettle();
expect(find.text('todo detail 456'), findsOneWidget);
expect(router.canPop(), isFalse);
expect(find.text('todo detail 456'), findsOneWidget);
});
testWidgets('does not navigate for placeholder path', (tester) async {