Prevent ExpansionTile from expanding beyond viewport/screen - flutter

I'm trying to build a screen where there are two lists containing items fetched from an API. I want to use an ExpansionTile for each list to present the data to the user so that the user can decide which data he wants to access.
As the data comes from an API it might be that the data present in a list is more than what fits on the screen, thus I'd like the ExpansionTile to be scrollable inside (I don't want the page to be scrollable but the ExpansionTile Widget!!!)
Currently if I expand the ExpansionTile this is the problem I'm facing if there's too much data to display within the ExpansionTile:
However, I'd like the UI to look something like this:
How can I limit the ExpansionTiles to the boundaries of the screen and force them to be scrollable if they overflow? Currently I use a ListView.builder and also tried nesting it inside a SingleChildScrollView but that didn't help.
This is how my code for this screen looks right now (which is only the inner part wrapped between the AppBar and BottomNavigationBar because they belong to the screen providing the PageView):
class MembershipScreen extends StatefulWidget {
const MembershipScreen({Key? key}) : super(key: key);
#override
_MembershipScreenState createState() => _MembershipScreenState();
}
class _MembershipScreenState extends State<MembershipScreen> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Center(
child: Container(
constraints: BoxConstraints(maxWidth: 500),
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Clubs',
style: kHeadingStyle,
),
SizedBox(
height: 20,
),
_buildMyClubs(),
SizedBox(
height: 20,
),
_buildAllClubs(),
],
),
),
),
);
}
Widget _buildMyClubs() {
return Container(
decoration: BoxDecoration(
color: kPrimarySwatch,
border: Border.all(
color: kSecondarySwatch,
width: 2,
),
borderRadius: BorderRadius.circular(25)
),
child: Theme(
data: ThemeData().copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
initiallyExpanded: false,
title: Text("My Clubs", style: kLabelStyle,),
children: [
SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: 4,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text("Test", style: kHintTextStyle,),
);
},
),
)
],
),
),
);
}
Widget _buildAllClubs() {
return Container(
decoration: BoxDecoration(
color: kPrimarySwatch,
border: Border.all(
color: kSecondarySwatch,
width: 2,
),
borderRadius: BorderRadius.circular(25)
),
child: Theme(
data: ThemeData().copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
initiallyExpanded: false,
title: Text("All Clubs", style: kLabelStyle,),
children: [
SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: 8,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text("Test", style: kHintTextStyle,),
);
},
),
)
],
),
),
);
}
}
I'd really appreciate any help as I don't have any idea what I could try to solve this issue now. If anything is still unclear please let me know and I will try to provide the missing information asap.

Related

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 gridview item at bottom

I have a gridView(3 items in a row) with dynamic number of items. I want gridView item should be at bottom means if there are 3 items then one row at bottom of screen, if there are 6 items then two row at bottom. So a dynamic padding at top depending on item count and screen size. My code structure is like below
class className extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
ImageFiltered(
//code here
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.black38,
),
GridView.builder(
primary: false,
reverse: false,
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 30,
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
//code
},
child: CustomWidget(
title: toolkitStore.getLabel(toolKit),
icon: ImageIcon(
AssetImage(
'assets/images/abcd.png'),
size: 48,
color: kWhiteColor,
),
),
);
},
itemCount: getCount().length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
),
],
),
),
);
}
}
Now I have this
but I want this
Thanks in advance.
**Try this one: **
import 'package:flutter/material.dart';
class ClassName extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(height: 100,width: 200,color: Colors.green,),//<----Your any widget
Align(
alignment: Alignment.bottomCenter,
child: GridView.builder(
primary: false,
reverse: true,
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 30,
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
//code
},
child: const Icon(Icons.person,size: 50,color: Colors.green,),
);
},
itemCount: 5,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
),
),
Container(height: 100,width: 100,color: Colors.red,),//<----Your any widget
],
),
),
);
}
}
Wrap your GridView.builder with Align widget provide alignment: Alignment.bottomCenter,
Align(
alignment: Alignment.bottomCenter,
child: GridView.builder(
primary: false,
reverse: false,
While using Stack use positional widget(Positioned, Align,...) to place children on UI.
Thank you, everyone.
I solved my problem by using Positioned widget and setting shrinkWrap: true, my code structure is
new Positioned(
left: 0.0,
bottom: 0.0,
width: MediaQuery.of(context).size.width,
child: GridView.builder(
shrinkWrap: true, //this is most important
))

it's possible to make listview in expand and fill parent without overflow?

it's possible to make listview in expand and fill the container without overflow the container, and shrink when listview is short of content without declare manual min or max height?
Code
Flexible(
fit: FlexFit.loose,
child: Container(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(kBoxRadius),
boxShadow: [kBoxShadow]),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DText(
"Aug 2021",
size: 12,
weight: FontWeight.bold,
),
SizedBox(height: 10),
Row(
children: dates,
),
SizedBox(height: 15),
Container(
height: 1,
color: Color(0xFFE7E7E7),
),
Container(
constraints:
BoxConstraints(minHeight: 0, maxHeight: 600),
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return DText("Asd");
},
separatorBuilder: (_, __) {
return SizedBox(height: 10);
},
itemCount: 20,
),
)
],
),
),
)
Expectation
when the content is big
because i set manually max height.
Yes, it is possible using shrinkWrap: true. The code will be something like below,
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
color: Colors.red,
child: ListView(
shrinkWrap: true,
children: <Widget>[
ListTile(title: Text('Apple')),
ListTile(title: Text('Orange')),
ListTile(title: Text('Banana')),
],
),
),
),
),
);
}
}

Flutter: CupertinoTabScaffold with CupertinoTabBar creating RenderFlex overflow issue at bottom for TabBar in pushed screens

I am an iOS developer, So I have idea how TabBarController works in iOS. Now I am working on Flutter (First APP).
I have an App which uses CupertinoApp-CupertinoTabScaffold-CupertinoTabBar to persist BottomNavigationBar in every nested screens.
My App's hierarchy
- CupertinoTabScaffold
-- CupertinoTabBar
--- Home
---- CupertinoPageScaffold (HomePage)
----- CupertinoPageScaffold (DetailPage pushed from home)
--- OtherTabs
To Push from HomePage to DetailPage, used below code:
Navigator.push(
context,
Platform.isIOS
? CupertinoPageRoute(
builder: (context) => DetailPage(),
)
: MaterialPageRoute(
builder: (context) => DetailPage(),
));
Now on detail screen I need Column for some view and GridView.
So when GridView have more items, it gives error:
A RenderFlex overflowed by 56 pixels on the bottom.
Which is space of TabBar.
So how to manage such type of pages in Flutter, having TabBar and scrollable Widgets in nested screens?
I have followed this link.
DetailPage Code:
class DetailPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
heroTag: 'detail 1',
backgroundColor: Colors.white,
transitionBetweenRoutes: false,
middle: Text('Tab 1 detail',),
),
child: Container(
child: Column(
children: <Widget>[
SizedBox(
height: 100.0,
child: Center(
child: Text('Some Menus'),
),
),
Container(
child: GridView.builder(
itemCount: 30,
scrollDirection: Axis.vertical,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
crossAxisCount: 2,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
height: 160.0
),
itemBuilder: (context, index) {
return Container(
child: Padding(
padding: EdgeInsets.all(6.0),
child: Container(
decoration: BoxDecoration(
color: Color(0xFF3C9CD9),
borderRadius: BorderRadius.all(Radius.circular(30.0)),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black54,
blurRadius: 2.0,
offset: Offset(4, 3))
]),
child: Padding(
padding: EdgeInsets.all(30.0),
child: Center(
child: Text('$index'),
),
)),
),
);
}
),
)
],
),
),
);
}
}
Output:
wrap the Grid with with Expanded widget
class DetailPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
heroTag: 'detail 1',
backgroundColor: Colors.white,
transitionBetweenRoutes: false,
middle: Text('Tab 1 detail',),
),
child: Container(
child: Column(
children: <Widget>[
SizedBox(
height: 100.0,
child: Center(
child: Text('Some Menus'),
),
),
Expanded(
child: Container(
child: GridView.builder(
itemCount: 30,
scrollDirection: Axis.vertical,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
crossAxisCount: 2,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
height: 160.0
),
itemBuilder: (context, index) {
return Container(
child: Padding(
padding: EdgeInsets.all(6.0),
child: Container(
decoration: BoxDecoration(
color: Color(0xFF3C9CD9),
borderRadius: BorderRadius.all(Radius.circular(30.0)),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black54,
blurRadius: 2.0,
offset: Offset(4, 3))
]),
child: Padding(
padding: EdgeInsets.all(30.0),
child: Center(
child: Text('$index'),
),
)),
),
);
}
),
),
)
],
),
),
);
}
}

How can I wrap the height of the ListView.builder with a Container?

I have a screen using ListView.builder.
I gave the background of the list a light color with Container and I gaved BorderRadius.circular it.
Like here. I want this background to wrap the list. Like this.
Curves must to be at the beginning and end of the list.
I've tried:
I gave a physics: NeverScrollableScrollPhysics() to the ListView.builder. I wrapped the first Container with a SingleChildScrollView. Although background continues below I must to give a height. But I don't know how long the list will be. So that method did not work.
What am I missing?
Full code below
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
canvasColor: Colors.green,
primaryColor: Colors.green,
accentColor: Colors.white,
),
home: ListScreen(),
);
}
}
class ListScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: Colors.white38,
),
child: ListView.builder(
itemBuilder: (context, index) {
return Container(
child: Column(
children: [
InkWell(
onTap: () {},
child: Container(
height: MediaQuery.of(context).size.height / 7,
alignment: Alignment.center,
child: ListTile(
leading: Padding(
padding: const EdgeInsets.all(0),
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Ash_Tree_-_geograph.org.uk_-_590710.jpg/330px-Ash_Tree_-_geograph.org.uk_-_590710.jpg",
fit: BoxFit.cover,
),
),
),
),
title: Text(
"${index + 1}. Tree",
),
),
),
),
Divider(
color: Colors.white,
height: 0,
),
],
),
);
},
itemCount: 12,
),
),
));
}
}
The way tried in the question was correct. It is only necessary to add shrinkWrap: true to the ListView.builder().
It will look like this:
SingleChildScrollView(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: Colors.white38,
),
child: ListView.builder(
itemBuilder: (context, index) {
return Container(//builder);
},
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: list.length,
),
),
),
The ListView widget is simply the stationary window on the screen that the child widgets scroll through. In order to achieve the desired effect, you need to use BorderRadius.only to round the correct corners of the first and last children of the ListView.
You are on the correct path here
I've tried: I gave a physics: NeverScrollableScrollPhysics() to the ListView.builder. I wrapped the first Container with a SingleChildScrollView. Although background continues below I must to give a height. But I don't know how long the list will be. So that method did not work.
in addition this this,
what you are missing here is using Expanded widget using expanding widget let the child expand as much as it want try this
Padding(
padding: const EdgeInsets.all(16),
child: Expanding(
child:Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: Colors.white38,
),
child: ListView.builder(
// your ListView.builder code here with neverScrollablePhysics
),
),
))
remember to wrap this all a singleChildScrollView() and maybe use a Column() too