import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../i18n/strings.g.dart'; import 'app_drawer.dart'; import 'seed_glyph.dart'; import 'theme.dart'; import 'unread_badge.dart'; /// The main menu (redesign screen 00): a sprout logo in a soft green disc over a /// faint seed-glyph watermark, with "Your inventory" as the primary green call /// to action and "Open market" (Block 2) as a disabled outlined card. The /// hamburger opens [AppDrawer]. class HomeScreen extends StatelessWidget { const HomeScreen({this.marketEnabled = false, super.key}); /// When the Block 2 social layer is wired, the market becomes a live /// destination; otherwise it stays a disabled "coming soon" card. final bool marketEnabled; @override Widget build(BuildContext context) { final t = context.t; return Scaffold( appBar: AppBar( title: Text(t.app.title), // Badge the hamburger so unread messages are visible from home, without // opening the drawer. Total count across conversations. leading: Builder( builder: (context) => UnreadBadge( child: IconButton( icon: const Icon(Icons.menu), tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, onPressed: () => Scaffold.of(context).openDrawer(), ), ), ), ), drawer: AppDrawer(marketEnabled: marketEnabled), body: Stack( children: [ const Positioned.fill(child: _SeedWatermark()), Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 440), child: Padding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 32), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Center( child: Container( width: 128, height: 128, decoration: const BoxDecoration( color: Color(0xFFDDF3CE), shape: BoxShape.circle, ), alignment: Alignment.center, child: Image.asset( 'assets/logo.png', height: 84, fit: BoxFit.contain, ), ), ), const SizedBox(height: 10), Text( t.app.title, textAlign: TextAlign.center, style: const TextStyle( color: Color(0xFF1A2A13), fontSize: 26, fontWeight: FontWeight.w500, letterSpacing: -0.3, ), ), const SizedBox(height: 2), Text( t.home.tagline, textAlign: TextAlign.center, style: const TextStyle( color: Color(0xFF55624A), fontSize: 14, ), ), const SizedBox(height: 34), _PrimaryMenuCard( key: const Key('home.inventory'), icon: Icons.inventory_2_outlined, label: t.home.yourInventory, subtitle: t.home.yourInventorySubtitle, onTap: () => context.push('/inventory'), ), const SizedBox(height: 16), _OutlinedMenuCard( key: const Key('home.market'), icon: Icons.storefront_outlined, label: t.home.openMarket, subtitle: t.home.openMarketSubtitle, tag: marketEnabled ? null : t.common.comingSoon, onTap: marketEnabled ? () => context.push('/market') : null, ), ], ), ), ), ), ], ), ); } } /// The primary green call-to-action: white icon in a translucent disc, a title /// and a subtitle, with a soft green shadow. class _PrimaryMenuCard extends StatelessWidget { const _PrimaryMenuCard({ required this.icon, required this.label, required this.subtitle, required this.onTap, super.key, }); final IconData icon; final String label; final String subtitle; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( color: seedGreen, borderRadius: BorderRadius.circular(18), elevation: 3, shadowColor: seedGreen.withValues(alpha: 0.4), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(18), child: Padding( padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 22), child: Row( children: [ _IconDisc( background: Colors.white.withValues(alpha: 0.18), child: Icon(icon, color: Colors.white, size: 26), ), const SizedBox(width: 16), Expanded( child: _CardText( label: label, subtitle: subtitle, color: Colors.white, subtitleColor: Colors.white.withValues(alpha: 0.85), ), ), ], ), ), ), ); } } /// A disabled Block-2 destination: an outlined white card with a green-disc /// icon and a "soon" tag. class _OutlinedMenuCard extends StatelessWidget { const _OutlinedMenuCard({ required this.icon, required this.label, required this.subtitle, this.tag, this.onTap, super.key, }); final IconData icon; final String label; final String subtitle; /// A small trailing tag (e.g. "coming soon"); omitted for live cards. final String? tag; /// When set, the card is tappable; otherwise it reads as disabled. final VoidCallback? onTap; @override Widget build(BuildContext context) { final card = Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(18), border: Border.all(color: const Color(0xFFB8C4AC)), ), padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 22), child: Row( children: [ _IconDisc( background: const Color(0xFFDDF3CE), child: Icon(icon, color: seedGreen, size: 26), ), const SizedBox(width: 16), Expanded( child: _CardText( label: label, subtitle: subtitle, color: seedOnSurface, subtitleColor: seedMuted, ), ), if (tag != null) Text( tag!.toUpperCase(), style: const TextStyle( color: Color(0xFFB3BDA8), fontSize: 11, fontWeight: FontWeight.w500, letterSpacing: 0.5, ), ) else if (onTap != null) const Icon(Icons.chevron_right, color: seedMuted), ], ), ); if (onTap == null) return card; return Material( color: Colors.transparent, borderRadius: BorderRadius.circular(18), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(18), child: card, ), ); } } class _IconDisc extends StatelessWidget { const _IconDisc({required this.background, required this.child}); final Color background; final Widget child; @override Widget build(BuildContext context) { return Container( width: 44, height: 44, decoration: BoxDecoration(shape: BoxShape.circle, color: background), alignment: Alignment.center, child: child, ); } } class _CardText extends StatelessWidget { const _CardText({ required this.label, required this.subtitle, required this.color, required this.subtitleColor, }); final String label; final String subtitle; final Color color; final Color subtitleColor; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( color: color, fontSize: 19, fontWeight: FontWeight.w500, ), ), Text(subtitle, style: TextStyle(color: subtitleColor, fontSize: 13)), ], ); } } /// A single seed pictogram scattered onto the watermark: [ax]/[ay] are /// fractional positions (0..1) within the layer, [angle] a slight tilt so the /// scatter feels hand-strewn rather than gridded (like screen 00). class _ScatteredGlyph { const _ScatteredGlyph(this.char, this.ax, this.ay, this.size, this.angle); final String char; final double ax; final double ay; final double size; final double angle; } /// A faint, disordered scatter of seed pictograms behind the menu (like screen /// 00): glyphs strewn around the edges at staggered positions and tilts, /// positioned by fraction so it holds up at any screen size. class _SeedWatermark extends StatelessWidget { const _SeedWatermark(); static const _glyphs = [ _ScatteredGlyph(SeedGlyphs.sack, 0.06, 0.14, 62, -0.18), _ScatteredGlyph(SeedGlyphs.envelope, 0.82, 0.09, 48, 0.22), _ScatteredGlyph(SeedGlyphs.scattered, 0.70, 0.30, 44, -0.12), _ScatteredGlyph(SeedGlyphs.jar, 0.16, 0.44, 40, 0.15), _ScatteredGlyph(SeedGlyphs.jars, 0.85, 0.66, 44, 0.18), _ScatteredGlyph(SeedGlyphs.smallSpoon, 0.10, 0.74, 48, -0.22), _ScatteredGlyph(SeedGlyphs.pouring, 0.40, 0.90, 52, 0.10), _ScatteredGlyph(SeedGlyphs.mug, 0.72, 0.88, 60, -0.14), ]; @override Widget build(BuildContext context) { return IgnorePointer( child: LayoutBuilder( builder: (context, constraints) { final w = constraints.maxWidth; final h = constraints.maxHeight; return Stack( children: [ for (final g in _glyphs) Positioned( left: w * g.ax, top: h * g.ay, child: Opacity( opacity: 0.08, child: Transform.rotate( angle: g.angle, child: SeedGlyph( g.char, size: g.size, color: seedGreenDark, ), ), ), ), ], ); }, ), ); } }