Scroll within a Flexible Flutter - flutter

I want to be able to make a section of the page scrollable, which will be between two sections of fixed sizes, but I want this section to expand as much as possible.
The reason why I want this, if the device is very small, the scroll works, but if the device is large, the scroll is not necessary.
I am trying to place a SingleChildScrollView (), inside a Flexible (), so that it expands as much as possible.
I am trying this but an error occurs
The design is like this

There are several ways to do it, you can do it with a ListView.builder
class UiMenu extends StatelessWidget {
const UiMenu({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
),
),
Expanded(
flex: 8,
child: Container(
child: ListView.builder(
padding: EdgeInsets.all(0.0),
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.only(bottom: 2),
child: ListTile(
leading: Icon(
Icons.equalizer_rounded,
size: 45,
),
title: Text(
"Option ${index + 1}",
style: Theme.of(context).textTheme.headline6,
),
trailing: Icon(
Icons.chevron_right,
size: 45,
),
),
);
},
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.orange,
),
)
],
),
);
}
}

Related

ElevatedButton Position Changing method

I have an Elevated Button which is on of the bottom of the Page and I am a beginner sorry for this silly doubts but i can't figure out how to change the position of the button I dont know how to try positioned widget too. Kindly help me
I tried positioned widget but couldn't do well can anyone help me with this. here is my full code.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: PageView.builder(
itemBuilder: (context, index)=> const OnBoardContent(
image: 'assets/splash-1.png',
description: "All under one roof with different approach"),
),
),
SizedBox(
height: 30,
width: 200,
child: ElevatedButton(
onPressed: (){},
child: const Text("Tap to get started"),
),
),
],
)
),
);
}
}
class OnBoardContent extends StatelessWidget {
const OnBoardContent({
Key? key,
required this.image,
required this.description,
}) : super(key: key);
final String image, description;
#override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(
height: 160,
),
const Text("Naz-Kearn",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold
)),
const Text("A simplify learning App",
style: TextStyle(
fontWeight: FontWeight.normal
),
),
Image.asset(image),
const SizedBox(
height: 50,
),
Text(description,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.normal),
),
],
);
}
}
Output of the above code
You need your widgets in a stack if you want to use Positioned widget on them :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack( //wrapped the whole column with a stack so that all the other widgets doesn't get disturbed
children: [
Column(
children: [
Expanded(
child: PageView.builder(
itemBuilder: (context, index)=> const OnBoardContent(
image: 'assets/splash-1.png',
description: "All under one roof with different approach"),
),
),
],
),
Positioned(
top: MediaQuery.of(context).size.height*0.7, //change the 0.7 part to any number you like
child: SizedBox(
height: 30,
width: 200,
child: ElevatedButton(
onPressed: (){},
child: const Text("Tap to get started"),
),
),
),
],
)
),
);
}
}
class OnBoardContent extends StatelessWidget {
const OnBoardContent({
Key? key,
required this.image,
required this.description,
}) : super(key: key);
final String image, description;
#override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(
height: 160,
),
const Text("Naz-Kearn",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold
)),
const Text("A simplify learning App",
style: TextStyle(
fontWeight: FontWeight.normal
),
),
Image.asset(image),
const SizedBox(
height: 50,
),
Text(description,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.normal),
),
],
);
}
}
try this code, you can use alignment property of the Stack widget to center everything.
SafeArea(
child: Stack(
alignment: Alignment.center, //do this
children: [
You can wrap your button with Padding widget which helps you to add padding as you like
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: PageView.builder(
itemBuilder: (context, index)=> const OnBoardContent(
image: 'assets/splash-1.png',
description: "All under one roof with different approach"),
),
),
Padding(
padding: EdgeInsets.all(8),
child: SizedBox(
height: 30,
width: 200,
child: ElevatedButton(
onPressed: (){},
child: const Text("Tap to get started"),
),
),),
],
)
),
);
}
Firstly please mention what precisely the issue you are facing. If you have a problem with the get started button, what is the expected place for the get started button in the design?
Based on the code given, I'm hoping that the get started button should be at the bottom of the screen with some space below. You have already placed the button at the bottom, but you are not able to give space below.
There are some possible ways, you can use it with the get started button component.
Instead of this,
SizedBox(
height: 30,
width: 200,
child: ElevatedButton(
onPressed: (){},
child: const Text("Tap to get started"),
),
),
Option 1
Use container with margin
Container(
height: 30,
width: 200,
margin: EdgeInsets.only(
bottom: 50,
),
child: ElevatedButton(
onPressed: () {},
child: const Text("Tap to get started"),
),
),
Option 2
Wrap existing SizedBox with padding widget
Padding(
padding: EdgeInsets.only(bottom: 50.0),
child: Sizedbox(
height: 30,
width: 200,
child: ElevatedButton(
onPressed: () {},
child: const Text("Tap to get started"),
),
),
),
Even with some more ways to move the button wherever you need, you can try your own with the following widgets Expanded(), Spacer(), SizedBox(), Positioned() and etc.

