Skip to content

Instantly share code, notes, and snippets.

@romanejaquez
Created August 5, 2024 13:21
Show Gist options
  • Save romanejaquez/c7bd4b26669b27ba4b702a6f3dd6a634 to your computer and use it in GitHub Desktop.
Save romanejaquez/c7bd4b26669b27ba4b702a6f3dd6a634 to your computer and use it in GitHub Desktop.
domino game pieces
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MultiProvider(
providers: [ChangeNotifierProvider(create: (_) => UserPiecesSelection())],
child: MainApp()));
}
class MainApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: DominoGame(),
),
);
}
}
class DominoGame extends StatefulWidget {
@override
State<DominoGame> createState() => _DominoGameState();
}
class _DominoGameState extends State<DominoGame> {
DominoModel? acceptedData;
List<DominoModel> addedDominos = Utils.takeRandom7Dominos();
List<DominoPiece> renderDominoPieces() {
List<DominoPiece> pieces = [];
addedDominos.forEach((DominoModel p) {
p.isOnTheTable = true;
pieces.add(DominoPiece(domino: p));
});
return pieces;
}
@override
Widget build(BuildContext context) {
UserPiecesSelection userSelection =
Provider.of<UserPiecesSelection>(context, listen: false);
return Container(
child: Stack(children: [
SingleChildScrollView(
child: Transform.scale(
scale: 0.8,
origin: Offset.zero,
child: Container(
margin: const EdgeInsets.only(top: 200, bottom: 200),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: renderDominoPieces(),
),
DragTarget<DominoModel>(
builder: (context, accepted, rejected) {
return Container(
margin: const EdgeInsets.only(top: 5),
child: Center(
child: Image.asset(
'assets/imgs/dominoborder.png',
width: 90,
height: 180),
));
},
onAccept: (DominoModel model) {
userSelection.removeDominoPiece(model);
setState(() {
addedDominos.add(DominoModel(
topNumber: model.isFlipped!
? model.bottomNumber
: model.topNumber,
bottomNumber: model.isFlipped!
? model.topNumber
: model.bottomNumber));
});
},
)
]),
)),
),
Align(
alignment: Alignment.topRight,
child: UserBadge(
userName: 'roman_jaquez',
userAvatar:
'https://avatars.githubusercontent.com/u/5081804?v=4',
opponent: false)),
Align(
alignment: Alignment.bottomLeft,
child: UserBadge(
userName: 'linda0516',
userAvatar:
'https://image.freepik.com/free-photo/close-up-shot-pretty-woman-with-perfect-teeth-dark-clean-skin-having-rest-indoors-smiling-happily-after-received-good-positive-news_273609-1248.jpg',
opponent: true)),
Consumer<UserPiecesSelection>(
builder: (context, piecesSelection, child) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: EdgeInsets.only(bottom: 100),
child: UserDominoPieces()),
);
},
)
]),
decoration: const BoxDecoration(
gradient: RadialGradient(
colors: [Utils.greenGradient1, Utils.greenGradient2])));
}
}
class UserBadge extends StatelessWidget {
bool? opponent;
String? userName;
String? userAvatar;
UserBadge({this.opponent, this.userName, this.userAvatar});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 20),
height: 150,
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 40),
child: Stack(clipBehavior: Clip.none, children: [
Align(
alignment: Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.only(top: 50),
padding: const EdgeInsets.only(
top: 30, left: 15, bottom: 12, right: 15),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(15),
),
child: opponent!
? const Text('7 Pieces Remaining',
style: TextStyle(color: Colors.white, fontSize: 10))
: Consumer<UserPiecesSelection>(
builder: (context, piecesSelection, child) {
var remainingPieces =
piecesSelection.userPieces.length;
return Text('$remainingPieces Pieces Remaining',
style: const TextStyle(
color: Colors.white, fontSize: 10));
},
))),
Align(
alignment: Alignment.centerRight,
child: Container(
padding: const EdgeInsets.only(
left: 5, top: 5, bottom: 5, right: 60),
decoration: BoxDecoration(
color:
opponent! ? Utils.opponentBarColor : Utils.userBarColor,
borderRadius: BorderRadius.circular(50),
boxShadow: [
BoxShadow(
offset: Offset.zero,
blurRadius: 20,
color: Colors.black.withOpacity(0.5))
]),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.only(
left: 20, top: 5, bottom: 5, right: 20),
decoration: BoxDecoration(
color: opponent!
? Utils.opponentScoreBadgeColor
: Utils.userScoreBadgeColor,
borderRadius: BorderRadius.circular(20),
),
child: const Text('200',
style: TextStyle(color: Colors.white))),
Text('$userName',
style: const TextStyle(color: Colors.white))
])),
),
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: () {
if (!opponent!) {
var piecesSelection = Provider.of<UserPiecesSelection>(
context,
listen: false);
piecesSelection.toggleUserPieces();
}
},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('$userAvatar'),
fit: BoxFit.cover),
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
offset: Offset.zero,
blurRadius: 20,
color: Colors.black.withOpacity(0.5))
],
border: Border.all(
width: 3,
color: opponent!
? Utils.oponentAvatarBorderColor
: Utils.userAvatarBorderColor))),
))
]));
}
}
class DominoPiece extends StatefulWidget {
DominoModel? domino;
DominoPiece({this.domino});
@override
State<DominoPiece> createState() => _DominoPieceState();
}
class _DominoPieceState extends State<DominoPiece> {
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: () {
if (!widget.domino!.isOnTheTable!) {
setState(() {
widget.domino!.isFlipped = !widget.domino!.isFlipped!;
});
}
},
child: Container(
margin: const EdgeInsets.all(2),
child: Stack(children: [
Container(
margin: const EdgeInsets.only(top: 8),
width: 80,
height: 170,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Utils.dominoPieceBackColor,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
blurRadius: 30,
offset: Offset.zero)
])),
Container(
width: 80,
height: 170,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Utils.dominoPieceFrontColor),
child: Column(children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
alignment: Alignment.center,
width: 90,
height: 80,
child: DominoNumber(
number: widget.domino!.isFlipped!
? widget.domino!.bottomNumber!
: widget.domino!.topNumber!))),
Stack(children: [
Container(
height: 2,
color: Colors.grey,
margin: const EdgeInsets.only(
left: 10, right: 10, top: 3, bottom: 3),
alignment: Alignment.center,
),
Center(
child: ClipOval(
child: Container(
color: Colors.grey, width: 8, height: 8)))
]),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
alignment: Alignment.center,
width: 90,
height: 80,
child: DominoNumber(
number: widget.domino!.isFlipped!
? widget.domino!.topNumber!
: widget.domino!.bottomNumber!)))
]))
])),
));
}
}
class DominoNumber extends StatelessWidget {
int? number;
DominoNumber({this.number});
Map<int, List<int>> numberMap = {
0: [],
1: [4],
2: [0, 8],
3: [0, 4, 8],
4: [0, 2, 6, 8],
5: [0, 2, 4, 6, 8],
6: [0, 1, 2, 6, 7, 8]
};
List<Widget> generateDominoDots() {
List<Column> columns = [];
int dotCount = 0;
for (var i = 0; i < 3; i++) {
Column col = Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [],
);
for (var d = 0; d < 3; d++) {
var dotWidget = Visibility(
visible: numberMap[number]!.contains(dotCount),
child: ClipOval(
child: Container(
width: 15, height: 15, color: Utils.dominoDotColor)),
);
col.children.add(dotWidget);
dotCount++;
}
columns.add(col);
}
return columns;
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: generateDominoDots(),
);
}
}
class UserDominoPieces extends StatefulWidget {
@override
State<UserDominoPieces> createState() => _UserDominoPiecesState();
}
class _UserDominoPiecesState extends State<UserDominoPieces> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer<UserPiecesSelection>(
builder: (context, piecesService, child) {
return SizedBox(
height: 300,
child: Stack(
children: [
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 160,
margin: const EdgeInsets.only(left: 20, bottom: 40),
decoration: const BoxDecoration(
color: Utils.userPiecesHandBgColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
bottomLeft: Radius.circular(20))),
)),
ListView.builder(
padding: const EdgeInsets.only(left: 50),
scrollDirection: Axis.horizontal,
itemCount: piecesService.userPieces.length,
itemBuilder: (context, index) {
var currentDomino = piecesService.userPieces[index];
return InteractiveDominoPiece(
data: currentDomino,
);
})
],
));
});
}
}
class DominoModel {
int? topNumber;
int? bottomNumber;
bool? isFlipped = false;
bool? isOnTheTable = false;
DominoModel({this.topNumber, this.bottomNumber});
}
class InteractiveDominoPiece extends StatefulWidget {
DominoModel? data;
InteractiveDominoPiece({this.data});
@override
State<InteractiveDominoPiece> createState() => _InteractiveDominoPieceState();
}
class _InteractiveDominoPieceState extends State<InteractiveDominoPiece> {
bool pieceBeingDragged = false;
@override
Widget build(BuildContext context) {
return Draggable(
data: widget.data!,
child: Container(
margin: const EdgeInsets.only(right: 10),
child: Opacity(
opacity: pieceBeingDragged ? 0.25 : 1.0,
child: DominoPiece(domino: widget.data))),
feedback: Container(
margin: const EdgeInsets.only(right: 10),
child: DominoPiece(domino: widget.data)),
onDragStarted: () {
setState(() {
pieceBeingDragged = true;
});
},
onDragCompleted: () {
setState(() {
pieceBeingDragged = false;
});
},
onDragEnd: (DraggableDetails d) {
setState(() {
pieceBeingDragged = false;
});
},
);
}
}
class Utils {
// colors
static const Color dominoDotColor = Color(0xFF4A1010);
static const Color userPiecesHandBgColor = Color(0xFF0D8F9A);
static const Color dominoPieceBackColor = Color(0xFFEFD99F);
static const Color dominoPieceFrontColor = Color(0xFFFFFCF4);
static const Color greenGradient1 = Color(0xFF007B17);
static const Color greenGradient2 = Color(0xFF004D0F);
static const Color opponentScoreBadgeColor = Color(0xFF620012);
static const Color userScoreBadgeColor = Color(0xFF353807);
static const Color oponentAvatarBorderColor = Color(0xFFEF1C44);
static const Color userAvatarBorderColor = Color(0xFFB0B704);
static const Color opponentBarColor = Color(0xFFA5001F);
static const Color userBarColor = Color(0xFF7F8609);
static const int initialUserPieces = 7;
static List<DominoModel> allDominos() {
List<DominoModel> dominos = [];
for (var i = 0; i < initialUserPieces; i++) {
for (var j = i; j < initialUserPieces; j++) {
dominos.add(DominoModel(topNumber: i, bottomNumber: j));
}
}
return dominos;
}
static List<DominoModel> takeRandom7Dominos() {
var rand = Random();
var randomPieces = [];
var allPieces = allDominos();
var index = rand.nextInt(allPieces.length);
while (randomPieces.length < initialUserPieces) {
if (!randomPieces.contains(index)) {
randomPieces.add(index);
}
index = rand.nextInt(allPieces.length);
}
List<DominoModel> myPieces = [];
for (var element in randomPieces) {
myPieces.add(allPieces[element]);
}
return myPieces;
}
}
class UserPiecesSelection extends ChangeNotifier {
bool showUserPieces = false;
List<DominoModel> userPieces = [];
UserPiecesSelection() {
userPieces = Utils.takeRandom7Dominos();
}
void toggleUserPieces() {
showUserPieces = !showUserPieces;
notifyListeners();
}
void removeDominoPiece(DominoModel pieceModel) {
userPieces.remove(pieceModel);
notifyListeners();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment