import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'app_config.dart'; abstract class FluffyThemes { static const double columnWidth = 380.0; static const double maxTimelineWidth = columnWidth * 2; static const double navRailWidth = 80.0; static bool isColumnModeByWidth(double width) => width > columnWidth * 2 + navRailWidth; static bool isColumnMode(BuildContext context) => isColumnModeByWidth(MediaQuery.of(context).size.width); static bool isThreeColumnMode(BuildContext context) => MediaQuery.of(context).size.width > FluffyThemes.columnWidth * 3.5; static LinearGradient backgroundGradient( BuildContext context, int alpha, ) { final colorScheme = Theme.of(context).colorScheme; return LinearGradient( begin: Alignment.topCenter, colors: [ colorScheme.primaryContainer.withAlpha(alpha), colorScheme.secondaryContainer.withAlpha(alpha), colorScheme.tertiaryContainer.withAlpha(alpha), colorScheme.primaryContainer.withAlpha(alpha), ], ); } static const Duration animationDuration = Duration(milliseconds: 250); static const Curve animationCurve = Curves.easeInOut; static ThemeData buildTheme( BuildContext context, Brightness brightness, [ Color? seed, ]) { final colorScheme = ColorScheme.fromSeed( brightness: brightness, seedColor: seed ?? AppConfig.colorSchemeSeed ?? AppConfig.primaryColor, ); final isColumnMode = FluffyThemes.isColumnMode(context); return ThemeData( visualDensity: VisualDensity.standard, useMaterial3: true, brightness: brightness, colorScheme: colorScheme, dividerColor: brightness == Brightness.dark ? colorScheme.surfaceContainerHighest : colorScheme.surfaceContainer, popupMenuTheme: PopupMenuThemeData( color: colorScheme.surfaceContainerLow, iconColor: colorScheme.onSurface, textStyle: TextStyle(color: colorScheme.onSurface), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), ), ), segmentedButtonTheme: SegmentedButtonThemeData( style: SegmentedButton.styleFrom( iconColor: colorScheme.onSurface, disabledIconColor: colorScheme.onSurface, ), ), textSelectionTheme: TextSelectionThemeData( selectionColor: colorScheme.onSurface.withAlpha(128), selectionHandleColor: colorScheme.secondary, ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), contentPadding: const EdgeInsets.all(12), ), chipTheme: ChipThemeData( showCheckmark: false, backgroundColor: colorScheme.surfaceContainer, side: BorderSide.none, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), ), appBarTheme: AppBarTheme( toolbarHeight: isColumnMode ? 72 : 56, shadowColor: isColumnMode ? colorScheme.surfaceContainer.withAlpha(128) : null, surfaceTintColor: isColumnMode ? colorScheme.surface : null, backgroundColor: isColumnMode ? colorScheme.surface : null, systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: brightness.reversed, statusBarBrightness: brightness, systemNavigationBarIconBrightness: brightness.reversed, systemNavigationBarColor: colorScheme.surface, ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( side: BorderSide( width: 1, color: colorScheme.primary, ), shape: RoundedRectangleBorder( side: BorderSide(color: colorScheme.primary), borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), ), ), ), snackBarTheme: isColumnMode ? const SnackBarThemeData( behavior: SnackBarBehavior.floating, width: FluffyThemes.columnWidth * 1.5, ) : const SnackBarThemeData(behavior: SnackBarBehavior.floating), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: colorScheme.secondaryContainer, foregroundColor: colorScheme.onSecondaryContainer, elevation: 0, padding: const EdgeInsets.all(16), textStyle: const TextStyle(fontSize: 16), ), ), ); } } extension on Brightness { Brightness get reversed => this == Brightness.dark ? Brightness.light : Brightness.dark; } extension BubbleColorTheme on ThemeData { Color get bubbleColor => brightness == Brightness.light ? colorScheme.primary : colorScheme.primaryContainer; Color get onBubbleColor => brightness == Brightness.light ? colorScheme.onPrimary : colorScheme.onPrimaryContainer; Color get secondaryBubbleColor => HSLColor.fromColor( brightness == Brightness.light ? colorScheme.tertiary : colorScheme.tertiaryContainer, ).withSaturation(0.5).toColor(); }