Skip to content

onfield manager #3557

@engmntazr-cmd

Description

@engmntazr-cmd

Relevant console output or errors

How did this error happen?

thank you

Relevant Dart code

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';

// ---------------------------------------------------------
// 🚀 ONFIELD CHAMELEON - DUAL THEME & DYNAMIC VIEW
// ---------------------------------------------------------

void main() {
  runApp(const OnFieldApp());
}

// Global Theme State Notifier
ValueNotifier<bool> isPurpleTheme = ValueNotifier<bool>(false);

class OnFieldApp extends StatelessWidget {
  const OnFieldApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<bool>(
      valueListenable: isPurpleTheme,
      builder: (context, isPurple, child) {
        // Define Colors based on mode
        Color bgColor = isPurple ? const Color(0xFF0F0518) : const Color(0xFF0F172A); // Deep Purple vs Deep Navy
        Color cardColor = isPurple ? const Color(0xFF1A0B2E) : const Color(0xFF1E293B);
        Color accentColor = isPurple ? const Color(0xFF00E5FF) : const Color(0xFF39FF14); // Cyan vs Neon Green

        return MaterialApp(
          debugShowCheckedModeBanner: false,
          theme: ThemeData.dark().copyWith(
            scaffoldBackgroundColor: bgColor,
            primaryColor: accentColor,
            cardColor: cardColor,
            dialogBackgroundColor: cardColor,
            colorScheme: ColorScheme.dark(primary: accentColor, secondary: isPurple ? Colors.purpleAccent : Colors.greenAccent),
            textTheme: const TextTheme(bodyMedium: TextStyle(fontFamily: 'Verdana')),
          ),
          home: const MainMatchScreen(),
        );
      },
    );
  }
}

class MainMatchScreen extends StatelessWidget {
  const MainMatchScreen({super.key});

  @override
  Widget build(BuildContext context) {
    var theme = Theme.of(context);
    var accent = theme.primaryColor;

    return DefaultTabController(
      length: 3, 
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: theme.scaffoldBackgroundColor,
          elevation: 0,
          title: Text(">> ONFIELD MANAGER <<", 
            style: TextStyle(color: accent, fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: 1.5)),
          centerTitle: true,
          actions: [
            // THEME TOGGLE BUTTON
            IconButton(
              icon: const Icon(Icons.color_lens),
              color: accent,
              tooltip: "Change Theme",
              onPressed: () {
                isPurpleTheme.value = !isPurpleTheme.value;
              },
            ),
            const SizedBox(width: 10),
          ],
          bottom: TabBar(
            indicatorColor: accent,
            labelColor: Colors.white,
            unselectedLabelColor: Colors.grey,
            tabs: const [
              Tab(text: "MATCH 1"),
              Tab(text: "MATCH 2"),
              Tab(text: "MATCH 3"),
            ],
          ),
        ),
        body: const TabBarView(
          physics: NeverScrollableScrollPhysics(), 
          children: [
            SingleMatchInstance(matchId: "1"),
            SingleMatchInstance(matchId: "2"),
            SingleMatchInstance(matchId: "3"),
          ],
        ),
      ),
    );
  }
}

class SingleMatchInstance extends StatefulWidget {
  final String matchId;
  const SingleMatchInstance({super.key, required this.matchId});

  @override
  State<SingleMatchInstance> createState() => _SingleMatchInstanceState();
}

