merge: 首页图标区分实心选中态
This commit is contained in:
commit
5860302ce7
6 files changed
+44
-12
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
@@ -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。
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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<Image>(
|
||||
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', (
|
||||
|
||||
Reference in new issue
Block a user