I'm trying to create a ListView with Card widget.
I have the ListView and the Card but I can't figure out how to place widgets correctly inside the Card itself. This is how it looks now:
I want the favorites icon and the image to keep their place, but to move the text to be near the image and to have the "Jean-Paul-Gaultier" text to be closer to the "Le Male" and have it align left.
This is what I have so far:
Widget _myListView(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index){
return Card(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network('${decoded[index]['image']}'),
),
],
),
SizedBox(width: 8,),
Column(
children: <Widget>[
Text(
'${decoded[index]['name']}',
style: TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
Row(
children: <Widget>[
Text(
'${decoded[index]['brand']}',
style: TextStyle(
color: Colors.blueGrey,
fontWeight: FontWeight.normal,
fontSize: 12,
),
),
],
),
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(Icons.favorite_border),
),
],
),
],
),
);
},
);
}
Without the mainAxisAlignment: MainAxisAlignment.space-between the text is indeed closer to the image, but the favorites icon gets right near the text which is something I do not want.
I tried to search for answers but as I'm new to Flutter I don't really know the right keywords so I couldn't find anything.
A much easier approach would be using a ListTile
Widget _myListView(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("title"),
subtitle: Text("subtitle"),
leading: Container(
width: 48, height: 48,
child: Placeholder(), //place image here replacing the Placeholder widget
),
trailing: IconButton(
icon: Icon(Icons.favorite_border),
onPressed: () {},
),
),
);
},
);
}
which gives us
But if you want your code then here it is
Widget _myListView(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Row(
children: <Widget>[
Container(
margin: const EdgeInsets.all(8.0),
child: Placeholder(),
width: 48,
height: 48,
),
SizedBox(
width: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'title',
style: TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
Text(
'subtitle',
style: TextStyle(
color: Colors.blueGrey,
fontWeight: FontWeight.normal,
fontSize: 12,
),
),
],
),
Spacer(),
IconButton(
icon: Icon(Icons.favorite_border),
onPressed: () {},
),
],
),
);
},
);
}
EDIT
to reduce the space between title and subtitle replace the title with following code
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("title",style: Theme.of(context).textTheme.body2,),
Text("subtitle",style: Theme.of(context).textTheme.caption,),
],
),
Related
the question is exactly same as this post
but I did not find an answer from the post.
my goal is to set the title widget in the center
and other two buttons on the right side.
the solutions in the link all put the title widget slightly on the left side.
does anyone know how to do this without using Stack? I'm afraid of the title widget being over the button widgets when the title gets too long.
the code:
SizedBox(
height: 40,
width: MediaQuery.of(context).size.width,
child: Row(
children: [
const SizedBox(width: 17),
Text(
'TITLE',
style: const TextStyle(
color: Colors.white, fontSize: 30, fontWeight: FontWeight.w500),
),
Spacer(),
Row(
children: [
GestureDetector(
onTap: (){null;},
child: SizedBox(
child: Icon(Icons.wallet_membership, color: Colors.white),
),
),
const SizedBox(width: 17),
GestureDetector(
onTap: (){Get.toNamed('/wallet_setting');},
child: const SizedBox(
child: Icon(Icons.settings, color: Colors.white),
),
),
]
)
]
),
)
Try this One
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Container(
child: Center(
child: Text(
'TITLE',
style: const TextStyle(color: Colors.white, fontSize: 30, fontWeight: FontWeight.w500),
),
)
)
),
Align(
alignment: Alignment.centerRight,
child: Row(
children: [
GestureDetector(
onTap: (){null;},
child: SizedBox(
child: Icon(Icons.wallet_membership, color: Colors.white),
),
),
const SizedBox(width: 17),
GestureDetector(
onTap: (){Get.toNamed('/wallet_setting');},
child: const SizedBox(
child: Icon(Icons.settings, color: Colors.white),
),
),
]
)
)
],
)
OutPut:
i think you should wrap it with centre
Try this approach:
first make widget from your right widgets:
Widget _rightIcons() {
return Row(children: [
GestureDetector(
onTap: () {
null;
},
child: SizedBox(
child: Icon(Icons.wallet_membership, color: Colors.white),
),
),
const SizedBox(width: 17),
GestureDetector(
onTap: () {
Get.toNamed('/wallet_setting');
},
child: const SizedBox(
child: Icon(Icons.settings, color: Colors.white),
),
),
]);
}
then build your header like this:
Row(
children: [
_rightIcons(),
Expanded(
child: Center(
child: Text(
'TITLE',
style: const TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w500),
),
),
),
Opacity(
opacity: 0,
child: _rightIcons(),
)
],
),
You can use centerTitle:true on Appbar
appBar: AppBar(
centerTitle: true,
title: Text("title"),
actions: [
Another way
Container(
color: Colors.purple,
height: 40 * 2,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(onPressed: () {}, icon: Icon(Icons.navigate_before)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: SizedBox(),
),
Expanded(
flex: 4,
child: Text(
'TITLE',
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w500),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
child: Icon(Icons.wallet_membership,
color: Colors.white),
),
const SizedBox(width: 17),
GestureDetector(
onTap: () {},
child: const SizedBox(
child:
Icon(Icons.settings, color: Colors.white),
),
),
]),
),
)
],
),
],
),
)
Preferable solution:
You can implements PreferredSizeWidget to create custom Appbar. Play with color and other decoration.
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
const MyAppBar({super.key});
#override
Widget build(BuildContext context) {
return Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Text("title"),
),
Align(
alignment: Alignment.bottomRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
//you can use callback method to get onpressed
onPressed: () {},
icon: Icon(Icons.settings),
),
IconButton(
onPressed: () {},
icon: Icon(Icons.settings),
),
],
),
)
],
);
}
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight * 2);
}
And use like appBar: MyAppBar(),
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: new Text(prod_name),
leading: Image.asset(prod_img, width: 50.0, height: 50.0,),
subtitle: new Column(
children: <Widget>[
new Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(0.0),
child: new Text("ice-cream: "),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: new Text(prod_ice_cream, style: TextStyle(color: Colors.red),),
),
),
new Container(
alignment: Alignment.topLeft,
child: new Text("\Rs${prod_price}", style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),),
),
trailing: new Column(
children: <Widget>[
new IconButton(icon: Icon(Icons.arrow_drop_up), onPressed: (){}),
new IconButton(icon: Icon(Icons.arrow_drop_down), onPressed: (){})
],
),
),
);
I am getting "A RenderFlex overflowed by 40 pixels on the bottom" after adding those two icons
I have tried stackoverflow solution gave but after that I am getting "A RenderFlex overflowed by 21 pixels on the bottom". Here are some screenshots screenshot1 screenshot2 screenshot3. Here is my full code code
If Text widget is throwing overflow error then just set set the overflow property to a Text widget.
Example:
Flexible(
child: new Container(
padding: new EdgeInsets.only(right: 13.0),
child: new Text(
'Your Text Value Here',
overflow: TextOverflow.ellipsis,
),
),
),
Here is the solution using Container instead of ListTile:
class Single_cart_prod extends StatelessWidget {
final prod_name;
final prod_img;
final prod_price;
final prod_ice_cream;
final prod_sugar;
Single_cart_prod(
{this.prod_name,
this.prod_img,
this.prod_price,
this.prod_ice_cream,
this.prod_sugar});
#override
Widget build(BuildContext context) {
return Card(
child: Container(
width: double.infinity,
height: 80,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 2,
child: Image.asset(prod_img, width: 50.0, height: 50.0),
),
Expanded(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
prod_name,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
Text(
"Rs:" + prod_price.toString(),
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.black38),
),
],
),
),
Expanded(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Ice Cream",
),
Text(
prod_ice_cream,
style: TextStyle(color: Colors.red),
),
],
),
),
Expanded(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sugar",
),
Text(
prod_sugar,
style: TextStyle(color: Colors.red),
),
],
),
),
Expanded(
flex: 1,
child: Column(
children: <Widget>[
Expanded(
child: IconButton(
icon: Icon(Icons.arrow_drop_up), onPressed: () {})),
Expanded(
child: IconButton(
icon: Icon(Icons.arrow_drop_down),
onPressed: () {}))
],
),
)
]),
),
);
}
}
Output:
check out this example
import 'package:flutter/material.dart';
void main() => runApp(Cartproduct());
class Cartproduct extends StatefulWidget {
#override
_CartproductState createState() => _CartproductState();
}
class _CartproductState extends State<Cartproduct> {
var cart_list = [
{
"name": "Avocado",
"img": "images/avocado.jpg",
"price": 180,
"ice-cream": "2 scoops",
"sugar": "3 cube"
},
{
"name": "Mango",
"img": "images/mango.jpg",
"price": 180,
"ice-cream": "1 scoops",
"sugar": "2 cube"
},
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: new ListView.builder(
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return new Single_cart_prod(
prod_name: cart_list[index]['name'],
prod_img: cart_list[index]['img'],
prod_price: cart_list[index]['price'],
prod_ice_cream: cart_list[index]['ice-cream'],
prod_sugar: cart_list[index]['sugar'],
);
},
),
),
);
}
}
class Single_cart_prod extends StatelessWidget {
final prod_name;
final prod_img;
final prod_price;
final prod_ice_cream;
final prod_sugar;
Single_cart_prod(
{this.prod_name,
this.prod_img,
this.prod_price,
this.prod_ice_cream,
this.prod_sugar});
#override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: new Text(prod_name),
leading: Image.asset(
prod_img,
width: 50.0,
height: 50.0,
),
subtitle: new Column(
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Row(
children: <Widget>[
new Text("Ice-cream: "),
new Text(
prod_ice_cream,
style: TextStyle(color: Colors.red),
),
],
),
Row(
children: <Widget>[
new Text(" Sugar: "),
new Text(
prod_sugar,
style: TextStyle(color: Colors.red),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new Container(
alignment: Alignment.topLeft,
child: new Text(
"\Rs${prod_price}",
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold),
),
),
),
],
),
new Column(
children: <Widget>[
GestureDetector(
onTap: () {}, child: new Icon(Icons.arrow_drop_up)),
GestureDetector(
onTap: () {}, child: new Icon(Icons.arrow_drop_down)),
],
),
],
),
],
),
),
);
}
}
Let me know if it works
use SingleChildScrollView widget like this:
SingleChildScrollView(
child: your_main_Widget()
)
This is the design of appbar that I want to create using flutter and that should be responsive for all types of mobile devices and Tablets. Please let me know what to do to achieve this.
Widget _buildHeader() {
return Container(
height: MediaQuery.of(context).size.height* .22,
width: MediaQuery.of(context).size.width * 1,
color: Color(0xFF0A182E),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10, top: 40),
child: FlatButton(
onPressed: () {
print("homebtn clicked..!!"); //Home button
},
child: Text(
"Home",
style: TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.bold),
),
),
),
SizedBox(width: MediaQuery.of(context).size.width *.47,),
FlatButton(
onPressed: () {
print("profilebtn clicked");
},
shape: CircleBorder(),
child: Container(
margin: EdgeInsets.only(top: 20),
height: MediaQuery.of(context).size.height*.07,
width: MediaQuery.of(context).size.width*.14,
child: CircleAvatar(
backgroundImage: AssetImage("images/profilebtn.png"),
),
),
),
],
),
SizedBox(height: MediaQuery.of(context).size.height * .04,),
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10),
child: FlatButton(
onPressed: () {
print("Roomsbtn clicked.!");
},
child: Container(
child: Text(
"ROOMS",
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
FlatButton(
onPressed: () {
print("Devicesbtn clicked.!");
},
child: Container(
child: Text(
"DEVICES",
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(width: MediaQuery.of(context).size.width *.27,),
Transform.scale( //add Button
scale: .65,
child: FloatingActionButton(
onPressed: () {
print("Redplusbtn clicked.!");
_onButtonPressed();
},
child: Icon(
Icons.add,
size: 45,
),
backgroundColor: Colors.red,
),
),
],
),
],
),
);
}
This is what I have tried but I find this approach is lengthy and not responsive.I have tried in other devices and appbar is not responsive so I want to make this appbar in a Standard way.
I think this is way better solution. First of all there is no need to call media query to find a width and height. Another thing is that you can use AppBar widget and rework it to the way you like. Look at this example and amend it to your needs..
Scaffold(
appBar: AppBar(
title: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[Text('Hello'), Icon(Icons.email)],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Text('Option1'),
Text('Option2'),
],
),
Icon(Icons.language)
],
)
],
),
),
body: Container())
I would try putting the higher elements (title and picture) on the AppBar and the lower elements (Room, devices and plus icon) on a TabBar.
AppBar: https://api.flutter.dev/flutter/material/AppBar-class.html
TabBar: https://api.flutter.dev/flutter/material/TabBar-class.html
You can identify the customer device first using this plugin https://pub.dev/packages/device_info
the next steps, you can set the condition of the appbar in each device.
for more details you can check this link https://iiro.dev/2018/01/28/implementing-adaptive-master-detail-layouts/
So I am trying to learn BLoC pattern and apply it to my weather app. The thing is the UI is kinda big, there are a lot of Columns and Rows. So I'm confused about where to put BlocBuilder.
Or maybe this whole Container should be returned by one BLoC event? This is basically the main and only screen of the app for now.
class WeatherMainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: kBackgroundColor,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.only(
bottom: 30,
top: 15,
left: 30,
right: 30,
),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Today 28. apr',
style: TextStyle(
color: kAccentColor,
fontSize: 18,
fontWeight: FontWeight.w400,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
MenuSearchButton(
boxSize: 60,
onPressed: () => print('search pressed'),
content: Icon(
Icons.search,
color: Colors.white,
size: 30,
),
),
MenuSearchButton(
boxSize: 60,
onPressed: () => print('menu pressed'),
content: Icon(
Icons.menu,
color: Colors.white,
size: 30,
),
),
],
),
],
),
Container(
padding: EdgeInsets.symmetric(vertical: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'16',
style: TextStyle(
color: Colors.white,
fontSize: 100,
),
),
Container(
padding: EdgeInsets.only(left: 10.0),
child: Row(
children: <Widget>[
Icon(
Icons.cloud,
color: Colors.white,
size: 25.0,
),
SizedBox(width: 15.0),
Text(
'Raining',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
],
),
),
],
),
),
],
),
),
Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
bottom: 30,
top: 15,
left: 60,
right: 60,
),
child: Text(
'Put on your coat, and don\'t forget the umbrella.',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
Divider(
color: kAccentColor,
),
Container(
margin: EdgeInsets.only(
bottom: 30,
top: 15,
left: 30,
right: 30,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
DetailedCard(
header: 'HUMIDITY',
headerColor: kAccentColor,
text: '87%',
textColor: Colors.white,
),
DetailedCard(
header: 'WIND M/S',
headerColor: kAccentColor,
text: '4,1',
textColor: Colors.white,
),
DetailedCard(
header: 'FEELS LIKE',
headerColor: kAccentColor,
text: '18',
textColor: Colors.white,
),
],
),
),
],
),
],
),
),
);
}
}
Just separate your UI based on different possible states. Then use BlocBuilder and check states. Finally render UI for respective state.
You can check this video
You can definitely have multiple BlocBuilder. To make it simpler, let's take a look at this Counter example app.
I put this one wrapping a Column with 2 Text Widgets even though the only one supposed to be changing is the second Text Widget. (Not a good thing)
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Center(
child: Column(
children: <Widget>[
Text(
'You have pressed this many',
style: TextStyle(fontSize: 24.0),
),
Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
],
),
);
},
),
);
}
}
In this snippet, I put the BlocBuilder pricisely wrapping the only Widget that is changing on state changes. And if you ask Where in my code should I put BLoC builder? This approach is better because there won't be unnecessary Widget rebuilds happen.
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
children: <Widget>[
Text(
'You have pressed this many',
style: TextStyle(fontSize: 24.0),
),
BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text(
'$count',
style: TextStyle(fontSize: 24.0),
);
},
),
],
),
),
);
}
}
And for can I have more of them?, absolutely.
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return AppBar(title: Text('$count'));
},
),
body: Center(
child: Column(
children: <Widget>[
Text(
'You have pressed this many',
style: TextStyle(fontSize: 24.0),
),
BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text(
'$count',
style: TextStyle(fontSize: 24.0),
);
},
),
],
),
),
);
}
}
How to place the "question mark" at center of row?
Expected result is "?" are all at center regardless of the values on left/right.
body: ListView.builder(
itemCount: testList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
color: Colors.green,
child: Text(testList[index].n1.toString(), style: TextStyle(fontSize: 80,)),
),
Container(
color: Colors.red,
child: Text(testList[index].x, style: TextStyle(fontSize: 80,)),
),
Container(
color: Colors.green,
child: Text(testList[index].n2.toString(), style: TextStyle(fontSize: 80, )),
),
],
),
Text('>'),
Text('<'),
Text('='), //
],
),
);
}),
You can use row and Expanded with flex widget to achieve your desire output.
Following minimal code help you more.
Card(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Row(
children: [
Expanded(child: Container()),
Container(
color: Colors.green,
child: Text('10',
style: TextStyle(
fontSize: 80,
)),
),
],
),
),
Container(
color: Colors.red,
child: Text("?",
style: TextStyle(
fontSize: 80,
)),
),
Expanded(
flex: 1,
child: Row(
children: [
Container(
color: Colors.green,
child: Text('100',
style: TextStyle(
fontSize: 80,
)),
),
Expanded(child: Container()),
],
),
),
],
),
Text('>'),
Text('<'),
Text('='), //
],
),
)