diff --git a/assets/icon/film_reel_filled.png b/assets/icon/film_reel_filled.png new file mode 100644 index 0000000..7191671 Binary files /dev/null and b/assets/icon/film_reel_filled.png differ diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index cc142c9..14b684c 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -1156,3 +1156,9 @@ $ flutter test - `AppRootTab` 新增可选 `assetPath`,首页使用 `assets/icon/film_reel.png`。 - 共享导航通过 `ColorFiltered + Image.asset` 按选中态着色,尺寸保持 26,缺图回退胶片 Material 图标。 - 推荐、追剧、我的图标不变;素材与测试断言同步,按当前流程未执行 analyze/test。 + +## 2026-10-22 首页选中态实心摄影机 + +- `AppRootTab` 新增 `selectedAssetPath`,首页选中使用 `film_reel_filled.png`,未选中使用描边版。 +- 共享导航保持 `ColorFiltered(srcIn)` 与 26×26,缺图按选中态回退实心/描边胶片图标。 +- 推荐、追剧、我的不变;测试断言同步,按当前流程未执行 analyze/test。 diff --git a/lib/core/widgets/app_bottom_nav.dart b/lib/core/widgets/app_bottom_nav.dart index f9a4ec0..fed7576 100644 --- a/lib/core/widgets/app_bottom_nav.dart +++ b/lib/core/widgets/app_bottom_nav.dart @@ -15,12 +15,14 @@ class AppBottomNavItem { required this.onTap, this.selected = false, this.assetPath, + this.selectedAssetPath, }); final Key tabKey; final IconData icon; final IconData selectedIcon; final String? assetPath; + final String? selectedAssetPath; final String label; final VoidCallback onTap; final bool selected; @@ -66,6 +68,9 @@ class _NavDestination extends StatelessWidget { Widget build(BuildContext context) { final color = item.selected ? AppColors.navSelected : unselectedColor; final fallbackIcon = item.selected ? item.selectedIcon : item.icon; + final assetPath = item.selected + ? item.selectedAssetPath ?? item.assetPath + : item.assetPath; return Expanded( child: Semantics( button: true, @@ -83,7 +88,7 @@ class _NavDestination extends StatelessWidget { duration: const Duration(milliseconds: 160), curve: Curves.easeOut, scale: item.selected ? 1 : 0.96, - child: item.assetPath == null + child: assetPath == null ? Icon( fallbackIcon, size: AppDimens.navIcon, @@ -92,12 +97,12 @@ class _NavDestination extends StatelessWidget { : ColorFiltered( colorFilter: ColorFilter.mode(color, BlendMode.srcIn), child: Image.asset( - item.assetPath!, + assetPath, width: AppDimens.navIcon, height: AppDimens.navIcon, fit: BoxFit.contain, errorBuilder: (_, _, _) => Icon( - item.icon, + fallbackIcon, size: AppDimens.navIcon, color: color, ), diff --git a/lib/core/widgets/app_root_tab.dart b/lib/core/widgets/app_root_tab.dart index c0e9d2d..c9f0c96 100644 --- a/lib/core/widgets/app_root_tab.dart +++ b/lib/core/widgets/app_root_tab.dart @@ -7,14 +7,18 @@ import 'app_bottom_nav.dart'; /// 客户端根导航的固定顺序。 enum AppRootTab { - home(assetPath: 'assets/icon/film_reel.png'), + home( + assetPath: 'assets/icon/film_reel.png', + selectedAssetPath: 'assets/icon/film_reel_filled.png', + ), recommend(), following(), mine(); - const AppRootTab({this.assetPath}); + const AppRootTab({this.assetPath, this.selectedAssetPath}); final String? assetPath; + final String? selectedAssetPath; } extension AppRootTabUi on AppRootTab { @@ -72,6 +76,7 @@ class AppRootBottomNav extends StatelessWidget { icon: tab.icon, selectedIcon: tab.selectedIcon, assetPath: tab.assetPath, + selectedAssetPath: tab.selectedAssetPath, label: tab.label(l10n), selected: tab == selected, onTap: () => onSelected(tab), diff --git a/test/home/app_bottom_nav_test.dart b/test/home/app_bottom_nav_test.dart index 37a44be..289d568 100644 --- a/test/home/app_bottom_nav_test.dart +++ b/test/home/app_bottom_nav_test.dart @@ -181,8 +181,8 @@ void main() { }); testWidgets('should_fallback_when_asset_icon_is_missing', (tester) async { - await tester.pumpWidget( - MaterialApp( + Widget navigation({required bool selected}) { + return MaterialApp( home: Scaffold( bottomNavigationBar: AppBottomNav( items: [ @@ -191,17 +191,23 @@ void main() { icon: Icons.theaters_outlined, selectedIcon: Icons.theaters, assetPath: 'assets/icon/missing_film_reel.png', + selectedAssetPath: 'assets/icon/missing_film_reel_filled.png', label: 'Home', - selected: true, + selected: selected, onTap: () {}, ), ], ), ), - ), - ); - await tester.pumpAndSettle(); + ); + } + await tester.pumpWidget(navigation(selected: true)); + await tester.pumpAndSettle(); + expect(find.byIcon(Icons.theaters), findsOneWidget); + + await tester.pumpWidget(navigation(selected: false)); + await tester.pumpAndSettle(); expect(find.byIcon(Icons.theaters_outlined), findsOneWidget); }); } diff --git a/test/home/home_nav_ui_test.dart b/test/home/home_nav_ui_test.dart index 1214bb8..354fc46 100644 --- a/test/home/home_nav_ui_test.dart +++ b/test/home/home_nav_ui_test.dart @@ -235,7 +235,7 @@ void main() { ); expect( (selectedHomeImage.image as AssetImage).assetName, - 'assets/icon/film_reel.png', + 'assets/icon/film_reel_filled.png', ); expect( Size(selectedHomeImage.width!, selectedHomeImage.height!), @@ -300,6 +300,16 @@ void main() { .colorFilter, const ColorFilter.mode(AppColors.navUnselected, BlendMode.srcIn), ); + final unselectedHomeImage = tester.widget( + find.descendant( + of: find.byKey(const Key('tab-home')), + matching: find.byType(Image), + ), + ); + expect( + (unselectedHomeImage.image as AssetImage).assetName, + 'assets/icon/film_reel.png', + ); }); testWidgets('should_map_four_tabs_to_home_recommend_following_and_me', (