Flutter : Horizontal Listview with viewportFraction - flutter

I want to implement this horizontal ListView effect.
Planning to create a horizontal ListView like this. Example the horizontal listView will show 2 items and 1 item only show up 20%.
When scrolling it will become like this. Example front and end show up 20% and center show 2 items.
Edited : Code I'm using right now :
viewportFraction = 1 / 2.3;
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final double itemWidth =
(constraints.maxWidth - padding.horizontal) * this.viewportFraction;
final double itemHeight = (itemWidth * this.aspectRatio);
return new Container(
height: itemHeight,
child: new ListView.custom(
scrollDirection: Axis.horizontal,
controller: new PageController(
initialPage: this.initialPage,
viewportFraction: this.viewportFraction,
),
physics: const PageScrollPhysics(),
padding: this.padding,
itemExtent: itemWidth,
childrenDelegate: this.childrenDelegate,
),
);
});

The easiest way of doing this is probably with an horizontal ListView builder
#override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection:Axis.horizontal,
itemCount:10,
itemBuilder:(context,index){
return Padding(
padding:EdgeInsets.symmetric(horizontal:MediaQuery.of(context).size.width*0.05),
child:Container(
color: Colors.blue,
child:Text(index.toString()),
height:20,
width:MediaQuery.of(context).size.width*0.25,
)
);
}
);
}
The MediaQuery.of(context).size allows you to get information about your sreen geometry
I'll let you do some math to find the right fraction of the screen to use to get the final result that you want

Related

My `GridView` breaks or is not scrollable

I am pulling my hair trying to make my grid view displays within the user's profile page, which is basically a Column Widget.
Either I get error regarding the "unbounceness" of the widget or other error like RenderFlex children have non-zero flex but incoming height constraints are unbounded..
If I set the shrinkWrap to true the grid is there, but unscrollable.
I tried many solution such as adding a Flexible or Expanded parent with a mainAxisSize to min for the Column.
My grid view code is a as follows:
/// A grid view of the user's ads
class UserAdWidget extends StatelessWidget {
final String userId;
const UserAdWidget({super.key, required this.userId});
#override
Widget build(BuildContext context) {
final query = ClassifiedAd.getQueryFromUserId(userId);
return FirestoreQueryBuilder<ClassifiedAd>(
query: query,
builder: (context, snapshot, _) => Flexible(
child: GridView.builder(
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
crossAxisSpacing: 2,
mainAxisSpacing: 20),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
// if we reached the end of the current items, get more
if (snapshot.hasMore && index + 1 == snapshot.docs.length) {
snapshot.fetchMore();
}
final ad = snapshot.docs[index].data();
return ad.galleryWidget(context,
withAvatar: false, onTap: () {});
})));
}
}
The component code where the grid is rendered within a Column is as follows:
#override
Widget build(BuildContext context) {
return Consumer<StateModel>(builder: (context, appState, child) {
final selfProfile = appState.loggedUser?.id == widget.user.id;
return Scaffold(
appBar: AppBar(
title:
Text(selfProfile ? "Votre profil" : widget.user.displayName)),
body: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// === User's profile widget
UserProfileWidget(user: widget.user),
const Divider(),
// == Grid of user's ads
const Text("The ads:"),
const Text(""),
UserAdWidget(userId: widget.user.id),
])));
});
}
The current code renders like so but does not scroll:

AnimatedList auto scrolling to last item and back

I've implemented an Animated list with SlideTransition like this
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: ListView(
children: [
// Other widgets
animatedList(),
],
),
),
);
}
Widget animatedList() {
return AnimatedList(
shrinkWrap: true,
key: _myKeyList,
initialItemCount: _myItemsList.length,
itemBuilder: (context, index, animation) {
return SlideTransition(
position: animation.drive(_offset),
child: _buildMyItemTile[index],
);
},
);
}
where _offset variable is a Tween animation. Each item of list is inserted and animated with a delay of 500 milliseconds.
Now, when all items are added to AnimatedList, i would like that AnimatedList content scroll automatically from first item to last (and back) continuously for show all its content.
How can i do?
Just do it
add a controller
final controll = ScrollController();
then add this controller in the AnimatedList
AnimatedList(
controller: controll,
...
)
Make a function to call the last position or the first
0 for first position and 1 for last position or true false, I don't know, use your imagination
void getLastItem(int 0){
var position = int == 0
? controll.position.minScrollExtent
: controll.position.maxScrollExtent;
controll.animateTo(
position,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInCubic,
)
}
I hope I helped you.

Flutter Listview builder show data in two rows depending upon `index` is odd or event

I am fairly new in Flutter, I am fetching data from api and displaying it in a listview. I have following demo code.
/*Other codes*/
return ListView.builder(
//Other Codes
physics: const AlwaysScrollableScrollPhysics(),
itemCount: item.length,
padding: new EdgeInsets.only(top: 5.0),
itemBuilder: (context, index) {
});
My requirement is to show data in Two Rows, such that if the item in index is even show it in first row and if its odd, show it in another row? More like in the Image below.
Update: There could be many items and can be scrolled horizontally to reveal more items.
Can we achieve this without manipulating the data received from API? From API the data comes in date-wise sorted in descending order.
Thanks
GridView.builder is probably what you are looking for.
Just give it scrollDirection: Axis.horizontal to make it horizontal.
Then give it gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2) to make sure you have only 2 rows.
Then use SizedBox to restrict the height of the GridView to 90 + 90.
Finally, use the childAspectRatio property of the SliverGridDelegateWithFixedCrossAxisCount to specify that you want your aspect ratio to be 90 / 256.
Color randomColor() => Color.fromARGB(255, Random().nextInt(255), Random().nextInt(100), Random().nextInt(200));
class MyApp extends StatelessWidget {
List<String> array = ["0", "1", "2", "3"];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Example')),
body: SizedBox(
height: 90 + 90,
child: GridView.builder(
itemCount: 4,
scrollDirection: Axis.horizontal,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 90 / 256,
crossAxisCount: 2,
),
itemBuilder: (context, index) {
return Container(
color: randomColor(), child: Text(array[index])
);
}
),
),
),
);
}
}