class _SingleMatchInstanceState extends State<SingleMatchInstance> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  // ⏱️ State
  Timer? _timer;
  int _seconds = 0;
  int _matchStage = 0; // 0 = Pre-Match (Lists Visible), 1+ = Live (Dashboard)
  String _btnText = "KICK OFF"; // Initial Text

  // 📊 Stats
  Map<String, int> statsA = {'GOALS': 0, 'SHOTS': 0, 'CORNERS': 0, 'FOULS': 0, 'OFFSIDES': 0, 'SAVES': 0};
  Map<String, int> statsB = {'GOALS': 0, 'SHOTS': 0, 'CORNERS': 0, 'FOULS': 0, 'OFFSIDES': 0, 'SAVES': 0};

  // 📝 Logs
  List<String> colPaid = [];
  List<String> colGoals = [];
  List<String> colAssists = [];
  List<String> colCards = [];

  // 👥 Players (15 Players per team)
  List<String> playersA = List.generate(15, (i) => "Player A${i + 1}");
  List<String> playersB = List.generate(15, (i) => "Player B${i + 1}");
  List<bool> paidA = List.filled(15, false);
  List<bool> paidB = List.filled(15, false);

  // --- Logic ---

  void _handleMatchCycle() {
    setState(() {
      if (_matchStage == 0) {
        // PRE-MATCH -> START MATCH
        _matchStage = 1; 
        _btnText = "PAUSE (1st)"; 
        _startTimer();
      } else if (_matchStage == 1) {
        // PAUSE / HALF TIME
        _matchStage = 2; _btnText = "START 2nd HALF"; _stopTimer();
      } else if (_matchStage == 2) {
        // START 2nd HALF
        _matchStage = 3; _btnText = "END MATCH"; _startTimer();
      } else if (_matchStage == 3) {
        // END MATCH
        _matchStage = 4; _btnText = "RESET MATCH"; _stopTimer();
      } else {
        // RESET
        _resetAll();
      }
    });
  }

  void _startTimer() {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() => _seconds++));
  }
  void _stopTimer() => _timer?.cancel();

  String get _timeStr {
    int m = _seconds ~/ 60;
    int s = _seconds % 60;
    return "${m.toString().padLeft(2,'0')}:${s.toString().padLeft(2,'0')}";
  }

  void _resetAll() {
    _matchStage = 0; _btnText = "KICK OFF"; _seconds = 0;
    statsA.updateAll((k,v)=>0); statsB.updateAll((k,v)=>0);
    colPaid.clear(); colGoals.clear(); colAssists.clear(); colCards.clear();
    // Keep names, reset paid? Optional. Let's keep names reset paid status visually
    paidA = List.filled(15, false); paidB = List.filled(15, false);
  }

  // --- Actions ---
  void _handleGoal() {
    if (_matchStage == 0 || _matchStage == 4) return;
    _showTeamSelectDialog("Who Scored?", (isTeamA) {
      _showPlayerSelectDialog("Scorer ⚽", isTeamA, (scorer) {
        _showPlayerSelectDialog("Assist 🎯", isTeamA, (assist) {
          setState(() {
            if (isTeamA) statsA['GOALS'] = statsA['GOALS']! + 1; else statsB['GOALS'] = statsB['GOALS']! + 1;
            colGoals.insert(0, "$scorer ($_timeStr)");
            colAssists.insert(0, "$assist");
          });
        }, includeNone: true);
      });
    });
  }

  void _handleCard(String type) {
    if (_matchStage == 0 || _matchStage == 4) return;
    _showTeamSelectDialog("Select Team", (isTeamA) {
      _showPlayerSelectDialog("$type for:", isTeamA, (player) {
        setState(() {
          String icon = type == "YELLOW" ? "🟨" : "🟥";
          colCards.insert(0, "$icon $player ($_timeStr)");
        });
      });
    });
  }

  void _handleGenericStat(String statKey) {
    if (_matchStage == 0 || _matchStage == 4) return;
    showDialog(context: context, builder: (ctx) => AlertDialog(
      title: Text("$statKey Team?", style: const TextStyle(color: Colors.white)),
      content: Row(children: [
        Expanded(child: _teamBtn("TEAM A", Theme.of(context).primaryColor, () { setState(() => statsA[statKey] = statsA[statKey]! + 1); Navigator.pop(ctx); })),
        const SizedBox(width: 10),
        Expanded(child: _teamBtn("TEAM B", Colors.white, () { setState(() => statsB[statKey] = statsB[statKey]! + 1); Navigator.pop(ctx); })),
      ]),
    ));
  }

  void _handleSave() {
    if (_matchStage == 0 || _matchStage == 4) return;
    showDialog(context: context, builder: (ctx) => AlertDialog(
      title: const Text("Who Saved? 🧤", style: TextStyle(color: Colors.white)),
      content: Column(mainAxisSize: MainAxisSize.min, children: [
        ListTile(title: Text("Team A GK", style: TextStyle(color: Theme.of(context).primaryColor)), onTap: () { setState(() => statsA['SAVES'] = statsA['SAVES']! + 1); Navigator.pop(ctx); }),
        ListTile(title: const Text("Team B GK", style: TextStyle(color: Colors.white)), onTap: () { setState(() => statsB['SAVES'] = statsB['SAVES']! + 1); Navigator.pop(ctx); }),
      ]),
    ));
  }

  // --- Dialogs ---
  void _showTeamSelectDialog(String title, Function(bool) onSelect) {
    showDialog(context: context, builder: (ctx) => AlertDialog(
      title: Text(title, style: const TextStyle(color: Colors.white)),
      content: Row(children: [
        Expanded(child: _teamBtn("TEAM A", Theme.of(context).primaryColor, () { Navigator.pop(ctx); onSelect(true); })),
        const SizedBox(width: 10),
        Expanded(child: _teamBtn("TEAM B", Colors.white, () { Navigator.pop(ctx); onSelect(false); })),
      ]),
    ));
  }

  void _showPlayerSelectDialog(String title, bool isTeamA, Function(String) onSelect, {bool includeNone = false}) {
    List<String> list = isTeamA ? playersA : playersB;
    showModalBottomSheet(context: context, backgroundColor: Theme.of(context).cardColor, builder: (ctx) => Column(children: [
      Padding(padding: const EdgeInsets.all(16), child: Text(title, style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 18, fontWeight: FontWeight.bold))),
      Expanded(child: ListView.builder(itemCount: list.length + (includeNone?1:0), itemBuilder: (ctx, i) {
        if (includeNone && i==0) return ListTile(title: const Text("None", style: TextStyle(color: Colors.grey)), onTap: (){Navigator.pop(ctx); onSelect("None");});
        int idx = includeNone ? i-1 : i;
        return ListTile(title: Text(list[idx], style: const TextStyle(color: Colors.white)), onTap: (){Navigator.pop(ctx); onSelect(list[idx]);});
      })),
    ]));
  }

  void _editName(int i, bool isTeamA) {
    TextEditingController c = TextEditingController(text: isTeamA ? playersA[i] : playersB[i]);
    showDialog(context: context, builder: (ctx) => AlertDialog(
      title: const Text("Edit Name"),
      content: TextField(controller: c, autofocus: true, style: const TextStyle(color: Colors.white)),
      actions: [TextButton(onPressed: (){
        setState(() { if(isTeamA) playersA[i] = c.text; else playersB[i] = c.text; });
        Navigator.pop(ctx);
      }, child: Text("SAVE", style: TextStyle(color: Theme.of(context).primaryColor)))]
    ));
  }

  void _togglePaid(int i, bool isTeamA) {
    setState(() {
      String name = isTeamA ? playersA[i] : playersB[i];
      if (isTeamA) { paidA[i] = !paidA[i]; if(paidA[i]) colPaid.add(name); else colPaid.remove(name); }
      else { paidB[i] = !paidB[i]; if(paidB[i]) colPaid.add(name); else colPaid.remove(name); }
    });
  }

  void _printStats() {
    String txt = "MATCH REPORT\n$statsA\n$statsB\nGoals:\n${colGoals.join('\n')}\nPaid:\n${colPaid.join(', ')}";
    showDialog(context: context, builder: (ctx) => AlertDialog(content: SingleChildScrollView(child: Text(txt)), actions: [TextButton(onPressed:()=>Navigator.pop(ctx), child:const Text("OK"))]));
  }

  // --- UI COMPONENTS ---

  Widget _teamBtn(String txt, Color clr, VoidCallback tap) => ElevatedButton(style: ElevatedButton.styleFrom(backgroundColor: clr.withOpacity(0.2), foregroundColor: clr), onPressed: tap, child: Text(txt));

  @override
  Widget build(BuildContext context) {
    super.build(context);
    var accent = Theme.of(context).primaryColor;
    
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          // 1. TOP BAR (Timer + Status + Actions)
          _buildTopBar(accent),
          
          const SizedBox(height: 10),

          // 2. DYNAMIC CONTENT (Pre-Match VS Live Match)
          Expanded(
            flex: 3,
            child: _matchStage == 0 
                ? _buildPreMatchView(accent)  // 🟢 Show Lists (15 players)
                : _buildLiveDashboard(accent), // 🔴 Show Stats/Buttons
          ),

          const SizedBox(height: 10),

          // 3. BOTTOM COLUMNS (Always Visible)
          Expanded(flex: 2, child: _buildBottomColumns(accent)),
        ],
      ),
    );
  }

  Widget _buildTopBar(Color accent) {
    return Container(
      height: 55,
      padding: const EdgeInsets.symmetric(horizontal: 16),
      decoration: BoxDecoration(color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white10)),
      child: Row(
        children: [
          // Status
          _statusItem("1st", _matchStage >= 1, accent), const Icon(Icons.arrow_right, color: Colors.grey),
          _statusItem("2nd", _matchStage >= 3, accent), const Icon(Icons.arrow_right, color: Colors.grey),
          _statusItem("End", _matchStage == 4, accent),
          
          const Spacer(),
          Text(_timeStr, style: TextStyle(color: accent, fontSize: 26, fontWeight: FontWeight.bold, fontFamily: 'monospace')),
          const Spacer(),
          
          IconButton(icon: const Icon(Icons.print, color: Colors.blue), onPressed: _printStats),
          IconButton(icon: const Icon(Icons.refresh, color: Colors.red), onPressed: _resetAll),
        ],
      ),
    );
  }

  Widget _statusItem(String t, bool a, Color c) => Text(t, style: TextStyle(color: a ? c : Colors.grey, fontWeight: a ? FontWeight.bold : FontWeight.normal));

  // --- VIEW 1: PRE-MATCH (Lists) ---
  Widget _buildPreMatchView(Color accent) {
    return Column(
      children: [
        // Kick Off Button Large
        SizedBox(
          width: double.infinity, height: 50,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(backgroundColor: accent, foregroundColor: Colors.black),
            onPressed: _handleMatchCycle,
            child: Text(_btnText, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
          ),
        ),
        const SizedBox(height: 10),
        Expanded(
          child: Row(
            children: [
              Expanded(child: _buildEditableList(true, accent)),
              const SizedBox(width: 10),
              Expanded(child: _buildEditableList(false, Colors.white)),
            ],
          ),
        ),
      ],
    );
  }

  Widget _buildEditableList(bool isTeamA, Color color) {
    List<String> list = isTeamA ? playersA : playersB;
    List<bool> paid = isTeamA ? paidA : paidB;
    return Container(
      decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(12)),
      child: Column(
        children: [
          Padding(padding: const EdgeInsets.all(8), child: Text(isTeamA ? "TEAM A (15)" : "TEAM B (15)", style: TextStyle(color: color, fontWeight: FontWeight.bold))),
          Expanded(
            child: ListView.builder(
              itemCount: 15, // 15 Players
              itemBuilder: (ctx, i) => Container(
                margin: const EdgeInsets.symmetric(vertical: 2, horizontal: 8),
                child: Row(
                  children: [
                    // Paid Checkbox
                    GestureDetector(
                      onTap: () => _togglePaid(i, isTeamA),
                      child: Container(
                        width: 20, height: 20,
                        decoration: BoxDecoration(
                          color: paid[i] ? Colors.green : Colors.transparent,
                          border: Border.all(color: paid[i] ? Colors.green : Colors.grey),
                          borderRadius: BorderRadius.circular(4),
                        ),
                        child: paid[i] ? const Icon(Icons.check, size: 16, color: Colors.black) : null,
                      ),
                    ),
                    const SizedBox(width: 8),
                    // Editable Name
                    Expanded(
                      child: InkWell(
                        onTap: () => _editName(i, isTeamA),
                        child: Padding(
                          padding: const EdgeInsets.symmetric(vertical: 6),
                          child: Text(list[i], style: const TextStyle(color: Colors.white, fontSize: 13)),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  // --- VIEW 2: LIVE MATCH (Dashboard) ---
  Widget _buildLiveDashboard(Color accent) {
    return Row(
      children: [
        // Left: Stats
        Expanded(
          flex: 4,
          child: Container(
            decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white10)),
            child: Column(
              children: [
                Container(
                  padding: const EdgeInsets.all(16),
                  decoration: BoxDecoration(color: accent.withOpacity(0.1), borderRadius: const BorderRadius.vertical(top: Radius.circular(12))),
                  child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
                    Text("TEAM A", style: TextStyle(color: accent, fontWeight: FontWeight.bold)),
                    Text("${statsA['GOALS']} - ${statsB['GOALS']}", style: const TextStyle(fontSize: 36, fontWeight: FontWeight.bold, color: Colors.white)),
                    const Text("TEAM B", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
                  ]),
                ),
                Expanded(child: ListView(
                  padding: const EdgeInsets.all(8),
                  children: [
                    _statRow("SHOTS", statsA['SHOTS']!, statsB['SHOTS']!, Colors.blue),
                    _statRow("FOULS", statsA['FOULS']!, statsB['FOULS']!, Colors.orange),
                    _statRow("CORNERS", statsA['CORNERS']!, statsB['CORNERS']!, Colors.white),
                    _statRow("OFFSIDES", statsA['OFFSIDES']!, statsB['OFFSIDES']!, Colors.purple),
                    _statRow("SAVES", statsA['SAVES']!, statsB['SAVES']!, Colors.teal),
                  ],
                )),
              ],
            ),
          ),
        ),
        const SizedBox(width: 10),
        // Right: Buttons & Control
        Expanded(
          flex: 5,
          child: Column(
            children: [
              SizedBox(
                width: double.infinity, height: 50,
                child: ElevatedButton(
                  style: ElevatedButton.styleFrom(backgroundColor: _matchStage%2!=0 ? Colors.orange : accent, foregroundColor: Colors.black),
                  onPressed: _handleMatchCycle,
                  child: Text(_btnText, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
                ),
              ),
              const SizedBox(height: 8),
              Expanded(
                child: GridView.count(
                  crossAxisCount: 4, crossAxisSpacing: 6, mainAxisSpacing: 6, childAspectRatio: 1.1,
                  children: [
                    _actionBtn("FOUL", Colors.orange, Icons.sports_handball, ()=>_handleGenericStat('FOULS')),
                    _actionBtn("CORNER", Colors.white, Icons.flag, ()=>_handleGenericStat('CORNERS')),
                    _actionBtn("SHOT", Colors.blue, Icons.sports_soccer, ()=>_handleGenericStat('SHOTS')),
                    _actionBtn("OFFSIDE", Colors.purple, Icons.do_not_step, ()=>_handleGenericStat('OFFSIDES')),
                    _actionBtn("GOAL", accent, Icons.sports_soccer, _handleGoal),
                    _actionBtn("SAVE", Colors.teal, Icons.back_hand, _handleSave),
                    _actionBtn("YELLOW", Colors.yellow, Icons.style, ()=>_handleCard('YELLOW')),
                    _actionBtn("RED", Colors.red, Icons.style, ()=>_handleCard('RED')),
                  ],
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  Widget _statRow(String lbl, int v1, int v2, Color c) {
    return Padding(padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
      Text("$v1", style: TextStyle(color: c, fontWeight: FontWeight.bold, fontSize: 16)),
      Text(lbl, style: const TextStyle(color: Colors.grey, fontSize: 10)),
      Text("$v2", style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)),
    ]));
  }

  Widget _actionBtn(String l, Color c, IconData i, VoidCallback t) {
    return ElevatedButton(
      style: ElevatedButton.styleFrom(backgroundColor: c.withOpacity(0.15), foregroundColor: c, padding: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8), side: BorderSide(color: c.withOpacity(0.3)))),
      onPressed: t,
      child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [Icon(i, size: 20), Text(l, style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold))]),
    );
  }

  Widget _buildBottomColumns(Color accent) {
    return Container(
      decoration: BoxDecoration(color: Colors.black45, borderRadius: BorderRadius.circular(12)),
      child: Row(children: [
        _logCol("PAID 💰", colPaid, Colors.green),
        const VerticalDivider(width: 1, color: Colors.grey),
        _logCol("GOALS ⚽", colGoals, accent),
        const VerticalDivider(width: 1, color: Colors.grey),
        _logCol("ASSISTS 🎯", colAssists, Colors.blue),
        const VerticalDivider(width: 1, color: Colors.grey),
        _logCol("CARDS 🟥", colCards, Colors.red),
      ]),
    );
  }

  Widget _logCol(String t, List<String> d, Color c) {
    return Expanded(child: Column(children: [
      Container(width: double.infinity, padding: const EdgeInsets.all(6), color: Colors.white10, child: Text(t, textAlign: TextAlign.center, style: TextStyle(color: c, fontWeight: FontWeight.bold, fontSize: 10))),
      Expanded(child: ListView.builder(itemCount: d.length, itemBuilder: (ctx, i) => Text(d[i], textAlign: TextAlign.center, style: const TextStyle(fontSize: 11, color: Colors.white70)))),
    ]));
  }
}

What OS did this error occur on?

No response

What browser(s) did you experience this issue on?

No response

Do you have any additional information?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugIncorrect behavior (everything from a crash to more subtle misbehavior)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions