In my app, I often use conditional widget visibility to hide or display a widget. It works fine but recently, the design was not as expected as you can see in below images.
All Chips are laid in a Row with mainAxisAlignment: MainAxisAlignment.spaceEvenly. When there are 5 buttons, the design is ok, but with 4 buttons (the middle one is hidden), a additional space appears in the middle, which is not perfect.
Here the code
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const Icon(
Icons.school,
),
onPressed: () {...
},
),
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const ImageIcon(
AssetImage("assets/images/Intervention.png"),
),
onPressed: () {
...
},
),
deviceId != -1
? ActionChip(
backgroundColor:
Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const ImageIcon(
AssetImage("assets/images/History.png"),
),
onPressed: () {
...
},
)
: const SizedBox(
),
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const Icon(
Icons.save_alt,
),
onPressed: () {
...
},
),
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const Icon(
Icons.settings,
),
onPressed: () {
...
},
),
],
),
I tried to put the middle button in a Visibility widget, but the result was the same.
How to solve this ugly problem?
Thank you
When you use SizedBox it actually take invisible space in that row so row consider it as an item and put padding around it because of spaceEvenly setting. Instead of put SizedBox in else condition, you can use if and set nothing for else condition, like this:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const Icon(
Icons.school,
),
onPressed: () {...
},
),
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const ImageIcon(
AssetImage("assets/images/Intervention.png"),
),
onPressed: () {
...
},
),
if(deviceId != -1) //<--- add this
ActionChip(
backgroundColor:
Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const ImageIcon(
AssetImage("assets/images/History.png"),
),
onPressed: () {
...
},
),
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const Icon(
Icons.save_alt,
),
onPressed: () {
...
},
),
ActionChip(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
padding: EdgeInsets.all(4),
shape: const CircleBorder(),
label: const Icon(
Icons.settings,
),
onPressed: () {
...
},
),
],
),
Result: top row is for using if the second row is for not using it:
Related
[
I can create a button with an icon using this code
ElevatedButton.icon(
icon: Icon(
Icons.home,
color: Colors.green,
size: 30.0,
),
label: Text('Elevated Button'),
onPressed: () {
print('Button Pressed');
},
style: ElevatedButton.styleFrom(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(20.0),
),
),
)
but how to put an arrow on the right side of the button?
As per your shared Image I have try same design in Various ways choice is yours 😊which way you want to try.
Using ElevatedButton.icon
ElevatedButton.icon(
icon: const Icon(
Icons.mail,
color: Colors.green,
size: 30.0,
),
label: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('Change Email Address'),
Icon(Icons.arrow_forward_ios)
],
),
onPressed: () {
print('Button Pressed');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
side: const BorderSide(color: Colors.black),
),
fixedSize: const Size(double.infinity, 40),
),
),
Result Using ElevatedButton.icon ->
Using OutlinedButton.icon
OutlinedButton.icon(
icon: const Icon(
Icons.mail,
color: Colors.green,
size: 30.0,
),
label: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text(
'Change Email Address',
style: TextStyle(
color: Colors.black,
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.grey,
)
],
),
onPressed: () {
print('Button Pressed');
},
style: ElevatedButton.styleFrom(
side: const BorderSide(color: Colors.black,),
fixedSize: const Size(double.infinity, 40),
),
),
Result Using OutlinedButton.icon ->
Using ListTile
ListTile(
onTap: () {
print('Button Pressed');
},
visualDensity: const VisualDensity(horizontal: -4,vertical: -4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
side: const BorderSide(color: Colors.black),
),
leading: const Icon(Icons.mail,color: Colors.green),
trailing: const Icon(Icons.arrow_forward_ios),
title: const Text('Change Email Address'),
),
Result Using ListTile ->
Using GestureDetector
GestureDetector(
onTap: () {
print('Button Pressed');
},
child: Container(
padding:const EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 1,
),
borderRadius: BorderRadius.circular(10),),
child: Row(
children: const [
Icon(Icons.mail, color: Colors.green),
SizedBox(width: 10,),
Text('Change Email Address'),
Spacer(),
Icon(Icons.arrow_forward_ios),
],
),
),
),
Result Using GestureDetector ->
Using InkWell
InkWell(
onTap: () {
print('Button Pressed');
},
child: Container(
padding:const EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 1,
),
borderRadius: BorderRadius.circular(10),),
child: Row(
children: const [
Icon(Icons.mail, color: Colors.green),
SizedBox(width: 10,),
Text('Change Email Address'),
Spacer(),
Icon(Icons.arrow_forward_ios),
],
),
),
),
Result Using InkWell->
It seems like you want a ListTile widget, as it has leading/trailing properties:
Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 1,
),
),
child: const ListTile(
leading: Icon(Icons.mail),
trailing: Icon(Icons.arrow_forward_ios),
title: Text('Change Email Address'),
),
)
You can also use IconButton instead of a regular Icon in this example.
To add two icons to an elevated button, just wrap your child widget with a row widget. See implementation below:
ElevatedButton(
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Icon(Icons.home),
Text('Home'),
Icon(Icons.navigate_next)
],
),
),
I need to add 3 buttons at the bottom, all buttons need in one row top of the body content
I need to add 3 buttons at the bottom of app
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('WinLife'),
elevation: 10,
backgroundColor: const Color(0XFF82B58D),
leading: Container(
padding: EdgeInsets.all(5),
child: Image.asset('assets/images/logo/WinLife.png'),
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.favorite,
color: Colors.white,
),
onPressed: () {},
),
IconButton(
icon: Icon(
Icons.settings,
color: Colors.white,
),
onPressed: () {},
)
],
),
body: ListView.builder(
itemBuilder: (BuildContext ctx, int index) {
return Padding(
padding: EdgeInsets.all(10),
child: Card(
shadowColor: const Color(0XFF82B58D),
shape: Border.all(
color: const Color(0XFF82B58D),
width: 2,
),
elevation: 50,
color: const Color(0XFF82B58D),
child: Column(
children: <Widget>[
Image.asset(imgList[index]),
SizedBox(
height: 200,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const <Widget>[
Icon(
Icons.favorite,
color: Colors.white,
size: 25,
),
Icon(
Icons.download,
color: Colors.white,
size: 25,
),
Icon(
Icons.share,
color: Colors.white,
size: 25,
),
],
),
],
),
),
);
},
itemCount: imgList.length,
),
);
}
There is a property called persistentFooterButtons in scaffold widget. It is using to show widgets to the screen footer. you can add any type of widgets inside to that. below some example code with output image FYR
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('WinLife'),
elevation: 10,
backgroundColor: const Color(0XFF82B58D),
leading: Container(
padding: const EdgeInsets.all(5),
child: Image.asset('assets/images/logo/WinLife.png'),
),
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.favorite,
color: Colors.white,
),
onPressed: () {},
),
IconButton(
icon: const Icon(
Icons.settings,
color: Colors.white,
),
onPressed: () {},
)
],
),
body: Container(),
persistentFooterButtons: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
persistentFooterButtonWidget(),
persistentFooterButtonWidget(),
persistentFooterButtonWidget(),
],
),
],
);
}
persistentFooterButtonWidget() {
return OutlinedButton.icon(
label: const Text("Book now", style: TextStyle(color: Colors.black)),
onPressed: () {},
icon: const Icon(Icons.library_add_check_sharp, color: Colors.black, size: 20.0),
style: ButtonStyle(
fixedSize: MaterialStateProperty.all(
const Size(170.0, 40.0),
),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
)),
side: MaterialStateProperty.all(
BorderSide(color: Colors.orange.shade200, width: 2)),
overlayColor: MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return Colors.orange.shade200;
}
if (states.contains(MaterialState.pressed)) {
return Colors.orange.shade200;
}
return null; // Defer to the widget's default.
}),
),
);
}
I am trying to create a Bottom Navigation Bar with a button group in the middle. The 2 icons on either side of the button group will switch the displayed screen. The centered button group has 3 buttons which would act on the displayed screen. This is similar to the new Google Assistant bottom bar
.
I tried using the BottomNavigationBar with the centre item a custom widget. However the sizes of all the items end up equal. Any help creating this layout would be appreciated. Thanks.
This is the design I am trying to achieve
Here's what I have now:
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.secondary,
leading: IconButton(
icon: Image.asset(alInvertedIcon, width: 32, semanticLabel: "Home screen"),
onPressed: () {
push(const HomeScreen());
},
iconSize: 32,
),
title: Image.asset(alTextInv, width: 175),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.person_outlined, semanticLabel: "User Profile"),
onPressed: () {
push(const ProfileScreen());
},
iconSize: 32,
)
],
),
body: bodyWidget,
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
showSelectedLabels: false,
showUnselectedLabels: false,
backgroundColor: Theme.of(context).colorScheme.secondary,
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Colors.grey,
currentIndex: _currentIndex,
items: [
const BottomNavigationBarItem(
icon: Icon(
Icons.camera_alt,
),
label: 'Visual',
),
BottomNavigationBarItem(
icon: SizedBox(
width: 300,
child: _actionPanel(),
),
label: 'Add',
),
const BottomNavigationBarItem(
icon: Icon(
Icons.chat,
),
label: 'Text',
),
],
),
),
);
}
Widget _actionPanel() {
return Material(
elevation: 1.0,
borderRadius: const BorderRadius.all(Radius.circular(25)),
child: Row(
children: <Widget>[
IconButton(
onPressed: () {},
icon: const Icon(
Icons.refresh,
),
),
IconButton(
onPressed: () {},
icon: const Icon(
Icons.mic,
),
),
IconButton(
onPressed: () {},
icon: const Icon(
Icons.camera,
),
),
],
),
);
}
Try using this way.
Widget _actionPanel() {
return Material(
elevation: 1.0,
borderRadius: BorderRadius.all(Radius.circular(25)),
child: Container(
padding: EdgeInsets.symmetric(vertical: 3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MaterialButton(
onPressed: () {},
shape: CircleBorder(),
minWidth: 0,
padding: EdgeInsets.all(5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: Icon(Icons.refresh),
),
MaterialButton(
onPressed: () {},
shape: CircleBorder(),
minWidth: 0,
padding: EdgeInsets.all(5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: Icon(Icons.mic),
),
MaterialButton(
onPressed: () {},
shape: CircleBorder(),
minWidth: 0,
padding: EdgeInsets.all(5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: Icon(Icons.camera),
),
],
),
),
);
}
Im trying to have a navigation bar with a slider and some buttons on a bottom app bar, but i found that when i initially added in my slider widget it caused my BottomAppBar to go to the top of my screen and so without the use of main axis alignment it will not remain at the bottom. is there a reason that the slider or my layout causes such an issue ?
Code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Title"),
backgroundColor: Colors.black87,
centerTitle: true,
actions: <Widget>[
FlatButton(
textColor: Colors.white,
minWidth: 1,
onPressed: () {},
child: Icon(Icons.arrow_back),
shape: CircleBorder(side: BorderSide(color: Colors.transparent)),
),
//Title( color: Colors.black,),
// title:Text("Title_Data"),backgroundColor: Colors.blueAccent,centerTitle: false,
FlatButton(
textColor: Colors.white,
minWidth: 1,
onPressed: () {},
child: Icon(
Icons.bookmark_border_outlined,
size: 25,
),
shape: CircleBorder(side: BorderSide(color: Colors.transparent)),
),
FlatButton(
textColor: Colors.white,
minWidth: 1,
onPressed: () {},
child: Icon(Icons.workspaces_filled),
shape: CircleBorder(side: BorderSide(color: Colors.transparent)),
),
FlatButton(
textColor: Colors.white,
minWidth: 1,
onPressed: () {},
child: Icon(Icons.brightness_4_outlined),
shape: CircleBorder(side: BorderSide(color: Colors.transparent)),
),
],
),
bottomNavigationBar: Column(
//mainAxisAlignment: MainAxisAlignment.end,
children: [
BottomAppBar(
color: Colors.black87,
child: new Row(
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
textColor: Colors.white,
minWidth: 1,
onPressed: () {},
child: Icon(Icons.fast_rewind),
shape:
CircleBorder(side: BorderSide(color: Colors.transparent)),
),
Text(
"0",
style: TextStyle(
color: Colors.white70,
fontSize: 16,
fontWeight: FontWeight.bold),
),
Slider(
inactiveColor: Colors.deepPurpleAccent,
value: _currentSliderValue,
min: 0,
max: 100,
divisions: 100,
label: _currentSliderValue.round().toString(),
activeColor: Colors.tealAccent,
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
),
Text(
"100",
style: TextStyle(
color: Colors.white70,
fontSize: 16,
fontWeight: FontWeight.bold),
),
FlatButton(
textColor: Colors.white,
minWidth: 1,
onPressed: () {},
child: Icon(Icons.fast_forward),
shape:
CircleBorder(side: BorderSide(color: Colors.transparent)),
),
],
),
),
],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Image Goes Here",
softWrap: true,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
'https://i.imgur.com/KqM0AMm.jpg',
width: 400,
errorBuilder: (BuildContext context, Object exception,
StackTrace stackTrace) {
return FlatButton(
textColor: Colors.black,
minWidth: 1,
onPressed: () {},
child: Icon(Icons.error),
shape: CircleBorder(
side: BorderSide(color: Colors.transparent)),
);
},
)),
],
),
);
}
}
With no alignment
mainAxisSize: MainAxisSize.min,
bottomNavigationBar: Column(
//mainAxisAlignment: MainAxisAlignment.end,
//mainAxisSize: MainAxisSize.min,
children: [
BottomAppBar()
]
)
The problem is that if BottomAppBar is the first child of bottomNavigationBar and it has columns as a children, it will cause this issue. mainAxisSize: MainAxisSize.min should be set to explicitly changing default positioning behavior of the widget like the following:
bottomNavigationBar:
Column(
mainAxisSize: MainAxisSize.min, // this column and this line solves the issue
children: [
BottomAppBar(
child: Padding(
padding: const EdgeInsets.all(8),
child: OverflowBar(
overflowAlignment: OverflowBarAlignment.center,
alignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
// your columns here
Column(children :[
]),
Column(children :[
]),
Column(children :[
]),
)),
])),
How to make this arrow icon in the center of the button
it's see the edge of icon is the center of icon, and I want make flutter see the center of icon is the center of icon
for more explain:
my code:
AppBar(
backgroundColor: Colors.transparent,
leading:
// Here the code of the button
SizedBox(
height: getProportionateScreenHeight(20),
width: getProportionateScreenWidth(10),
child: Padding(
padding: EdgeInsets.all(8),
child: FlatButton(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Colors.white,
onPressed: () {
Navigator.pop(context);
},
child: Center(
child: Icon(
Icons.arrow_back_ios,
),
),
),
),
),
// ....
actions: [
IconButton(
icon: Icon(
Icons.favorite,
color: favFlag ? Colors.red : Colors.red[100],
),
onPressed: () {
setState(() {
favFlag = !favFlag;
});
},
),
],
);
And it's was working with me before last upgrade.
I think there is some problem with the use of your height and width factor for SizedBox. I used simple values for these and it is working fine.
In fact, I removed the SizedBox widget and there was no such effect. I have attached the pic after the code as well :)
appBar: AppBar(
backgroundColor: Colors.transparent,
leading:
// Here the code of the button
SizedBox(
height: 20,
width: 20,
child: Padding(
padding: EdgeInsets.all(8),
child: FlatButton(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Colors.white,
onPressed: () {
// Navigator.pop(context);
},
child: Center(
child: Icon(
Icons.arrow_back_ios,
),
),
),
),
),
actions: [
IconButton(
icon: Icon(
Icons.favorite,
color: favFlag ? Colors.red : Colors.red[100],
),
onPressed: () {
setState(() {
favFlag = !favFlag;
});
},
),
],
),
Output:
My solution
AppBar(
backgroundColor: Colors.transparent,
// Here the code of the button
leading: Padding(
padding: EdgeInsets.all(8),
child: FlatButton(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Colors.white,
onPressed: () {
Navigator.pop(context);
},
child: Stack(
alignment: Alignment.center,
children: [
Positioned.directional(
textDirection: Directionality.of(context),
end: 3,
child: Icon(
Icons.arrow_back_ios,
),
),
],
),
),
),
// ....
actions: [
IconButton(
icon: Icon(
Icons.favorite,
color: favFlag ? Colors.red : Colors.red[100],
),
onPressed: () {
setState(() {
favFlag = !favFlag;
});
AdsService.listByPagination();
},
),
],
);
I've positioned it to center manually, so I prefer if there an another clean short solution.