I am building a Flutter web-app, where I have a menu interface with 4 Iconbuttons and 1 MaterialButton that is the users profile, like this:
I am using Flexible to make sure there is no overflow, but if I minimise the window, the Iconbuttons overflow, like this:
Is there any way to prevent this from happening? This is my code:
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
flex: 2,
child: IconButton(
icon: Icon(
Icons.chat_bubble_outline_rounded,
color: Colors.white,
),
onPressed: null),
),
Flexible(
flex: 2,
child: IconButton(
icon: Icon(
Icons.settings_outlined,
color: Colors.white,
),
onPressed: null),
),
Flexible(
flex: 2,
child: IconButton(
icon: Icon(
FontAwesomeIcons.wallet,
color: Colors.white,
),
onPressed: null),
),
Flexible(
flex: 2,
child: IconButton(
icon: Icon(
Icons.notifications_outlined,
color: Colors.white,
),
onPressed: null),
),
MaterialButton(
shape: CircleBorder(),
onPressed: () {
print('click');
},
child: CircleAvatar(
backgroundImage: NetworkImage(user.photoURL.toString()),
),
)
],
),
Thank you for your help!
Is this what you want to achieve?
You could achieve this with a SingleChildScrollView.
I combined the SingleChildScrollView with a ConstrainedBox to allow the spread of the icons if the screen is larger than needed.
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth),
child: Row(...),
),
);
},
),
Full source code for easy copy-paste
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(width: 800, child: MyBar()),
const SizedBox(height: 16.0),
Container(width: 400, child: MyBar()),
const SizedBox(height: 16.0),
Container(width: 200, child: MyBar()),
const SizedBox(height: 16.0),
Container(
width: 100,
child: SingleChildScrollView(child: MyBar()),
),
],
),
),
);
}
}
class MyBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 8.0),
color: Colors.amber.shade300,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: constraints.maxWidth,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
icon: Icon(
Icons.chat_bubble_outline_rounded,
color: Colors.white,
),
onPressed: null),
IconButton(
icon: Icon(
Icons.settings_outlined,
color: Colors.white,
),
onPressed: null),
IconButton(
icon: Icon(
FontAwesomeIcons.wallet,
color: Colors.white,
),
onPressed: null),
IconButton(
icon: Icon(
Icons.notifications_outlined,
color: Colors.white,
),
onPressed: null),
MaterialButton(
shape: CircleBorder(),
onPressed: () {
print('click');
},
child: CircleAvatar(
child: Text('X'),
),
)
],
),
),
);
},
),
);
}
}
I don't think Flexible will solve the problem here. Instead of this I have two options for you.
1) Replace Row with ListView (also provide scrollDirection as horizontal).
This will prevent the overflow and the list will be scrollable when the screen will be resized to a smaller width. I will prefer doing this.
2) Scale all the buttons with respect to the screen width.
This will provide you a responsive layout as the buttons will resize as the screen width changes. But you need to make sure that they don't get so small that user will find it difficult to press them.
Related
How can I create a row w/2 buttons that, together, take up the space of their parent (the row)? I want to place this row immediately above the bottom navigation bar at times and then at other times I want it to take the place of the bottom navigation bar. Can I do it just using Row? I've tried persistentFooterButtons with a child of Row and children of FractionallySizedBox but it results in an overflow error and the buttons don't take up the height of of the persistentFooterButtons. There's got to be a better, more straight-forward, approach. Just learning Flutter.
This is a rough example of what I want placed at the bottom of the screen (buttons should be of equal height), where the bottom nav bar would be... and/or immediately above.
[![horizontallyAlignedButtonsWithWidthOfRow][1]][1]
Some of the many (unsuccessful attempts) below:
Wrap Buttons in Expand Attempt:
return Row(
children: <Widget>[
Expanded(
child: ElevatedButton(
onPressed: () {},
style: TextButton.styleFrom(backgroundColor: Colors.blue),
child: Text(
"Some Text #1",
style: TextStyle(color: Colors.white),
))),
Expanded(
child: ElevatedButton(
onPressed: () {},
style: TextButton.styleFrom(backgroundColor: Colors.green),
child: Text("Some Text #2"),
),
),
],
Wrap Buttons in Expand Attempt Result:
[![Result][2]][2]
As you can see, there is white space that shouldn't be there and the buttons are evenly spaced. I have also tried using ColoredBoxes instead of buttons as I read that buttons have margins that cannot be changed?
UPDATE
Finally figured it out, with the help of #lava solutions. Just made a few tweaks to it to remove white space, etc.
return Container(
padding: EdgeInsets.all(0.0),
height: 50,
child: Row(
children: [
Container(
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blue, // background
onPrimary: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.zero),
),
onPressed: () {},
child: Text("Button1"),
),
),
Expanded(
child: Container(
height: 100,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.green, // background
onPrimary: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.zero)),
onPressed: () {},
child: Text("Button2"),
),
),
)
],
),
);
}
}```
[1]: https://i.stack.imgur.com/CydR4.png
[2]: https://i.stack.imgur.com/1JifM.png
Container(
padding: EdgeInsets.all(8.0),
height: 100,
child: Row(
children: [
Expanded(
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text("Button1"),
),
)),
Container(
padding: EdgeInsets.only(left: 8.0),
height: 100,
child:
ElevatedButton(onPressed: () {}, child: Text("Button2")))
],
),
)
Container(
padding: EdgeInsets.all(8.0),
height: 100,
child: Row(
children: [
Expanded(
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text("Button1"),
),
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 8.0),
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text("Button2"),
),
),
)
],
),
)
FullCode
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(home: Mainwidget()));
}
class Mainwidget extends StatefulWidget {
const Mainwidget({Key? key}) : super(key: key);
#override
_MainwidgetState createState() => _MainwidgetState();
}
class _MainwidgetState extends State<Mainwidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.all(8.0),
height: 100,
child: Row(
children: [
Expanded(
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text("Button1"),
),
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 8.0),
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text("Button2"),
),
),
)
],
),
),
),
);
}
List<Widget> children() {
return [
Expanded(
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {},
child: Text("Button1"),
),
)),
Container(
padding: EdgeInsets.only(left: 8.0),
height: 100,
child:
ElevatedButton(onPressed: () {}, child: Text("Button2")))
];
}
}
You can either add persistentFooterButtons to the Scaffold like
Scaffold(
persistentFooterButtons: [
TextButton(),
TextButton()
]
)
Or in the view:
Column(
children: [
Expanded(child: yourContent), // To use max height space
Row(
children: [
Expanded(child: TextButton()),
Expanded(child: TextButton()),,
]
),
]
),
To give a different width you can add flex to the Expanded or use Flexible and add flex
Wrap both your buttons with Expanded so that they take same amount of width and fill the row.
In the project I'm currently working on, I have a Scaffold that contains a SinlgeChildScrollView. Within this SingleChildScrollView the actual content is being displayed, allowing for the possibility of scrolling if the content leaves the screen.
While this makes sense for ~90% of my screens, however I have one screen in which I display 2 ExpansionTiles. Both of these could possibly contain many entries, making them very big when expanded.
The problem right now is, that I'd like the ExpansionTile to stop expanding at latest when it reaches the bottom of the screen and make the content within the ExpansionTile (i.e. the ListTiles) scrollable.
Currently the screen looks like this when there are too many entries:
As you can clearly see, the ExpansionTile leaves the screen, forcing the user to scroll the actual screen, which would lead to the headers of both ExpansionTiles disappearing out of the screen given there are enought entries in the list. Even removing the SingleChildScrollView from the Scaffold doesn't solve the problem but just leads to a RenderOverflow.
The code used for generating the Scaffold and its contents is the following:
class MembershipScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MembershipScreenState();
}
class _MembershipScreenState extends State<MembershipScreen> {
String _fontFamily = 'OpenSans';
Widget _buildMyClubs() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Color(0xFFD2D2D2),
width: 2
),
borderRadius: BorderRadius.circular(25)
),
child: Theme(
data: ThemeData().copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
title: Text("My Clubs"),
trailing: Icon(Icons.add),
children: getSearchResults(),
),
)
);
}
Widget _buildAllClubs() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Color(0xFFD2D2D2),
width: 2
),
borderRadius: BorderRadius.circular(25)
),
child: Theme(
data: ThemeData().copyWith(dividerColor: Colors.transparent),
child: SingleChildScrollView(
child: ExpansionTile(
title: Text("All Clubs"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add)
],
),
children: getSearchResults(),
),
)
)
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Stack(
children: <Widget>[
Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
gradient: kGradient //just some gradient
),
),
Center(
child: Container(
height: double.infinity,
constraints: BoxConstraints(maxWidth: 500),
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 40.0, vertical: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Clubs',
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
fontFamily: _fontFamily,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
_buildMyClubs(),
SizedBox(height: 20,),
_buildAllClubs()
],
),
),
),
),
],
),
)
),
);
}
List<Widget> getSearchResults() {
return [
ListTile(
title: Text("Test1"),
onTap: () => print("Test1"),
),
ListTile(
title: Text("Test2"),
onTap: () => print("Test2"),
), //etc..
];
}
}
I hope I didn't break the code by removing irrelevant parts of it in order to reduce size before posting it here. Hopefully, there is someone who knows how to achieve what I intend to do here and who can help me with the solution for this.
EDIT
As it might not be easy to understand what I try to achieve, I tried to come up with a visualization for the desired behaviour:
Thereby, the items that are surrounded with dashed lines are contained with the list, however cannot be displayed because they would exceed the viewport's boundaries. Hence the ExpansionTile that is containing the item needs to provide a scroll bar for the user to scroll down WITHIN the list. Thereby, both ExpansionTiles are visible at all times.
Try below code hope its help to you. Add your ExpansionTile() Widget inside Column() and Column() wrap in SingleChildScrollView()
Refer SingleChildScrollView here
Refer Column here
You can refer my answer here also for ExpansionPanel
Refer Lists here
Refer ListView.builder() here
your List:
List<Widget> getSearchResults = [
ListTile(
title: Text("Test1"),
onTap: () => print("Test1"),
),
ListTile(
title: Text("Test2"),
onTap: () => print("Test2"),
), //etc..
];
Your Widget using ListView.builder():
SingleChildScrollView(
padding: EdgeInsets.all(20),
child: Column(
children: [
Card(
child: ExpansionTile(
title: Text(
"My Clubs",
),
trailing: Icon(
Icons.add,
),
children: [
ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Column(
children: getSearchResults,
);
},
itemCount: getSearchResults.length, // try 50 length just testing
),
],
),
),
],
),
),
Your Simple Widget :
SingleChildScrollView(
padding: EdgeInsets.all(20),
child: Column(
children: [
Card(
child: ExpansionTile(
title: Text(
"My Clubs",
),
trailing: Icon(
Icons.add,
),
children:getSearchResults
),
),
],
),
),
Your result screen ->
I am trying to implement a horizontal Listview which shows my images pulled from the Flutter image_picker plugin.
its work fine if I do not use a Listview and only display a single image. however I am trying to use multiple images and as soon as I place within the Listview the widgets just shows up as black. my code for the Listview is as follows:
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
Navigator.pushReplacementNamed(context, 'inventory');
return false;
},
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.green[700],
title: Text(
'MyApp',
textAlign: TextAlign.center,
),
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushReplacementNamed(
context,
'inventory');
},
),
actions: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10.0),
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white, //remove this when you add image.
),
child: GestureDetector(
onTap: (){
Navigator.pushNamed(context,'profile');
},
child: Image(
image:NetworkImage("imageUrl goes here"),
width: 120,
height: 120,
fit: BoxFit.cover,
),
),
),
)
],
),
body: SafeArea(
child: Column(
children: [
pickedFile == null ?
GestureDetector(
onTap: () {
_showMyDialog();
setState(() {
});
},
child: Container(
width: 200,
height: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Click to add a Photo',textAlign:
TextAlign.center,style: TextStyle(fontSize: 24),),
SizedBox(height: 20,),
Icon(
Icons.add_circle_outline,
color: Colors.grey[700],
size: 30,
),
],
),
margin: EdgeInsets.all(20.0),
decoration: BoxDecoration(
border: Border.all(width: 2,color: Colors.grey),
shape: BoxShape.circle
),
),
)
:
GestureDetector(
onTap: () {
_showMyDialog();
setState(() {
});
},
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _imageFileList!.length,
itemBuilder: (BuildContext context, int index) {
return kIsWeb
? Image.network(_imageFileList![index].path)
: Image.file(File(_imageFileList!
[index].path));
}
)
],
),
),),
);
}
}
I am not showing the whole code as the page is too long however the rest of the page works fine. if i remove the Listview builder and instead test using just the below it works fine. what am i doing wrong?
child: Row(
children: [
kIsWeb
? Container(
child: Image.network(_imageFileList![index].path))
: Container(
child: Image.file(File(_imageFileList![index].path)));
}
),
],
),
Please help.
**Edited the above code with my full widget
Either use Row or use ListView, not both.
You can use Row with List.generate:
Row(
children: List.generate(_imageFileList!.length,
(i) => kIsWeb
? Container(child: Image.network(_imageFileList![index].path))
: Container(child: Image.file(File(_imageFileList![index].path))
),
Or ListView exactly how you have it without the Row. ListView is probably the widget you’re wanting to use. Row will overflow if the content is larger than the width while ListView will act as its own ScrollView in that situation.
I’d also double-check that you need that Expanded widget. That’s typically for use inside a Row (or similar widgets).
Managed to solve my issue. it was related to a height constraint which needed to be added. found the solution in this link
How to add a ListView to a Column in Flutter?
I'm working with flutter. I want to make a CupertinoAlertDialog(iOS style is required). My problem is UI designers require the background color of the alert dialog should be #F0F0F0. But I can only adjust its theme into dark or light(e.g. following picture). The code I completed is placed below.
showCupertinoDialog(
context: context,
builder: (BuildContext context){
return Theme(
data: ThemeData.dark(),
child: CupertinoAlertDialog(
title: Text('Title'),
content: Text('Some message here'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Cancle'),
),
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
),
);
}
);
Is that possible? Thanks for any advice.
If I recall correctly, the background color for CupertinoAlertDialog is hardcoded. However, you can create a custom dialog that can change the background color of it as well as the functions of the buttons.
You need to create a type Dialog for the showDialog function instead of showCupertinoDialog:
Dialog customDialog = Dialog(
backgroundColor: Color(0xfff0f0f0), // your color
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), // change 40 to your desired radius
child: CustomAlertDialog(),
);
I also created a stateless widget called CustomAlertDialog, but if you don't want to, you can replace the CustomAlertDialog() with its content.
class CustomAlertDialog extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 150,
child: Column(
children: [
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.grey, width: 1),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: Center(
child: Text(
"Title",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20),
),
),
),
Container(
child: Center(
child: Text("Some message here"),
),
),
],
),
),
),
Expanded(
flex: 1,
child: Row(
children: [
Expanded(
flex: 1,
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
border: Border(
right: BorderSide(color: Colors.grey, width: 1),
),
),
child: Center(
child: Text("Cancel"),
),
),
onTap: () {
Navigator.of(context).pop(); // replace with your own functions
},
),
),
Expanded(
flex: 1,
child: GestureDetector(
child: Container(
child: Center(
child: Text("OK"),
),
),
onTap: () {
Navigator.of(context).pop(); // replace with your own functions
},
),
),
],
),
),
],
),
);
}
}
Lastly, replace your whole showCupertinoDialog with this showDialog function:
showDialog(
barrierDismissible: true, // set false if you dont want the dialog to be dismissed when user taps anywhere [![enter image description here][1]][1]outside of the alert
context: context,
builder: (context) {
return customDialog;
},
);
Result: https://i.stack.imgur.com/cV13A.png
I'm new to flutter. I'm trying to render a page whose body contains Listview with multiple widgets.
_buildOrderDetails widget in the listview is widget that is build with listview.builder() , remaining are normal widgets.
The problem is page is not being scrolled .
When the body Listview is changed to column and _buildOrderDetails is given as child to the Expanded, the listview is limited to some extent of the page height and being scrolled. But when input is focused the page is overflowed
Widget build(BuildContext context){
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return Scaffold(
appBar: AppBar(
title: Text('Order Details'),
actions: <Widget>[
IconButton(
onPressed: () {
model.addNewOrder();
},
icon: Icon(Icons.add),
),
BadgeIconButton(
itemCount: model.ordersCount,
badgeColor: Color.fromRGBO(37, 134, 16, 1.0),
badgeTextColor: Colors.white,
icon: Icon(Icons.shopping_cart, size: 30.0,),
onPressed: () {}
),
]
),
body: ListView(
children: [
Column(
children: [
_buildItemsTitle(),
Expanded(child: _buildOrderDetails(context, model)),
]
),
Card(
child: Column(
children:[
TextField(
decoration: InputDecoration(
labelText: 'Offer Code'
),
),
RaisedButton(
onPressed: (){},
child: Text('Apply'),
)
]
),
),
Card(child: _orderAmount(context, model),),
RaisedButton(
color: Theme.of(context).accentColor,
onPressed: (){},
child: Text('Checkout',
style: TextStyle(
fontSize: 20.0,
color: Colors.white
)
),
)
]
),);});}}
Maybe it can help someone in the future, but the trick seems to be: use ListView + LimitedBox(maxHeight) + Column ...
ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
FocusTraversalGroup(
child: Form(
key: _formKey,
child: LimitedBox(
maxHeight: MediaQuery.of(context).size.height,
child: Column(
// mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(flex: 30), // Or Expanded
// More Widgets...
Spacer(flex: 70), // Or Expanded
// ....
Try not to use expanded on growing items. If you want to cover a percentage/fractional height wrap the height with a fixed height or the full height with a container that includes box contstains, then proceed to have expanded or fixed height children. also helpful is the FracionalBox
In the example you showed there is no need for expanded, the children inside will give a content height and the SingleChildScrollView will automaticly handle scrolling based on children.
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return Scaffold(
appBar: AppBar(title: Text('Order Details'), actions: <Widget>[
IconButton(
onPressed: () {
model.addNewOrder();
},
icon: Icon(Icons.add),
),
BadgeIconButton(
itemCount: model.ordersCount,
badgeColor: Color.fromRGBO(37, 134, 16, 1.0),
badgeTextColor: Colors.white,
icon: Icon(
Icons.shopping_cart,
size: 30.0,
),
onPressed: () {}),
]),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Column(children: [
_buildItemsTitle(),
Container(child: _buildOrderDetails(context, model)),
]),
Card(
child: Column(children: [
TextField(
decoration: InputDecoration(labelText: 'Offer Code'),
),
RaisedButton(
onPressed: () {},
child: Text('Apply'),
)
]),
),
Card(
child: _orderAmount(context, model),
),
RaisedButton(
color: Theme.of(context).accentColor,
onPressed: () {},
child: Text('Checkout',
style: TextStyle(fontSize: 20.0, color: Colors.white)),
),
],
),
),
);
});
}