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?
In my design, there is a close button in the BottomSheet. I have tried Stack. But it didn't work. Does anyone know how to achieve the result? Thanks :)
modalBottomSheet(context) {
return showModalBottomSheet(
context: context,
builder: (context) {
return Stack(
children: [
Container(
child: Text('Sample Text'),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
],
);
}
);
}
So I've been trying around for a bit, and this seems to work as you explained.
modalBottomSheet(context) {
return showModalBottomSheet(
context: context,
builder: (context) {
// using a scaffold helps to more easily position the FAB
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.maxFinite,
),
Padding(
padding: EdgeInsets.all(30.0),
child: Text("Text in the sheet"),
),
],
),
// translate the FAB up by 30
floatingActionButton: Container(
transform: Matrix4.translationValues(0.0, -30, 0.0), // translate up by 30
child: FloatingActionButton(
onPressed: () {
// do stuff
print('doing stuff');
},
child: Icon(Icons.add),
),
),
// dock it to the center top (from which it is translated)
floatingActionButtonLocation: FloatingActionButtonLocation.centerTop,
);
});
}
The meat of the solution here is to transform the Container which contains the FAB. I got this idea from this older SO answer to a somewhat related question.
The result looks like this:
You'll probably have to make some more edits to achieve the exact result you desire, but I hope this sets you on the right path.
Edit
When, in the above solution, you want to press the FAB, and you tap the top half, the onPressed handler fires, but the modal also closes. You should probably use a WillPopScope that only pops when the actual button is pressed (and not the area around/above it). If you think it's fine pressing anywhere above it as well, you can just leave it as-is.
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (BuildContext context) {
return Stack(clipBehavior: Clip.none, children: [
Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
height: 220.h,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
color: Colors.green,
onPressed: () {},
child: const Center(child: Text('Remove')))
],
),
),
),
),
Positioned(
top: -30.h,
right: 12.w,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.close),
)),
]);
},
)
I have a listview with every Item wrap inside a GestureDetector to be clickable, but is there a way to have a portion of the Item view to be not clickable? Thanks
GestureDetector(
onTap: () {
...
},
behavior: HitTestBehavior.opaque,
child: Column(
children: <Widget>[
child: SizedBox( height: 40,
child: Container(
color: Colors.green,
child: Text("Hello world"), // want to make the text area not clikable
),
),
someOtherWidgets...
],
),
Yes then you have to take particular item click and it should be blank in that case, Example InkWell click and child Text
InkWell(
onTap: () {
...
},
behavior: HitTestBehavior.opaque,
child: Column(
children: <Widget>[
child: SizedBox( height: 40,
child: Container(
color: Colors.green,
child: GestureDetector(
onTap: (){}
,
child:Text("PBX",style: TextStyle(fontSize: 15.0),)), // you can use like this text will be blank click
),
),
someOtherWidgets...
],
),