import 'dart:io' show Platform; import 'dart:math' as math; import 'dart:ui' show ImageFilter; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../providers/appearance_provider.dart'; import '../../../shared/theme/app_theme.dart'; import '../../../shared/widgets/top_drag_area.dart'; import '../../../shared/widgets/window_chrome.dart'; import '../../../shared/widgets/window_controls.dart'; const double _kDesktopTitleBarHeight = 38.0; class DetailNavBar extends ConsumerWidget { final ValueListenable scrollProgress; final String title; final VoidCallback onBack; final Color chromeColor; const DetailNavBar({ super.key, required this.scrollProgress, required this.title, required this.onBack, this.chromeColor = const Color(0xFF08090c), }); @override Widget build(BuildContext context, WidgetRef ref) { final isDesktop = WindowChrome.isDesktop; final topPadding = isDesktop ? _kDesktopTitleBarHeight : math.max(MediaQuery.paddingOf(context).top, _kDesktopTitleBarHeight); final tokens = context.appTokens; final headerMode = ref.watch(appearanceProvider).value?.headerMode ?? HeaderMode.frosted; final isFrosted = headerMode == HeaderMode.frosted; final navHeight = topPadding + 30; return Stack( children: [ SizedBox( height: navHeight, width: double.infinity, child: ValueListenableBuilder( valueListenable: scrollProgress, builder: (_, raw, _) { final p = raw.clamp(0.0, 1.0); if (isFrosted) { if (Platform.isAndroid && p < 0.05) { return const SizedBox.expand(); } final maxSigma = Platform.isAndroid ? 10.0 : 18.0; return ClipRect( child: BackdropFilter( filter: ImageFilter.blur( sigmaX: p * maxSigma, sigmaY: p * maxSigma, ), child: DecoratedBox( decoration: BoxDecoration( color: chromeColor.withValues(alpha: p * 0.7), border: Border( bottom: BorderSide( color: const Color( 0xFFFFFFFF, ).withValues(alpha: p * 0.08), width: 1, ), ), ), ), ), ); } return ColoredBox(color: chromeColor.withValues(alpha: p)); }, ), ), const Positioned.fill(child: TopDragArea(child: SizedBox.expand())), Positioned( top: isDesktop ? tokens.chromeInset : MediaQuery.paddingOf(context).top, left: 0, right: 0, child: Row( children: [ SizedBox(width: tokens.chromeInset), IconButton( icon: const Icon( Icons.arrow_back, color: Colors.white, size: 18, ), onPressed: onBack, tooltip: '返回', constraints: const BoxConstraints(minWidth: 30, minHeight: 30), padding: const EdgeInsets.all(6), ), const SizedBox(width: 4), Expanded( child: IgnorePointer( child: ValueListenableBuilder( valueListenable: scrollProgress, builder: (_, p, child) { return Opacity(opacity: p.clamp(0.0, 1.0), child: child); }, child: Text( title, style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600, ), overflow: TextOverflow.ellipsis, ), ), ), ), const SizedBox(width: 4), if (isDesktop) const WindowControls.forDarkSurface(), SizedBox(width: tokens.chromeInset), ], ), ), ], ); } }