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

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

Related

Flutter : When I rotate a horizontal ListView by Transform.rotate, the left and right edges are cut off

When I use Transform.rotate to make a horizontal ListView diagonal as shown below, it becomes diagonal, but the left and right edges are cut off.
Is there a way to display without clipping the left and right edges, or is there a widget that can be used for that?
I came up with a way to use the Stack widget to overlay the left and right edges with strips of gradation and make them invisible.
I've actually tried it and it's fine, but I thought I'd ask if there is another way.
I think this is happening because the screen width is passed from the parent as a constraint, but is there any way to disable the constraint?
Thank you.
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Animated Icons',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.greenAccent,
body: Column(
children: [
Expanded(
child: Center(
child: SizedBox(
width: double.infinity,
height: 200.0,
child: Transform.rotate(
angle: -math.pi / 20,
child: Container(
color: Colors.white,
height: 200.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 6,
itemBuilder: (context, innerIndex) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
//padding: EdgeInsets.all(4.0),
height: 50.0,
width: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color:
Colors.red.withOpacity(1.0 - 0.1 * innerIndex),
),
child:Center(child:Text(innerIndex.toString()),),
),
),
),
),
),
),
),
),
Container(
height:100.0,
color: Colors.white,
child:Center(
child:Text('title'),
),
)
],
),
);
}
}
With the following code using OverflowBox, the left and right corners are no longer cut off.
Expanded(
child: Center(
child: OverflowBox(
maxWidth: MediaQuery.of(context).size.width*1.2,
child: Transform.rotate(
angle: -math.pi / 20,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.white,
),
height: 200.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 6,
itemBuilder: (context, innerIndex) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
//padding: EdgeInsets.all(4.0),
height: 50.0,
width: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color:
Colors.red.withOpacity(1.0 - 0.1 * innerIndex),
),
child:Center(child:Text(innerIndex.toString()),),
),
),
),
),
),
),
),
),

Prevent ExpansionTile from expanding beyond viewport/screen

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.

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

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'),
),
)),
),
);
}
),
),
)
],
),
),
);
}
}

Items in ListView get it's height

I'm trying to create a horizontal list view with some card.
I want the list view to have height X and the cards to have height Y, I don't know why but the cards are getting the height of the list view.
This is what I have:
class FinanceApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Container(
color: Color(0x180000),
child: Column(
children: <Widget>[
Header(),
SizedBox(
height: 20,
),
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32.0),
topRight: Radius.circular(32.0),
),
),
child: Column(
children: <Widget>[
Container(
height: 250,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
CustomCard(),
CustomCard(),
],
),
),
],
),
),
),
],
),
),
),
);
}
}
EDIT: The only thing that kinda works for me, Is to wrap the card container in another container, use padding to get the size I want, but it does not seem like a great solution.
Check this out:
import 'package:flutter/material.dart';
class StackOverFlow extends StatefulWidget {
#override
_StackOverFlowState createState() => _StackOverFlowState();
}
class _StackOverFlowState extends State<StackOverFlow> {
#override
Widget build(BuildContext context) {
return Scaffold(
body:
Center(
child: Container(
color: Colors.blue,
height: 200.0,
width: double.infinity,
child: ListView.builder(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.all(16.0),
itemCount: 100,
itemBuilder: _buildItem,
),
),
),
);
}
Widget _buildItem (BuildContext context, int index) {
return Center(
child: Card(
child: Text(index.toString()),
),
);
}
}
And for giving children same size consider wrapping the cards with a Container:
Widget _buildItem (BuildContext context, int index) {
return Center(
child: Container(
height: 100.0,
width: 100.0,
child: Card(
child: Center(child: Text(index.toString())),
),
),
);
}