Introduce FrostyColors ThemeExtension with 7 semantic color fields (highlightedMessage, emoteOnly, followersOnly, slowMode, subOnly, uniqueMode, overlayOnSurface) and brightness-aware light/dark variants. Replace hardcoded colors across 15 files: - Links use colorScheme.primary instead of Colors.blue - Mentions use colorScheme.error instead of Colors.red - First-time chatters share highlightedMessage (Twitch purple) - Chat mode indicators use frostyColors fields - Overlay text uses frostyColors.overlayOnSurface Remove Provider<FrostyThemes> pattern from main.dart, channel.dart, video_overlay.dart, and frosty_photo_view_dialog.dart. Replace manual onSurface.withValues(alpha: 0.6) with M3's built-in onSurfaceVariant for secondary text in section headers, chat modes, alerts, loading indicators, and input hints. Fix double-muted onSurfaceVariant.withValues(alpha: 0.6) in navigation icons and notification dismiss button. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
2.3 KiB
Dart
82 lines
2.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// A simple widget that displays an alert message in the center.
|
|
class AlertMessage extends StatelessWidget {
|
|
final String message;
|
|
final Color? color;
|
|
final bool centered;
|
|
final EdgeInsetsGeometry? padding;
|
|
final bool vertical;
|
|
final IconData? trailingIcon;
|
|
final VoidCallback? onTrailingIconPressed;
|
|
|
|
const AlertMessage({
|
|
super.key,
|
|
required this.message,
|
|
this.centered = true,
|
|
this.color,
|
|
this.padding,
|
|
this.vertical = false,
|
|
this.trailingIcon,
|
|
this.onTrailingIconPressed,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
final defaultColor =
|
|
vertical ? colorScheme.onSurfaceVariant : colorScheme.onSurface;
|
|
|
|
final Widget widget;
|
|
|
|
if (vertical) {
|
|
widget = Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.min,
|
|
spacing: 8,
|
|
children: [
|
|
Icon(Icons.info_outline_rounded, color: color ?? defaultColor),
|
|
Text(
|
|
message,
|
|
textAlign: centered ? TextAlign.center : TextAlign.start,
|
|
style: TextStyle(color: color ?? defaultColor),
|
|
),
|
|
],
|
|
);
|
|
} else {
|
|
widget = Row(
|
|
mainAxisAlignment: centered
|
|
? MainAxisAlignment.center
|
|
: MainAxisAlignment.start,
|
|
spacing: 8,
|
|
children: [
|
|
Icon(Icons.info_outline_rounded, color: color ?? defaultColor),
|
|
Flexible(
|
|
child: Text(
|
|
message,
|
|
style: TextStyle(color: color ?? defaultColor),
|
|
),
|
|
),
|
|
if (trailingIcon != null) ...[
|
|
const SizedBox(width: 8),
|
|
IconButton(
|
|
icon: Icon(trailingIcon, color: color ?? defaultColor),
|
|
onPressed: onTrailingIconPressed,
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
iconSize: 20,
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
final effectivePadding =
|
|
padding ??
|
|
(vertical ? const EdgeInsets.symmetric(horizontal: 24) : null);
|
|
|
|
return effectivePadding != null
|
|
? Padding(padding: effectivePadding, child: widget)
|
|
: widget;
|
|
}
|
|
}
|