I have this code which displays a back button, text, and then a listview builder widget.
When I remove the listview builder widget, the back button works fine, but when I add it back the back button breaks. I don’t know why this is happening and need this issue to be fixed pretty quickly. Thanks!
Here’s the code for the screen (everything works perfectly so the other screens passing in data are not included):
import 'package:flutter/material.dart';
import 'package:workout_app/Screens/Components/Sign_Up_Screens/screen2.dart';
class SingleSelectListViewWithLogo extends StatefulWidget {
final List<String> items;
const SingleSelectListViewWithLogo({Key? key, required this.items}) : super(key: key);
@override
_SingleSelectListViewWithLogoState createState() =>
_SingleSelectListViewWithLogoState();
}
class _SingleSelectListViewWithLogoState extends State<SingleSelectListViewWithLogo> {
int? selectedIndex;
void returnScreen(context) {
print('returning');
Navigator.of(context).pushReplacement(
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => screen2(),
),
);
}
bool nextValid = false;
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Material(
child: Container (
decoration: const BoxDecoration(color: Colors.grey),
height: size.height,
width: double.infinity,
child: Stack(
children: <Widget> [
Positioned(
top: size.height * .06,
left: size.width * .03,
child: InkWell(
onTap: () {
returnScreen(context);
},
child: Image.asset(
alignment: Alignment.topLeft,
"assets/images/back2.png",
width: size.width * .07,
),
),
),
Positioned(
top: size.height * .09,
left: size.width * .4,
child: const Text(
style: TextStyle(fontSize: 30, color: Color.fromARGB(255, 4, 3, 3)),
'Goals'
)
),
Positioned(
top: size.height * .15,
left: size.width * .07,
child: const Text(
style: TextStyle(fontSize: 20, color: Color.fromARGB(255, 49, 48, 48)),
'What body type do you want to get?'
)
),
Positioned(
top: size.height * .26,
left: size.width * .4,
child: const Text(
style: TextStyle(fontSize: 15, color: Color.fromARGB(255, 33, 31, 31)),
'Select 1'
)
),
ListView.builder(
itemCount: widget.items.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
nextValid = true;
});
},
child: Container(
height: size.height * .15,
decoration: BoxDecoration(
color: selectedIndex == index ? Color.fromARGB(255, 40, 188, 72) : Color.fromARGB(255, 202, 195, 195),
border: Border.all(
color: selectedIndex == index ? Color.fromARGB(255, 16, 66, 37) : Colors.transparent,
width: 3,
),
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.fromLTRB(16, index == 0 ? MediaQuery.of(context).size.height * 0.265 : 0, 16, 8),
child: Row(
children: [
Flexible(
child: Text(widget.items[index]),
)
],
),
),
);
},
),
Positioned(
top: size.height * .86,
left: size.width * .1,
child: SizedBox(
width: size.width * .8,
height: size.height * .08,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(!nextValid ? Color.fromRGBO(69, 75, 85, 1) : Color.fromARGB(255, 0, 147, 246)),
),
child: const Text('Continue',
style: TextStyle(fontSize: 20),
),
onPressed: () async {
},
),
),
),
]
)
)
);
}
}
Thanks!
>Solution :
The problem here is that you’re using Stack
widget everywhere, even when you’re rendering the ListView.builder
. When this is the case, the ListView.builder
covers the whole screen, especially when not given a bounded height/width. In that case since everything is in a Stack
, based on how you write the widgets inside it, some of them stay on top while others will stay on the bottom, therefore not tappable. If I were to rewrite this implementation I would go with a Column
and it would be like the following:
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Material(
child: SafeArea(
child: Container(
decoration: const BoxDecoration(color: Colors.grey),
height: size.height,
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: returnScreen,
child: const Icon(Icons.back_hand),
),
const Text(
style: TextStyle(
fontSize: 30, color: Color.fromARGB(255, 4, 3, 3)),
'Goals'),
const Text(
style: TextStyle(
fontSize: 20, color: Color.fromARGB(255, 49, 48, 48)),
'What body type do you want to get?'),
const Text(
style: TextStyle(
fontSize: 15, color: Color.fromARGB(255, 33, 31, 31)),
'Select 1'),
Expanded(
child: ListView.builder(
itemCount: widget.items.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
nextValid = true;
});
},
child: Container(
height: size.height * .15,
decoration: BoxDecoration(
color: selectedIndex == index
? const Color.fromARGB(255, 40, 188, 72)
: const Color.fromARGB(255, 202, 195, 195),
border: Border.all(
color: selectedIndex == index
? const Color.fromARGB(255, 16, 66, 37)
: Colors.transparent,
width: 3,
),
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.fromLTRB(
16,
index == 0
? MediaQuery.of(context).size.height * 0.265
: 0,
16,
8),
child: Row(
children: [
Flexible(
child: Text(widget.items[index]),
)
],
),
),
);
},
),
),
Positioned(
top: size.height * .86,
left: size.width * .1,
child: SizedBox(
width: size.width * .8,
height: size.height * .08,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
!nextValid
? const Color.fromRGBO(69, 75, 85, 1)
: const Color.fromARGB(255, 0, 147, 246)),
),
child: const Text(
'Continue',
style: TextStyle(fontSize: 20),
),
onPressed: () async {},
),
),
),
])),
));
}
But if you’re adamant in using a Stack
instead of Column
this is how you should go with it:
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Material(
child: Container(
decoration: const BoxDecoration(color: Colors.grey),
height: size.height,
width: double.infinity,
child: Stack(
children: <Widget>[
Positioned(
top: size.height * .09,
left: size.width * .4,
child: const Text(
style: TextStyle(
fontSize: 30, color: Color.fromARGB(255, 4, 3, 3)),
'Goals')),
Positioned(
top: size.height * .15,
left: size.width * .07,
child: const Text(
style: TextStyle(
fontSize: 20, color: Color.fromARGB(255, 49, 48, 48)),
'What body type do you want to get?')),
Positioned(
top: size.height * .26,
left: size.width * .4,
child: const Text(
style: TextStyle(
fontSize: 15, color: Color.fromARGB(255, 33, 31, 31)),
'Select 1')),
ListView.builder(
itemCount: widget.items.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
nextValid = true;
});
},
child: Container(
height: size.height * .15,
decoration: BoxDecoration(
color: selectedIndex == index
? const Color.fromARGB(255, 40, 188, 72)
: const Color.fromARGB(255, 202, 195, 195),
border: Border.all(
color: selectedIndex == index
? const Color.fromARGB(255, 16, 66, 37)
: Colors.transparent,
width: 3,
),
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.fromLTRB(
16,
index == 0
? MediaQuery.of(context).size.height * 0.265
: 0,
16,
8),
child: Row(
children: [
Flexible(
child: Text(widget.items[index]),
)
],
),
),
);
},
),
Positioned(
top: size.height * .86,
left: size.width * .1,
child: SizedBox(
width: size.width * .8,
height: size.height * .08,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(!nextValid
? const Color.fromRGBO(69, 75, 85, 1)
: const Color.fromARGB(255, 0, 147, 246)),
),
child: const Text(
'Continue',
style: TextStyle(fontSize: 20),
),
onPressed: () async {},
),
),
),
Positioned(
top: size.height * .06,
left: size.width * .03,
child: InkWell(
onTap: () {
returnScreen(context);
},
child: Image.asset(
alignment: Alignment.topLeft,
"assets/images/back2.png",
width: size.width * .07,
),
),
),
],
),
),
);
}
Notice how I put the InkWell
as the last child of the Stack, so it stays at the top and becomes tappable.