This commit is contained in:
2025-11-14 18:03:50 +09:00
parent 1f5cea9a96
commit 13ed537b23
342 changed files with 18293 additions and 0 deletions
@@ -0,0 +1,87 @@
// packages/feature_game_sudoku/lib/widgets/number_pad.dart
import 'package:flutter/material.dart';
import 'package:service_api/service_api.dart'; // 👈 SudokuTheme import
class NumberPad extends StatelessWidget {
final int blockSize;
final SudokuTheme theme;
final Map<int, int> numberCounts;
final int? selectedNumber;
final Function(int) onNumberTapped;
final bool isLandscape;
const NumberPad({
super.key,
required this.blockSize,
required this.theme,
required this.numberCounts,
required this.selectedNumber,
required this.onNumberTapped,
required this.isLandscape,
});
@override
Widget build(BuildContext context) {
final int gridSize = blockSize * blockSize;
final ThemeData themeData = Theme.of(context);
final bool isDark = themeData.brightness == Brightness.dark;
final Color selectedColor = themeData.primaryColor;
final Color onSelectedColor = themeData.colorScheme.onPrimary;
final Color completedColor = isDark ? Colors.white24 : Colors.black26;
final Color completedTextColor = isDark ? Colors.white54 : Colors.black54;
final Color defaultTextColor = isDark ? Colors.white70 : Colors.black87;
List<Widget> numberButtons = List.generate(gridSize, (index) {
int numberValue = index + 1;
String numberSymbol = theme.getSymbol(numberValue);
bool isSelected = (numberValue == selectedNumber);
bool isCompleted = (numberCounts[numberValue] ?? 0) >= gridSize;
Widget button = ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: isSelected ? selectedColor : null,
foregroundColor: isSelected ? onSelectedColor : defaultTextColor,
disabledBackgroundColor: completedColor,
disabledForegroundColor: completedTextColor,
padding: const EdgeInsets.all(4.0),
textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
),
onPressed: isCompleted
? null
: () => onNumberTapped(numberValue),
child: FittedBox(
fit: BoxFit.contain,
child: Text(numberSymbol),
),
);
if (isLandscape) {
return Flexible(child: button);
} else {
return button;
}
});
if (isLandscape) {
return Wrap(
runSpacing: 4.0,
spacing: 4.0,
children: numberButtons,
);
} else {
return GridView.count(
crossAxisCount: blockSize,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: numberButtons,
);
}
}
}
@@ -0,0 +1,113 @@
// packages/feature_game_sudoku/lib/widgets/sudoku_board.dart
import 'package:flutter/material.dart';
import 'package:service_api/service_api.dart'; // 👈 SudokuTheme import
class SudokuBoard extends StatelessWidget {
final int blockSize;
final SudokuTheme theme;
final List<int> cells;
final List<int> originalCells;
final int? selectedIndex;
final int? selectedNumberPad;
final Set<int> incorrectCells;
final Function(int) onCellTapped;
const SudokuBoard({
super.key,
required this.blockSize,
required this.theme,
required this.cells,
required this.originalCells,
required this.selectedIndex,
required this.selectedNumberPad,
required this.incorrectCells,
required this.onCellTapped,
});
@override
Widget build(BuildContext context) {
final int gridSize = blockSize * blockSize;
final double fontSize = (gridSize > 9) ? (gridSize > 16 ? 12 : 16) : 24;
final ThemeData themeData = Theme.of(context);
final ColorScheme colorScheme = themeData.colorScheme;
final bool isDark = themeData.brightness == Brightness.dark;
final Color thickBorderColor = colorScheme.onSurface.withOpacity(isDark ? 0.8 : 1.0);
final Color thinBorderColor = themeData.dividerColor;
final Color incorrectBg = colorScheme.error.withOpacity(0.2);
final Color highlightedBg = colorScheme.primary.withOpacity(0.2);
final Color editableBg = themeData.scaffoldBackgroundColor;
final Color fixedBg = isDark ? colorScheme.surfaceVariant : colorScheme.onSurface.withOpacity(0.1);
final Color selectedTextColor = colorScheme.secondary;
final Color incorrectTextColor = colorScheme.error;
final Color editableTextColor = colorScheme.primary;
final Color fixedTextColor = colorScheme.onSurface;
return AspectRatio(
aspectRatio: 1.0,
child: GridView.builder(
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: gridSize,
),
itemCount: gridSize * gridSize,
itemBuilder: (context, index) {
int row = index ~/ gridSize;
int col = index % gridSize;
int cellValue = cells[index];
bool isEditable = (originalCells[index] == 0);
bool isSelected = (index == selectedIndex);
bool isHighlighted = (cellValue != 0 &&
selectedNumberPad != null &&
cellValue == selectedNumberPad);
bool isIncorrect = incorrectCells.contains(index);
BorderSide thickBorder = BorderSide(color: thickBorderColor, width: 2.0);
BorderSide thinBorder = BorderSide(color: thinBorderColor, width: 0.5);
return GestureDetector(
onTap: () => onCellTapped(index),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: isIncorrect
? incorrectBg
: isHighlighted
? highlightedBg
: isEditable
? editableBg
: fixedBg,
border: Border(
top: (row == 0) ? thickBorder : thinBorder,
left: (col == 0) ? thickBorder : thinBorder,
right: (col == gridSize - 1) ? thickBorder : (col % blockSize == blockSize - 1) ? thickBorder : thinBorder,
bottom: (row == gridSize - 1) ? thickBorder : (row % blockSize == blockSize - 1) ? thickBorder : thinBorder,
),
),
child: Text(
cellValue == 0 ? '' : theme.getSymbol(cellValue),
style: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: isSelected
? selectedTextColor
: isIncorrect
? incorrectTextColor
: isEditable
? editableTextColor
: fixedTextColor,
),
),
),
);
},
),
);
}
}