import 'package:flutter/material.dart'; import 'state.dart'; import 'util.dart'; /// Board of a single game of tic tac toe class TTCGame extends StatefulWidget { const TTCGame({ super.key, required this.turn, this.data, this.onClick, }); final TTCState turn; final List? data; /// hook into acction of tapping on square final Function? onClick; @override State createState() => _TTCGameState(); } class _TTCGameState extends State { late TTCState turn; late List data; void setCellState(int index) { switch (data[index]) { case TTCState.empty: setState(() { data[index] = turn; turn = switch (turn) { TTCState.x => TTCState.o, TTCState.o => TTCState.x, TTCState.empty => TTCState.empty, }; }); widget.onClick?.call(); notifyWin(); break; default: ScaffoldMessenger.of(context) ..clearSnackBars() ..showSnackBar(const SnackBar( content: Text("Invalid Choice: cell already has value"))); } } void notifyWin() { TTCState state = Util.checkWin(data); if (state != TTCState.empty) { ScaffoldMessenger.of(context) ..clearSnackBars() ..showSnackBar( SnackBar(content: Text("${state.name.toUpperCase()} wins"))); } } Border _genCellBorder( int index, { BorderSide borderSide = const BorderSide(), }) { return Border( top: index < 3 ? BorderSide.none : borderSide, bottom: index > 5 ? BorderSide.none : borderSide, left: [0, 3, 6].contains(index) ? BorderSide.none : borderSide, right: [2, 5, 8].contains(index) ? BorderSide.none : borderSide, ); } Widget _genCell(int index, TTCState state) => Container( decoration: BoxDecoration(border: _genCellBorder(index)), child: TTCCell( state: state, stateSetCallback: () { setCellState(index); }, )); @override void initState() { super.initState(); turn = widget.turn; data = widget.data ?? [ TTCState.empty, TTCState.empty, TTCState.empty, TTCState.empty, TTCState.empty, TTCState.empty, TTCState.empty, TTCState.empty, TTCState.empty, ]; } @override Widget build(BuildContext context) { List cells = []; for (final (index, state) in data.indexed) { cells.add(_genCell(index, state)); } return GridView.count( crossAxisCount: 3, shrinkWrap: true, children: cells, ); } } class TTCCell extends StatelessWidget { const TTCCell({ super.key, required this.state, this.stateSetCallback, this.textStyle, }); final TTCState state; final VoidCallback? stateSetCallback; final TextStyle? textStyle; String get text => switch (state) { TTCState.empty => "", TTCState.x => "X", TTCState.o => "O", }; @override Widget build(BuildContext context) => InkWell( onTap: stateSetCallback, child: Center( child: Text(text, style: textStyle), )); }