I got a task in which I had to make a something like crossword puzzle. First of all, I will show you the exact image which I want to achieve.
I have tried using many possible ways like
tried using GridView
and Table
widgets provided in flutter.
Tried putting GridView/Table
inside GestureDetector
but the problem is that I can't get the word on which the user dragged with his finger. The alphabets and the correct word is coming from server-side. Also when the user dragged on some alphabets then if words match then I have to make an oval shape on the correct words and so there could be many words so many oval shapes. This means that how can I make the oval shape?
Using Positioned
or some other tricks?
I searched for any packages in flutter which could help me but unfortunately, I didn't find any.
Okay as promised I have an answer for you I want apologize for how messy it is. It's getting really late here and I wanted to get this to you tonight. Now this might not be this best way but it works and you could definitely modularize some parts of my code into their own functions. You are probably going to want to test this as I'm sure it is breakable at this point and add conditions as necessary. There seems like there should be an easier way to do this but I am could not find one so this is what I went with.
List<bool> isSelected = [];
List<String> selectedLetters = [];
Map<GlobalKey, String> lettersMap;
Offset initialTappedPosition = Offset(0, 0);
Offset initialPosition = Offset(0, 0);
Offset finalPosition;
int intialSquare;
int crossAxisCount = 4; //whether you use GridView or not still need to provide this
int index = -1;
bool isTapped = false;
String selectedWord = '';
double width = 50;
double height = 50;
Size size;
List<String> letters = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'b',
'b',
'b',
'b',
'z',
];
@override
void initState() {
super.initState();
lettersMap =
Map.fromIterable(letters, key: (i) => GlobalKey(), value: (i) => i[0]);
isSelected = List.generate(letters.length, (e) => false);
}
_determineWord() {
double differnce;
int numberOfSquares;
if ((finalPosition.dx - initialPosition.dx) > 20) {
print('right');
//moved right
differnce = finalPosition.dx - initialPosition.dx;
numberOfSquares = (differnce / size.width).ceil();
for (int i = intialSquare + 1;
i < (intialSquare + numberOfSquares);
i++) {
isSelected[i] = true;
}
for (int i = 0; i < isSelected.length; i++) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
} else if ((initialPosition.dx - finalPosition.dx) > 20) {
print('left');
// moved left
differnce = finalPosition.dx + initialPosition.dx;
numberOfSquares = (differnce / size.width).ceil();
for (int i = intialSquare - 1;
i >= (intialSquare - numberOfSquares + 1);
i--) {
isSelected[i] = true;
}
for (int i = 0; i < isSelected.length; i++) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
} else if ((finalPosition.dy - initialPosition.dy) > 20) {
//moved up when moving up/down number of squares numberOfSquares is also number of rows
differnce = finalPosition.dy - initialPosition.dy;
numberOfSquares = (differnce / size.height).ceil();
for (int i = intialSquare + crossAxisCount;
i < (intialSquare + (numberOfSquares * crossAxisCount));
i += 4) {
isSelected[i] = true;
}
for (int i = 0; i < isSelected.length; i++) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
} else if ((initialPosition.dy - finalPosition.dy) > 20) {
//moved down
differnce = initialPosition.dy - finalPosition.dy;
numberOfSquares = (differnce / size.height).ceil();
for (int i = intialSquare - crossAxisCount;
i > (intialSquare - (numberOfSquares * crossAxisCount));
i -= 4) {
isSelected[i] = true;
print('$i');
}
for (int i = isSelected.length - 1; i >= 0; i--) {
if (isSelected[i]) {
selectedWord += letters[i];
}
}
print(selectedWord);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: GestureDetector(
child: GridView(
physics: NeverScrollableScrollPhysics(), //Very Important if
// you don't have this line you will have conflicting touch inputs and with
// gridview being the child will win
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
childAspectRatio: 2,
),
children: <Widget>[
for (int i = 0; i != lettersMap.length; ++i)
Listener(
child: Container(
key: lettersMap.keys.toList()[i],
child: Text(
lettersMap.values.toList()[i],
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.amber,
fontSize: 18,
),
),
),
onPointerDown: (PointerDownEvent event) {
final RenderBox renderBox = lettersMap.keys
.toList()[i]
.currentContext
.findRenderObject();
size = renderBox.size;
setState(() {
isSelected[i] = true;
intialSquare = i;
});
},
),
],
),
onTapDown: (TapDownDetails details) {
//User Taps Screen
// print('Global Position: ${details.globalPosition}');
setState(() {
initialPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
initialTappedPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
isTapped = true;
});
// print(initialPosition);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
// print('${details.delta.dy}');
setState(() {
if (details.delta.dy < 0) {
initialTappedPosition = Offset(initialTappedPosition.dx,
initialTappedPosition.dy + details.delta.dy);
height -= details.delta.dy;
} else {
height += details.delta.dy;
}
finalPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
});
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
// print('${details.delta.dx}');
setState(() {
if (details.delta.dx < 0) {
initialTappedPosition = Offset(
initialTappedPosition.dx + details.delta.dx,
initialTappedPosition.dy,
);
width -= details.delta.dx;
} else {
width += details.delta.dx;
}
finalPosition = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
});
},
onHorizontalDragEnd: (DragEndDetails details) {
_determineWord();
},
onVerticalDragEnd: (DragEndDetails details) {
_determineWord();
},
),
),
),
Positioned(
top: initialTappedPosition.dy,
left: initialTappedPosition.dx,
child: Container(
height: height,
width: width,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(
color: isTapped ? Colors.blue : Colors.transparent,
width: 3.0,
),
),
),
),
),
],
),
);
}
Pleasure working with you hope your project goes well. I tried to clean up all the unnecessary print statements sorry if I missed any.