Expand Widget to fill remaining space in ListView

As in the image shown above, I want widget 2 to always be at least the height of the remaining space available.
But widget 2 might contain so many ListTiles so that they can not be displayed without scrolling. But scrolling should affect widget 1 and widget 2. What is the best way to implement something like this?
Wrap Widget 2 in an Expanded Widget.
To scroll both Widget 1 and Widget 2, wrap both of them in a SingleChildScrollView Widget.
If you can distinguish between the case with a few and many elements (for example during loading), you can use CustomScrollView with SliverFillRemaining for this:
var _isLoading = true;
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
_buildWidget1(),
_buildWidget2(),
],
);
}
Widget _buildWidget1() {
return SliverToBoxAdapter(
child: Container(height: 400, color: Colors.blue),
);
}
Widget _buildWidget2() {
if(_isLoading) {
return SliverFillRemaining(
hasScrollBody: false,
child: Center(child: const CircularProgressIndicator()),
);
} else {
return SliverFixedExtentList(
delegate: SliverChildBuilderDelegate(
_buildItem,
childCount: childCount,
),
itemExtent: 56,
);
}
}
A simple way to do that would be to place your widgets in Column and wrap it with a single child scroll view. For the ListView use shrinkWrap as true and physics you can set to NeverScrollableScrollPhysics
Here is an example
SingleChildScrollView(
child: Column(
children: [
Container(
height: MediaQuery.of(context).size.height / 2,
color: Colors.red,
),
ListView.builder(
shrinkWrap:true,
physics:NeverScrollableScrollPhysics(),
itemCount: 100,
itemBuilder: (context, index) => Text("$index"),
),
],
),
);
Hope this helps!
var widgetHeight = MediaQuery.of(context).size.height - fixedSize;
return SingleChildScrollView(
child: Container(
height: widgetHeight,
child: Widget2
)
)

How to create pageview overlapping effect?

I need to create an overlapping pageview collection, but because of draw/layout order of items the second page always shows up in front of first page. There a way to create a collection list that the first items overlapping the others?
PAGE BUILDER ->
Widget buildList(PreloadPageController pageController, List data,
double currentPosition) {
return AspectRatio(
aspectRatio: 12.0 / 15.0,
child: PreloadPageView.builder(
itemCount: data.length,
controller: pageController,
preloadPagesCount: 2,
itemBuilder: (context, index) {
return CardWidget(
page: index,
currentPage: currentPosition,
);
},
),
);
}
CARD WIDGET ->
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, contraints) {
final double padding = 20.0;
var delta = currentPage - page;
var start = padding * delta.abs() * 10;
var top = padding + padding * max(-delta, 0.0);
var bottom = padding + padding * max(-delta, 0.0);
//print(start);
return Transform.translate(
offset: Offset(-start, 0),
child: Container(
padding: EdgeInsets.only(top: top, bottom: bottom),
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Container(
color: _randomColor(page),
),
),
),
);
});
}
I was expecting create a collection effect so the second page would come from behind the first one, but actually second pages always appears overlapping the first.
I could use reverse in PageView.builder, but this collection needs to be a infinity list that loads more data when it reaches the end and with reverse the code will be alot trickier.
I'm achieving this:
But what I want is the blue card behind the red one.
So right now, to create your overlapping effect, you are offsetting the next-Page so that it overlaps the cur-Page, and as you mentioned, you discover that the next-Page visually over-laps instead of the desired under-lap.
Then, in additional to your offset, have you tried cropping off the overlapping portion of next-Page? This can simulate the effect of under-lapping.
Now, I tried replicating your sample, but I'm uncertain about your PreloadPageController (and maybe other details), so my sample might look glitchy. Additionally, I'm not wholly familiar with cropping widgets. But I bumped into this possible solution, all I did was wrap it with another ClipRect:
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final double padding = 20.0;
var delta = widget.currentPage - widget.myPage;
var start = padding * delta.abs() * 10;
var top = padding + padding * max(-delta, 0.0);
var bottom = padding + padding * max(-delta, 0.0);
return ClipRect(
child: Transform.translate(
offset: Offset(-start, 0),
child: Container(
padding: EdgeInsets.only(top: top, bottom: bottom),
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Container(
color: redOrBlue(widget.myPage),
),
),
),
),
);
});
}
This additional ClipRect basically clips off your offset portion. Feel free to explore and modify as needed!
There's an Overlay widget but it may cause more headaches, especially at scale. You can wrap your pages in a ClipRect, but only the widgets at the end of the list will need it. So in the builder function, set up a bool:
clipNeeded = (controller.page + 0.5) <= index;
Then in the new ClipRect widget:
clipper: clipNeeded ? CustomRect() : null,
Then you'll need to create that CustomRect class, extending CustomClipper<Rect>
The exact math required depends on the implementation, but in this left to right scroll example, it'll be something like
class CustomRect extends CustomClipper<Rect>{
#override
Rect getClip(Size size) {
double leftLine = /* some calculation */;
return Rect.fromLTRB(leftLine, 0.0, size.width, size.height);
}
#override
bool shouldReclip(CustomRect oldClipper) {
return true;
}
}