Drawer with ListViewBuilder and Header in Flutter

I'm trying to make a drawer widget that uses a ListViewBuilder to populate itself based on a list injected into the ViewModel.
However, I'm having issues getting it to play ball.
I've wrapped the LVB in a SizedBox to provide it with vertical bounds (since it was throwing a bunch of errors, as suggeested by another answer, and that's stopped those, but now I'm getting an overflow.
The header also doesn't fill out the width anymore either.
class MainDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<MainDrawerViewModel>(builder: (context, model, child) {
return Drawer(
child: Column(
children: [
DrawerHeader(
decoration: const BoxDecoration(color: ThemeColors.primaryDark),
child: Text(S.current.drawerTitle, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 30)),
),
SizedBox(
height: double.maxFinite,
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: model.mainDrawerItems.length,
itemBuilder: (_, index) {
final drawerItem = model.mainDrawerItems[index];
return ListTile(
leading: drawerItem.icon,
title: Text(drawerItem.title, style: Theme.of(context).textTheme.headline6),
selected: model.currentScreen == drawerItem.screen,
selectedTileColor: ThemeColors.selectedDrawerItem,
onTap: () {
model.selectScreen(drawerItem.screen);
Navigator.pop(context);
},
);
}),
),
],
));
});
}
}
This feels like something that should be pretty easy... What am I missing here?
Use Expanded widget on listView instead of height: double.maxFinite,
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
double.maxFinite = 1.7976931348623157e+308; and it is equal to 1.7976931348623157 × 10^308 which is too big. and the overflow happens.
For header, you can wrap With SizedBox and provide width: double.maxFinite,. Also you can just use a container with decoration like
class MainDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
Container(
width: double.maxFinite,
height: 200, // based on your need
decoration: const BoxDecoration(color: Colors.amber),
padding: EdgeInsets.only(left: 16, top: 16),
child: Text(
"S.cu ",
style: TextStyle(
color: ui.Color.fromARGB(255, 203, 19, 19),
fontWeight: FontWeight.bold,
fontSize: 30),
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: 3,
itemBuilder: (_, index) {
return ListTile(
leading: Icon(Icons.abc_outlined),
title: Text("drawerItem.title",
style: Theme.of(context).textTheme.headline6),
selected: true,
onTap: () {},
);
}),
),
],
));
}
}

Flutter Bottom overflowed by xx pixel

I am trying to construct a page with multiple widgets Row, Column, Expanded, ListView, etc...
I am a bit confused. I want a page scrollable with my widgets ThemeList.
I have the error :
A RenderFlex overflowed by 28 pixels on the bottom.
class SettingsViewState extends State<SettingsView> {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: const NavDrawer(),
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.settingsTitle),
backgroundColor: Theme.of(context).primaryColor,
),
body: CustomScrollView(slivers: [
SliverFillRemaining(
child: Column(
children: const [
ThemeList(),
SizedBox(height: 8),
ThemeList(),
SizedBox(height: 8),
ThemeList(),
],
),
),
]),
);
}
}
class ThemeList extends StatelessWidget {
const ThemeList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).primaryColor, width: 2),
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 5, left: 20),
child: Text(
AppLocalizations.of(context)!.settingsThemeSubTitle,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 23,
),
),
)
],
),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: AppTheme.values.length,
itemBuilder: (context, index) {
final itemAppTheme = AppTheme.values[index];
var nameTheme = itemAppTheme.toString()
return Card(
color: appThemeData[itemAppTheme]?.primaryColor,
child: ListTile(
title: Text(
nameTheme,
style: appThemeData[itemAppTheme]?.textTheme.bodyText1,
),
onTap: () {
BlocProvider.of<ThemeBloc>(context).add(
ThemeChanged(theme: itemAppTheme),
);
Preferences.saveTheme(itemAppTheme);
},
),
);
},
)
],
),
),
),
);
}
}
Desired result :
Just wrap the ListView.builder() inside ThemeList with an Expanded and the problem would vanish.
If you want to have all the items inside each ThemeList displayed with a scroll for the whole screen then the easiest why is to do the following:
Change the CustomScrollView in the body of the Scaffold to be SingleChildScrollView with the Column as its child.
Remove the Expanded at the start of ThemeList.
Remove the ListView.builder() inside the ThemeList and replace it with any looping logic to directly render the cards, for example:
...AppTheme.values.map((itemAppTheme) {
var nameTheme = itemAppTheme.toString();
return Card(
color: appThemeData[itemAppTheme]?.primaryColor,
child: ListTile(
title: Text(
nameTheme,
style: appThemeData[itemAppTheme]?.textTheme.bodyText1,
),
onTap: () {
BlocProvider.of<ThemeBloc>(context).add(
ThemeChanged(theme: itemAppTheme),
);
Preferences.saveTheme(itemAppTheme);
},
),
);
}).toList()

