I'm making a pop-up dialog that asks for the user's password when he enters certain screens. This field should have an icon that changes the password visibility, but the state change is not being made, it only happens when I exit the dialog and open it again.
getConfirmationPortalKey() {
return showGeneralDialog(
transitionBuilder: (ctx, anim1, anim2, child) => BackdropFilter(
filter:
ImageFilter.blur(sigmaX: 4 * anim1.value, sigmaY: 4 * anim1.value),
child: FadeTransition(
child: child,
opacity: anim1,
),
),
context: context,
barrierDismissible: true,
barrierLabel: '',
barrierColor: Colors.black26,
transitionDuration: Duration(milliseconds: 200),
pageBuilder: (ctx, anim1, anim2) => AlertDialog(
content: Wrap(
alignment: WrapAlignment.center,
children: [
Wrap(
alignment: WrapAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: 10, bottom: 10),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
hintText: 'Input Password',
suffixIconConstraints:
BoxConstraints.tightFor(height: 50, width: 50),
suffixIcon: Padding(
padding: EdgeInsets.only(top: 5),
child: SizedBox(
width: 24,
height: 24,
child: IconButton(
icon: passwordPortalVisible
? SvgPicture.asset(
"assets/svgs/icons/visibility_off.svg",
fit: BoxFit.scaleDown,
color: widget.colors.grey.shade400,
)
: SvgPicture.asset(
"assets/svgs/icons/visibility_on.svg",
fit: BoxFit.scaleDown,
color: widget.colors.grey.shade400,
),
onPressed: passwordVisible
? null
: () {
setState(() {
passwordVisible =
!passwordVisible;
});
},
),
),
),
),
controller: textEditingControllerKey,
keyboardType: TextInputType.number,
obscureText: !passwordVisible,
),
),
],
),
],
),
Padding(
padding:
EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 10),
child: Row(
children: [
Expanded(
widget._myBtns.elevatedButton('OK
', (){UserLogin user = UserLogin();})
),
],
),
),
),
],
),
);
}
I think you will need to use StatefulBuilder inside the AlertDialog. I think this is because the dialog is an overlay and doesn't know about the widget it came from.
https://api.flutter.dev/flutter/widgets/StatefulBuilder-class.html
Below is your code edited:
getConfirmationPortalKey() {
return showGeneralDialog(
transitionBuilder: (ctx, anim1, anim2, child) => BackdropFilter(
filter:
ImageFilter.blur(sigmaX: 4 * anim1.value, sigmaY: 4 * anim1.value),
child: FadeTransition(
child: child,
opacity: anim1,
),
),
context: context,
barrierDismissible: true,
barrierLabel: '',
barrierColor: Colors.black26,
transitionDuration: Duration(milliseconds: 200),
pageBuilder: (ctx, anim1, anim2) => AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setDialogState) {
bool passwordVisible = false;
return Wrap(
alignment: WrapAlignment.center,
children: [
Wrap(
alignment: WrapAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: 10, bottom: 10),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
hintText: 'Input Password',
suffixIconConstraints:
BoxConstraints.tightFor(height: 50, width: 50),
suffixIcon: Padding(
padding: EdgeInsets.only(top: 5),
child: SizedBox(
width: 24,
height: 24,
child: IconButton(
icon: passwordPortalVisible
? SvgPicture.asset(
"assets/svgs/icons/visibility_off.svg",
fit: BoxFit.scaleDown,
color: widget.colors.grey.shade400,
)
: SvgPicture.asset(
"assets/svgs/icons/visibility_on.svg",
fit: BoxFit.scaleDown,
color: widget.colors.grey.shade400,
),
onPressed:() => setDialogState(() {
passwordVisible =
!passwordVisible;
});
),
),
),
),
controller: textEditingControllerKey,
keyboardType: TextInputType.number,
obscureText: !passwordVisible,
),
),
],
),
],
),
Padding(
padding:
EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 10),
child: Row(
children: [
Expanded(
widget._myBtns.elevatedButton('OK
The problem is in this line:
onPressed: passwordVisible ? null : () {...}
Your onPressed event only calls the function if passwordVisible is false. Remove the ternary operator and it will call whether passwordVisible is true or false:
onPressed: () {...}
Related
I am trying to remove the blinking/flashing effect after the widgets are rebuild. I read about keys, but it's not working, I added a unique key to every GridView builder element as you see in the code below.
Now when I resize, the widgets are rebuilt because the setState function is called but I can not preserve their state.
void addOutfitPlannerDialog(BuildContext context) async{
await showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionBuilder: (context, a1, a2, widget) {
return SafeArea(
child: Transform.scale(
scale: a1.value,
child: Opacity(
opacity: a1.value,
child: AlertDialog(
actionsPadding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
contentPadding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
titlePadding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
buttonPadding: const EdgeInsets.all(0),
title: _getBackAndSaveButtons(context),
backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(0),
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return SizedBox(
width: 100.w,
height: 100.h,
child: Stack(
children: [
Positioned(
top: 0,
child:
Container(
width: 100.w,
height: 82.h,
color: const Color(0xff393432),
child: FittedBox(
fit: BoxFit.fill,
child: Container(
width: 100.w,
height: 100.h,
margin: EdgeInsets.fromLTRB(45, 10, 45, 15.h),
child: Screenshot(
controller: screenshotController,
child: Container(
color: Colors.white,
child: Stack(
children: stackChildren,
),
),
),
),
),
),
),
Positioned(
bottom: 0,
child: Container(
height: heightBottomOutfitPlanner,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide( // <--- top side
color: Colors.grey,
width: 1.0,
),
),
),
width: 100.w,
child: Column(
children: [
SizedBox(
width: 100.w,
height: 8.h,
child: Row(
children: [
Expanded(
flex: 7,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: outfitPlanerOrganizerEntriesList
.length,
itemBuilder: (BuildContext context,
int index) {
return InkWell(
onTap: () {
getOutfitPlannerTabTappedImages(
outfitPlanerOrganizerEntriesList[index],
setState);
},
child: Container(
padding: const EdgeInsets.fromLTRB(
30, 0, 30, 0),
decoration: (outfitPlanerOrganizerEntriesList[index] ==
myOutfitPlannerTabTappedImages[0]
.closetOrganizer) ||
(outfitPlanerOrganizerEntriesList[index] ==
"To Buy" &&
myOutfitPlannerTabTappedImages[0]
.closetOrganizer ==
null)
? const BoxDecoration(
border: Border(
bottom: BorderSide( // <--- top side
color: Color(0xffE4BCB4),
width: 3.0,
),
),
)
: null,
child: Center(child: Text(
outfitPlanerOrganizerEntriesList[index],
style: TextStyle(
fontSize: SizerUtil
.deviceType ==
DeviceType.mobile
? 16
: 25,
))),
),
);
}
),
),
const SizedBox(width: 30),
Expanded(
flex: 1,
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide( // <--- top side
color: Colors.black,
width: 1.0,
),
),
),
child: GestureDetector(
onPanStart: (details) =>
_handleDrag(details, setState),
onPanUpdate: (details) =>
_handleUpdate(details, setState),
child: Icon(Icons.drag_indicator,
size: SizerUtil.deviceType ==
DeviceType.mobile ? 35 : 45,
),
)
),
),
],
),
),
const SizedBox(height: 5),
!isLoadingOutfitPlannerTabTappedImages ? Container(
width: 100.w,
margin: const EdgeInsets.fromLTRB(5, 0, 5, 0),
height: heightBottomOutfitPlanner - 10.h,
child: GridView.builder(
physics: const ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
crossAxisSpacing: 5,
mainAxisSpacing: 5,
maxCrossAxisExtent: SizeConfig.screenWidth! /
4,),
itemCount: myOutfitPlannerTabTappedImages
.length,
itemBuilder: (BuildContext ctx, index) {
return InkWell(
key: Key(index.toString()),
onTap: () {
try {
removeBackground(ctx, setState, CleverCloset.dataFromBase64String(myOutfitPlannerTabTappedImages[index].getImage!));
}
catch (e) {
stackChildren.add(MoveableStackItem(CleverCloset.imageFromBase64String(myOutfitPlannerTabTappedImages[index].getImage!).image));
}
},
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(20))
),
width: SizeConfig.screenWidth! / 4,
height: SizeConfig.screenWidth! / 4,
child: FittedBox(
child:
FadeInImage(
placeholder: const AssetImage(
'assets/placeholder.gif'),
image: CleverCloset
.imageFromBase64String(
myOutfitPlannerTabTappedImages[index]
.getImage!)
.image,
fit: BoxFit.fill,
),
fit: BoxFit.fill,
),
),
);
}
),
) : Container(),
],
),
),
),
],
),
);
}
)
),
),
),
);
},
transitionDuration: const Duration(milliseconds: 100),
barrierDismissible: false,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {return Container();}
);
}
You can add the below logic to your code. It basically gets executed when the widget has build.
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => function(context));
}
And now you can implement the function(context) to do what you need to do after the build.
I am using a textfield in my modal bottom sheet. When I open the keyboard to type, it shows me error for renderflex overflowed. I used singlechildscrollview and isScrollControlled true, still error comes up. Any solutions?
return showModalBottomSheet(
context: context,
builder: (context) => Builder(
builder: (context) => SingleChildScrollView(
child: Container(
height: 350,
width: MediaQuery.of(context).size.width*0.9,
color: cardColor2,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10),
child: Column(
children: [
ValueListenableBuilder(
valueListenable: _image,
builder: (BuildContext context, File f, Widget? child) {
return SizedBox(
height: f.path!='' ? 180 : 280,
width: MediaQuery.of(context).size.width*0.9,
child: TextField(
decoration: InputDecoration(
hintText: 'Type your question here',
border: InputBorder.none,
),
),
);
},
),
ValueListenableBuilder(
valueListenable: _image,
builder: (BuildContext context, File f, Widget? child) {
return f.path!='' ? SizedBox(
width: 150.0,
height: 100.0,
child: Image.file(
_image.value,
fit: BoxFit.fill,
),
) : Container();
},
),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Container(
height: 1,
color: tertiaryTextColor2,
width: double.infinity,
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(top: 13.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () {
_onAlertPress(context);
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: Text(
'Add an image',
style: TextStyle(
color: backgroundColor2,
fontSize: 14,
),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
),
SizedBox(
width: 10,
),
InkWell(
onTap: () {
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: Text(
'Post',
style: TextStyle(
color: backgroundColor2,
fontSize: 14,
),
),
decoration: BoxDecoration(
color: brandYellow,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
),
],
),
),
),
],
),
),
],
),
),
),
),
isScrollControlled: true,
);
try to remove height of Container because the height is not enough for other child container or sizedbox
return showModalBottomSheet(
context: context,
builder: (context) => Builder(
builder: (context) => SingleChildScrollView(
child: Container(
height: 350, ---------------> remove this
width: MediaQuery.of(context).size.width*0.9,
I am trying to get rid of this blinking/flashing of the widget presented in the image below. This is an Alert Dialog opened. Below it's a GridView Builder which creates images. The images are refreshing when I am trying to drag and resize the height of the container. I know setState it's called multiple times, but it doesn't matter, the widget should be immutable.
I changed the Grid View to a Stateless Widget, after trying a while using AutomaticKeepAliveClientMixin, wantToKeepAlive, but nothing is changing.
class GridViewBuilderOutiftTabTapped extends StatelessWidget {
final List<CleverCloset> myClassVar;
const GridViewBuilderOutiftTabTapped(this.myClassVar);
#override
Widget build(BuildContext context) {
return GridView.builder(
physics: const ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
crossAxisSpacing: 5,
mainAxisSpacing: 5,
maxCrossAxisExtent: SizeConfig.screenWidth!/4,),
itemCount: myClassVar.length,
itemBuilder: (BuildContext ctx, index) {
return InkWell(
onTap: (){
},
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20))
),
width: SizeConfig.screenWidth!/4,
height: SizeConfig.screenWidth!/4,
child: FittedBox(
child:
FadeInImage(
placeholder: const AssetImage('assets/placeholder.gif'),
image: CleverCloset.imageFromBase64String(myClassVar[index].getImage!).image,
fit: BoxFit.fill,
),
fit: BoxFit.fill,
),
),
);
}
);
}
}
void addOutfitPlannerDialog(BuildContext context) async{
await showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionBuilder: (context, a1, a2, widget) {
return SafeArea(
child: Transform.scale(
scale: a1.value,
child: Opacity(
opacity: a1.value,
child: AlertDialog(
actionsPadding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
contentPadding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
titlePadding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
buttonPadding: const EdgeInsets.all(0),
title: _getBackAndSaveButtons(context),
backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(0),
content: SizedBox(
width: 100.w,
height: 100.h,
child: Stack(
children: [
Positioned(
top: 0,
child: Container(
width: SizeConfig.screenWidth,
height: 82.h,
color: const Color(0xff393432),
child: Container(
margin: EdgeInsets.fromLTRB(45, 8, 45, 10.h),
color: Colors.white,
),
),
),
Positioned(
bottom: 0,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Container(
height: heightBottomOutfitPlanner,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide( // <--- top side
color: Colors.grey,
width: 1.0,
),
),
),
width: 100.w,
child: Column(
children: [
SizedBox(
width: 100.w,
height: 8.h,
child: Row(
children: [
Expanded(
flex:7,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: outfitPlanerOrganizerEntriesList.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: (){
getOutfitPlannerTabTappedImages(outfitPlanerOrganizerEntriesList[index], setState);
},
child: Container(
padding: const EdgeInsets.fromLTRB(30, 0, 30, 0),
decoration: (outfitPlanerOrganizerEntriesList[index]==myOutfitPlannerTabTappedImages[0].closetOrganizer) || (outfitPlanerOrganizerEntriesList[index]=="To Buy" && myOutfitPlannerTabTappedImages[0].closetOrganizer==null) ? const BoxDecoration(
border: Border(
bottom: BorderSide( // <--- top side
color: Color(0xffE4BCB4),
width: 3.0,
),
),
):null,
child: Center(child: Text(outfitPlanerOrganizerEntriesList[index],
style: const TextStyle(
fontSize: 16,
))),
),
);
}
),
),
const SizedBox(width: 30),
Expanded(
flex:1,
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide( // <--- top side
color: Colors.black,
width: 1.0,
),
),
),
child: GestureDetector(
onPanStart:(details) => _handleDrag(details, setState),
onPanUpdate:(details) => _handleUpdate(details, setState),
child: const Icon(Icons.drag_indicator,
size: 35,
),
)
),
),
],
),
),
const SizedBox(height: 5),
!isLoadingOutfitPlannerTabTappedImages ? Container(
width: 100.w,
margin: const EdgeInsets.fromLTRB(5, 0, 5, 0),
height: heightBottomOutfitPlanner-10.h,
child: GridViewBuilderOutiftTabTapped(myOutfitPlannerTabTappedImages),
) : Container(),
],
),
);
},
),
)
],
),
)
),
),
),
);
},
transitionDuration: const Duration(milliseconds: 100),
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {return Container();}
);
}
I leave the entire code fragment, the main idea is to have a textfield that, by using the keyboard, shows me a series of elements, in the description variable, I have that element to select, this works well, the only issue is, how to keep my alertdialog with all the correct notes.
return await Get.generalDialog(
barrierLabel: "Barrier",
barrierDismissible: false,
transitionBuilder: (context, a1, a2, widget) {
return StatefulBuilder(builder: (context, setState) {
return Transform.scale(
scale: a1.value,
child: Opacity(
opacity: a1.value,
child: Dialog(
insetAnimationCurve: Curves.bounceOut,
insetPadding: EdgeInsets.only(right: 20, left: 20),
//backgroundColor: colorFour(),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: SingleChildScrollView(
child: Container(
height: 340,
child: Column(
children: <Widget>[
Container(
alignment: Alignment.topCenter,
height: 50,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
padding: EdgeInsets.all(5),
child: Center(
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600),
maxLines: 2,
textAlign: TextAlign.center,
),
),
),
SingleChildScrollView(
padding: EdgeInsets.only(left: 10, right: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Container(
margin: EdgeInsets.only(top: 10),
child: TextFormField(
controller: controller,
decoration: InputDecoration(
border: OutlineInputBorder(
gapPadding: 5.0)),
keyboardType: TextInputType.text,
maxLines: 1,
onChanged: (String cadena) async {
listado = pivote
.where((x) => x.descripcion
.split("/")[value]
.toLowerCase()
.contains(
cadena.toLowerCase()))
.toList();
setState(() {});
},
),
),
Container(
height: 200,
margin: EdgeInsets.only(
top: 10, bottom: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(5)),
border: Border.all(
color: Colors.grey, width: 1)),
child: ListView.builder(
padding: const EdgeInsets.all(0.0),
physics: BouncingScrollPhysics(),
itemCount: listado.length,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Container(
height: 34,
child: InkWell(
child: SizedBox.expand(
child: Align(
alignment:
Alignment.centerLeft,
child: Padding(
padding:
EdgeInsets.only(
right: 5,
left: 5),
child: Text(
listado[index]
.descripcion
.split(
"/")[value],
textAlign:
TextAlign.left),
),
),
),
onTap: () {
Get.back(result: {
"item": listado[index],
});
}),
);
}),
),
],
)),
],
),
),
),
),
));
});
},
pageBuilder: (context, animation1, animation2) {
return Container();
});
I need to overlay my alertdialog, that is to say that the size does not break my design, this happens after focusing on the textfield
Show us some code to help you out.
In any case, it might make sense to wrap the layout in a SingleChildScrollView.
Here is a very good explanation.
https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
yourAlertDialog(
context: context,
builder: (BuildContext context) {
return SingleChildScrollView(child: yourchild);
},
);
Your layout should be wrapped in Scrollview , I didn't replicate or run this code but it should be like this:
AlertDialog(
title: Text("title", style: TextStyle(color: ThemeColors.colorPrimary),),
content: Container(
width: double.maxFinite,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: StatefulBuilder(builder: (BuildContext context1, StateSetter setState) {
this.setState = setState;
return Container(
height: 340,
child: Column(
children: <Widget>[
//your container
],
),
),
}),
)),
);
How would I manage the responsiveness on different devices since the app works great on emulator Pixel 3 XL, but on real device such as Samsung - SM-G991B with screen resolution 1080.0 X 2256.0
as you can see all images are overlapping text does not looks good, maybe i am doing something wrong with the code..
How i can manage responsiveness on different devices ?
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ListView(
physics: PageScrollPhysics(),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: kDefaultPadding, vertical: kDefaultPadding),
child: Text('Меню', style: kStyleHeaders),
),
SizedBox(
height: 10.0,
),
Builder(builder: (_) {
if (_isLoading) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 200.0),
child: Center(child: null),
);
}
if (_apiResponse.error) {
return Center(child: Text(_apiResponse.errorMessage));
}
if (_apiResponse.data.length == 0) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 200.0),
child: Center(
child: Text(
'No products has been found..!',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
fontFamily: 'BuffetBold',
color: Colors.black,
),
),
),
);
}
return GridView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: _apiResponse.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.72,
mainAxisSpacing: 1.0,
crossAxisSpacing: 1.0,
),
itemBuilder: (context, index) {
var product = _apiResponse.data[index];
return CachedNetworkImage(
imageUrl: product.imageMedium,
imageBuilder: (context, imageProvider) => Column(
children: [
GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetail(
id: product.id,
title: product.title,
description: product.description,
structure: product.structure,
imageLarge: product.imageLarge,
weight: product.weight,
hasDiscount: product.hasDiscount,
priceBefore:
double.parse(product.priceBefore),
priceAfter:
double.parse(product.priceAfter),
isHit: product.isHit,
isNew: product.isNew,
isSpicy: product.isSpicy,
isRecommended: product.isRecommended,
isVegetarian: product.isVegetarian,
attributes: product.attributes),
),
),
child: Container(
alignment: Alignment(-1, 0.9),
child: (product.isNew == '1')
? Image.asset(
'assets/images/new.png',
width: 60.0,
)
: null,
height: 165.0,
width: 165.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 2.0,
spreadRadius: 0,
offset: Offset(0, 2))
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 10.0),
child: Row(
children: [
Flexible(
child: Text(product.title,
style: kStyleTitle))
],
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
product.weight != null
? product.weight
: '',
style: kStyleWeight),
Text(
product.hasDiscount != '0'
? product.priceBefore
: '',
style: kStyleDiscount),
Container(
margin: EdgeInsets.symmetric(
horizontal: 3, vertical: 0),
height: 30.0,
width: 70.0,
child: FlatButton(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0),
),
color: kPrimaryColor,
textColor: Colors.white,
padding:
EdgeInsets.symmetric(horizontal: 5.0),
onPressed: () {
// ... Todo
context.read<OrderNotify>().addOrder(
CartOrder(
productId: product.id,
product: product,
qty: 1,
price: hasDiscount(product)),
);
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(hasDiscount(product),
style: kStylePrice),
Icon(
FontAwesomeIcons.rubleSign,
size: 13.0,
color: Colors.white,
)
],
),
),
),
],
),
),
],
),
placeholder: (context, url) => Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Color(0xffB81F33)),
),
),
errorWidget: (context, url, error) => Icon(
Icons.error,
size: 40.0,
color: Theme.of(context).primaryColor,
),
);
},
);
}),
],
),
],
),
),
);
}
On your first container you don't have a rendered size, try to give a size to your first container, not infinity and not maxFinite size.