mirror of
https://github.com/zapstore/zapstore.git
synced 2026-09-14 03:05:06 +00:00
Fixes #333 The navigation bar felt laggy on tap, especially on mid-range devices The splash/ripple touch effect on nav items and TextButtons was contributing to the perceived delay. Additionally, the pill height changed when switching tabs, and the profile avatar border made the selected pill grow. Performance: - Remove splash, ripple, and highlight from BottomNavigationBar and NavigationRail - Apply NoSplash to TextButtons globally UI: - Use fixed pill size for all nav items so height stays consistent - Unify icon size (28px) for Search, Updates, and Profile - Add avatar border when selected using strokeAlignInside so it doesn't increase layout size
386 lines
12 KiB
Dart
386 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:skeletonizer/skeletonizer.dart';
|
|
|
|
/// Base font family used for body text and UI elements
|
|
const kFontFamily = 'Inter';
|
|
|
|
/// Display and headline font family
|
|
const kHeadlineFontFamily = 'Inter Display';
|
|
|
|
// Professional color constants (dark theme only)
|
|
class AppColors {
|
|
// Dark theme professional blues
|
|
static const Color darkPrimary = Color(0xFF3A6FCC);
|
|
static const Color darkSecondary = Color(0xFF4A8FCC);
|
|
static const Color darkSurface = Color(
|
|
0xFF0F141B,
|
|
); // slightly darker for cards/pills
|
|
static const Color darkSurfaceVariant = Color(
|
|
0xFF161C27,
|
|
); // darker container variant
|
|
static const Color darkBackground = Color(0xFF0A0F1A); // from palette
|
|
static const Color darkBackgroundSecondary = Color(
|
|
0xFF111723,
|
|
); // from palette
|
|
static const Color darkOnSurface = Color(0xFFE8EAED);
|
|
static const Color darkOnSurfaceVariant = Color(0xFFB8BCC8);
|
|
static const Color darkOnSurfaceSecondary = Color(
|
|
0xFFC1C7CD,
|
|
); // Lighter text for descriptions and "Published by"
|
|
static const Color darkOutline = Color(0xFF2D3748);
|
|
|
|
// Dark skeleton loader colors (dark blue theme)
|
|
static const Color darkSkeletonBase = Color(0xFF1E3A5F);
|
|
static const Color darkSkeletonHighlight = Color(0xFF2B4A73);
|
|
|
|
// App-specific pill backgrounds (slightly bluish)
|
|
static const Color darkPillBackground = Color(0xFF213A60); // darker bluish
|
|
|
|
// Primary action colors (used for buttons, badges, info notifications)
|
|
static const Color darkActionPrimary = Color(
|
|
0xFF2D5FAA,
|
|
); // darker professional blue for dark mode
|
|
|
|
// Helper method to get skeleton colors
|
|
static SkeletonizerConfigData getSkeletonizerConfig(Brightness brightness) {
|
|
return SkeletonizerConfigData(
|
|
effect: ShimmerEffect(
|
|
baseColor: darkSkeletonBase,
|
|
highlightColor: darkSkeletonHighlight,
|
|
duration: const Duration(milliseconds: 1000),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Professional gradient decorations
|
|
class AppGradients {
|
|
static const LinearGradient darkBackgroundGradient = LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [Color(0xFF0A0F1A), Color(0xFF141A22), Color(0xFF111723)],
|
|
stops: [0.0, 0.6, 1.0],
|
|
);
|
|
|
|
static const LinearGradient darkSurfaceGradient = LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [AppColors.darkSurface, AppColors.darkSurfaceVariant],
|
|
stops: [0.0, 1.0],
|
|
);
|
|
}
|
|
|
|
// Professional typography system using Inter
|
|
TextTheme _createProfessionalTextTheme() {
|
|
const baseColor = AppColors.darkOnSurface;
|
|
const secondaryColor = AppColors.darkOnSurfaceVariant;
|
|
|
|
return const TextTheme().copyWith(
|
|
// Display styles - for main headings
|
|
displayLarge: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 62,
|
|
fontWeight: FontWeight.w800,
|
|
letterSpacing: -0.5,
|
|
color: baseColor,
|
|
height: 1.1,
|
|
),
|
|
displayMedium: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 48,
|
|
fontWeight: FontWeight.w800,
|
|
letterSpacing: -0.25,
|
|
color: baseColor,
|
|
height: 1.15,
|
|
),
|
|
displaySmall: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 40,
|
|
fontWeight: FontWeight.w800,
|
|
letterSpacing: 0,
|
|
color: baseColor,
|
|
height: 1.2,
|
|
),
|
|
|
|
// Headline styles - for section titles
|
|
headlineLarge: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 35,
|
|
fontWeight: FontWeight.w800,
|
|
letterSpacing: 0,
|
|
color: baseColor,
|
|
height: 1.25,
|
|
),
|
|
headlineMedium: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 31,
|
|
fontWeight: FontWeight.w800,
|
|
letterSpacing: 0.15,
|
|
color: baseColor,
|
|
height: 1.3,
|
|
),
|
|
headlineSmall: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 26,
|
|
fontWeight: FontWeight.w800,
|
|
letterSpacing: 0.15,
|
|
color: baseColor,
|
|
height: 1.35,
|
|
),
|
|
|
|
// Title styles - for card titles and prominent text
|
|
titleLarge: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.1,
|
|
color: baseColor,
|
|
height: 1.4,
|
|
),
|
|
titleMedium: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.15,
|
|
color: baseColor,
|
|
height: 1.45,
|
|
),
|
|
titleSmall: const TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.15,
|
|
color: baseColor,
|
|
height: 1.45,
|
|
),
|
|
|
|
// Body styles - for main content
|
|
bodyLarge: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w400,
|
|
letterSpacing: 0.1,
|
|
color: baseColor,
|
|
height: 1.5,
|
|
),
|
|
bodyMedium: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w400,
|
|
letterSpacing: 0.15,
|
|
color: secondaryColor,
|
|
height: 1.55,
|
|
),
|
|
bodySmall: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
letterSpacing: 0.25,
|
|
color: secondaryColor,
|
|
height: 1.6,
|
|
),
|
|
|
|
// Label styles - for UI elements
|
|
labelLarge: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: 0.3,
|
|
color: baseColor,
|
|
height: 1.4,
|
|
),
|
|
labelMedium: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: 0.3,
|
|
color: baseColor,
|
|
height: 1.35,
|
|
),
|
|
labelSmall: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: 0.3,
|
|
color: secondaryColor,
|
|
height: 1.3,
|
|
),
|
|
);
|
|
}
|
|
|
|
// Professional Dark Theme with Gradient Backgrounds
|
|
final darkTheme = ThemeData(
|
|
useMaterial3: true,
|
|
brightness: Brightness.dark,
|
|
scaffoldBackgroundColor: Colors.transparent,
|
|
colorScheme: const ColorScheme.dark(
|
|
primary: AppColors.darkPrimary,
|
|
secondary: AppColors.darkSecondary,
|
|
surface: AppColors.darkSurface,
|
|
surfaceContainerHighest: AppColors.darkSurfaceVariant,
|
|
onSurface: AppColors.darkOnSurface,
|
|
onSurfaceVariant: AppColors.darkOnSurfaceVariant,
|
|
outline: AppColors.darkOutline,
|
|
error: Color(0xFFEF4444),
|
|
onError: Colors.white,
|
|
),
|
|
textTheme: _createProfessionalTextTheme(),
|
|
|
|
// Professional dark card design with subtle elevation
|
|
cardTheme: CardThemeData(
|
|
elevation: 0,
|
|
color: AppColors.darkSurface,
|
|
surfaceTintColor: Colors.transparent,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
side: BorderSide(
|
|
color: AppColors.darkOutline.withValues(alpha: 0.3),
|
|
width: 0.5,
|
|
),
|
|
),
|
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
|
),
|
|
|
|
// Professional dark chip design
|
|
chipTheme: ChipThemeData(
|
|
backgroundColor: AppColors.darkSurfaceVariant,
|
|
selectedColor: AppColors.darkPrimary.withValues(alpha: 0.25),
|
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
labelPadding: const EdgeInsets.symmetric(horizontal: 8),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
|
labelStyle: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 14,
|
|
letterSpacing: 0.15,
|
|
color: AppColors.darkOnSurface,
|
|
),
|
|
elevation: 0,
|
|
pressElevation: 0,
|
|
),
|
|
|
|
// Professional dark button designs
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.darkActionPrimary,
|
|
foregroundColor: Colors.white,
|
|
elevation: 0,
|
|
shadowColor: Colors.transparent,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
|
textStyle: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 16,
|
|
letterSpacing: 0.15,
|
|
),
|
|
),
|
|
),
|
|
|
|
filledButtonTheme: FilledButtonThemeData(
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: AppColors.darkActionPrimary,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
|
textStyle: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 16,
|
|
letterSpacing: 0.15,
|
|
),
|
|
),
|
|
),
|
|
|
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.darkPrimary,
|
|
side: const BorderSide(color: AppColors.darkPrimary, width: 1.5),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
|
textStyle: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 16,
|
|
letterSpacing: 0.15,
|
|
),
|
|
),
|
|
),
|
|
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
splashFactory: NoSplash.splashFactory,
|
|
),
|
|
),
|
|
|
|
// Professional dark input design
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: AppColors.darkOutline, width: 1.5),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: AppColors.darkOutline, width: 1.5),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: AppColors.darkPrimary, width: 2),
|
|
),
|
|
filled: true,
|
|
fillColor: AppColors.darkSurfaceVariant,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
|
hintStyle: const TextStyle(
|
|
fontFamily: kFontFamily,
|
|
color: AppColors.darkOnSurfaceVariant,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
|
|
// Professional dark app bar
|
|
appBarTheme: const AppBarTheme(
|
|
elevation: 0,
|
|
scrolledUnderElevation: 0,
|
|
backgroundColor: AppColors.darkBackground,
|
|
surfaceTintColor: Colors.transparent,
|
|
foregroundColor: AppColors.darkOnSurface,
|
|
titleTextStyle: TextStyle(
|
|
fontFamily: kHeadlineFontFamily,
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.w800,
|
|
color: AppColors.darkOnSurface,
|
|
letterSpacing: 0.1,
|
|
),
|
|
systemOverlayStyle: SystemUiOverlayStyle(
|
|
statusBarColor: Colors.transparent,
|
|
statusBarIconBrightness: Brightness.light,
|
|
),
|
|
),
|
|
|
|
// Professional navigation theming
|
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
|
backgroundColor: AppColors.darkSurface,
|
|
selectedItemColor: AppColors.darkPrimary,
|
|
unselectedItemColor: AppColors.darkOnSurfaceVariant,
|
|
elevation: 0,
|
|
type: BottomNavigationBarType.fixed,
|
|
),
|
|
|
|
navigationRailTheme: const NavigationRailThemeData(
|
|
backgroundColor: AppColors.darkSurface,
|
|
selectedIconTheme: IconThemeData(color: AppColors.darkPrimary),
|
|
unselectedIconTheme: IconThemeData(color: AppColors.darkOnSurfaceVariant),
|
|
selectedLabelTextStyle: TextStyle(color: AppColors.darkPrimary),
|
|
unselectedLabelTextStyle: TextStyle(color: AppColors.darkOnSurfaceVariant),
|
|
),
|
|
|
|
// Dark blue progress indicators
|
|
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
|
color: AppColors.darkPrimary,
|
|
linearTrackColor: AppColors.darkSurfaceVariant,
|
|
circularTrackColor: AppColors.darkSurfaceVariant,
|
|
),
|
|
);
|