How to make ExpansionTile scrollable when end of screen is reached?

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 ->

Getting 'Horizontal viewport was given unbounded height.' with TabBarView in flutter

I'm trying to make a profile page, where the users info is at the top. And then have a tab view below that for different views.
This is the code I'm using at the moment, when I take the TabBarView out it doesn't through an error, and if I wrap the TabBarView in an Expanded the error RenderFlex children have non-zero flex but incoming height constraints are unbounded. comes up.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(''),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
CircleAvatar(
minRadius: 45.0,
backgroundImage: NetworkImage(
'https://www.ienglishstatus.com/wp-content/uploads/2018/04/Anonymous-Whatsapp-profile-picture.jpg'),
),
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Testing Name',
style: TextStyle(
fontSize: 22.0,
color: Colors.grey.shade800,
),
),
Text(
'#testing_username',
style: TextStyle(
fontSize: 13.0,
color: Colors.grey.shade800,
),
),
],
),
),
],
),
),
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
TabBar(
tabs: <Widget>[
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
],
),
TabBarView(
children: <Widget>[
Container(
color: Colors.grey,
),
Container(
color: Colors.green,
),
Container(
color: Colors.purple,
),
],
),
],
),
)
],
),
);
}
I did try a variation of this but couldn't get it to work.
The error description is clear, the TabBarView doesn't have a bounded height. the parent widget also doesn't have a bounded height. So, the Expanded widget will not solve this issue.
EDIT: below solutions are for above question(with columns).In general cases, use a ListView with shrinkWrap: true.(Or any other widgets with shrinkWrap) As #Konstantin Kozirev mentioned correctly, the shrinkWrap causes some performance issues. look for a better updated solution.
There are some options:
1st Solution:
Wrap the parent widget(Column) with a limited height widget like SizedBox or AspectRatio. Then use the Expanded widget like this:
Expanded(
child: TabBarView(...),
)
2nd Solution:
Use a bounded widget like SizedBox or AspectRatio on the TabBarView itself:
SizedBox(
height: 300.0,
child: TabBarView(...),
)
Note Your can also calcuate the height dynamicly if the height is not static.
I solved it by adding TabBar inside Container and TabBarView inside Expanded:
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
Container(child: TabBar(..)),
Expanded(child: TabBarView(..)),
],
),
);
try to use IndexedStack instead of TabBarView
i tried Expanded, shrinkWrap = true , ...
but no one work's fine
just try example.
Example:
class Product extends StatefulWidget {
#override
_ProductState createState() => _ProductState();
}
class _ProductState extends State<Product> with SingleTickerProviderStateMixin {
TabController tabController;
int selectedIndex = 0;
#override
void initState() {
super.initState();
tabController = TabController(length: 5, vsync: this, initialIndex: 0);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
initialIndex: 0,
child: Scaffold(
body: ListView(
shrinkWrap: true,
children: [
TabBar(
tabs: <Widget>[
Tab(
text: 'one',
),
Tab(
text: 'two',
),
Tab(
text: 'three',
),
],
controller: tabController,
onTap: (index) {
setState(() {
selectedIndex = index;
tabController.animateTo(index);
});
},
),
IndexedStack(
children: <Widget>[
Visibility(
child: Text('test1'),
maintainState: true,
visible: selectedIndex == 0,
),
Visibility(
child: Text('test2'),
maintainState: true,
visible: selectedIndex == 1,
),
Visibility(
child: Text('test3'),
maintainState: true,
visible: selectedIndex == 2,
),
],
index: selectedIndex,
),
],
),
),
);
}
}
special thank's to #arbalest
based on #Yamin answer I used SizeBox Like below to get full page
SizedBox.expand(
child: TabBarView(),
)
or any other size :
SizedBox(
height: height:MediaQuery.of(context).size.height // or every other size ,
child: TabBarView(),
)
The error message in console mentions this: "Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of vertical space in which to expand".
Clearly, the horizontal viewport here is referring to the TabBarView widget which is not given a height constraint.
So wrap both the TabBar and TabBarView widgets in Expanded widgets and give appropriate flex values to them to let them share their parent's height.
Concretely,
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
Expanded(
flex: 1,
child: TabBar(
tabs: <Widget>[
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
],
),
),
Expanded(
flex: 9,
child: TabBarView(
children: <Widget>[
Container(
color: Colors.grey,
),
Container(
color: Colors.green,
),
Container(
color: Colors.purple,
),
],
),
)
],
),
)