'Wrap' widget - items alignment - flutter

Is there a way to align Wrap widget items in all lines except the last with spaceAround and the last one with start? Is there another way to do it? Number of rows and columns should not be fixed - it depends on device width and height.
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SingleChildScrollView(
child: new Wrap(
direction: Axis.horizontal,
spacing: 10.0,
alignment: WrapAlignment.spaceAround,
children: <Widget>[
//items
],
),
);
});
See screenshot how it actually looks like. I need the last row to have the same spacing as the others and start from the left.

AFAIK there's no way to achieve this with Wrap widget, but similar effect produce GridView with extent constructor:
GridView.extent(
children: List.generate(10, (index) {
return Center(
child: Icon(
Icons.star_border,
size: 100.0,
),
);
}),
maxCrossAxisExtent: 150.0,
)

If the size of the item is known, it can be achieved in this way:
const itemCount = 10;
const itemSize = 100.0;
LayoutBuilder(
builder: (context, c) {
const spacing = 0.0;
final columnCount =
((c.maxWidth + spacing) / (itemSize + spacing)).floor();
final rowCount = (itemCount / columnCount).ceil();
final maxLength = columnCount * rowCount;
return Wrap(
spacing: spacing,
children: List.generate(
math.max(itemCount, maxLength),
(index) => index < itemCount
? _buildItem(context, index)
: const SizedBox(width: itemSize, height: itemSize),
),
);
},
);

Related

Create Center Aligned Button Menu For different Device Flutter

I have a problem with this layout. I have a menu button with 6 item and I use a grid for it( I disable the scroll function). When I try it in my Poco F1, it place right in the center of the screen. But when I use a phone with bigger screen, the menu is slightly in the left side of the screen. Is there any way to make it in the center of the screen despite different screen size?
Here is the picture when using Pixel 3 XL
This is my code
Widget _gridView (BuildContext context) {
double cardWidth = MediaQuery.of(context).size.width / 2;
double cardHeight = MediaQuery.of(context).size.height * 31/100;
return GridView.count(
shrinkWrap: true,
childAspectRatio: cardWidth / cardHeight,
crossAxisCount: 2,
scrollDirection: Axis.horizontal,
mainAxisSpacing: 5,
crossAxisSpacing: 3,
children: List.generate(6, (index) {
return
Center(
child: Container(
alignment: Alignment.center,
child:
SizedBox(
child: Column(
children: [
InkWell(
onTap: (){
if(index == 0)
Navigator.of(context).push(new MaterialPageRoute(builder: (context) => new DoctorList()));
if(index == 1)
launchWhatsApp();
if(index == 2)
launchWhatsApp();
if(index == 5)
showMenu(context);
},
child: Container(
width: 80,
height: 80,
child: Image.asset(icons[index]),
),
),
SizedBox(
height: 5,
),
Text(teks[index], style: TextStyle(color: Colors.black, fontSize:14, fontWeight: FontWeight.bold),)
],
)
)
),
);
})
);
}
you can use flutter screen util package and give dimension using it,
it will give dimension according to device aspect ratio

if statement in ListView flutter

I'm trying to have a responsive drawer that when the screen size is higher than X itemExtent is set to default, how could I add a if statement inside a ListView?
Widget drawer(context){
double _containerHeight = MediaQuery. of(context). size. height;
int _numberOfListTiles = 16;
return new Drawer(
child: Container(
height: _containerHeight,
color: const Color(0xff68778d),
child: ListView(
padding: EdgeInsets.zero,
itemExtent: _containerHeight/_numberOfListTiles,
children: [
],
),
)
);
}
I would like to do:
if(_containerHeight < 700){
itemExtent: _containerHeight/_numberOfListTiles,
}
The cleanest way (in my opinion) would be to instantiate your variable at the top of your drawer method as you did for the _containerHieght and _numberOfListTiles variables.
Your code would then look like that
Widget drawer(context) {
final double _containerHeight = MediaQuery.of(context).size.height;
final int _numberOfListTiles = 16;
double itemExtent;
if(_containerHeight < 700){
itemExtent = _containerHeight/_numberOfListTiles;
} else {
itemExtent = 100; // Default ?
}
return new Drawer(
child: Container(
height: _containerHeight,
color: const Color(0xff68778d),
child: ListView(
padding: EdgeInsets.zero,
itemExtent: _containerHeight / _numberOfListTiles,
children: [],
),
));
}

Flutter - Add Layers on top of ListView to scroll

I am making a reader app and I have a ListView.builder which is loading multiple Images from the API. Now I want to add a layer on top of the ListView with 2 transparent containers so that when I click the left container the list goes up and when I click the right container the list goes down. I tried adding the ListView to stack but then the scrolling ability of the list is gone. This is my code so far:
return Scaffold(
body: Stack(
children: [
ListView.builder(
controller: _scrollViewController,
cacheExtent: 1000,
itemCount: _picLinkMap.length,
itemBuilder: (BuildContext ctx, int index) {
return buildImageLoader(
_picLinkMap[index], index + 1);
},
),
Row(
children: [
GestureDetector(
child: Container(
width: mySize.width * 0.5,
color: Colors.black38,
),
onTap: () {
print("Tap Left");
},
),
GestureDetector(
child: Container(
width: mySize.width * 0.5,
color: Colors.red.withOpacity(0.5),
),
onTap: () {
print("Tap RIGHT");
},
),
],
),
],
),
);
I only want to scroll about half the height of the screen on tap.
I searched the web but did not find a suitable solution.
ScrollController _scrollViewController = ScrollController();
...
var jumpTo = (MediaQuery.of(context).size.height - 104)/2;
//104 is random number for the rest of the screen widgets (e.g. appbar ..)
_scrollViewController.jumpTo(jumpTo); //pixel value
...
ListView.builder(
controller: _scrollViewController,

How to Achieve Smooth Scrolling When Using SingleChildScrollView and Wrap Widget with a Lot of Children?

I am using a Wrap widget to wrap a bunch of cards and sometime the number of cards are high and causing the scroll to be unsmooth and sometime the app stop if the cards has images. The Wrap allows dynamic behavior across different devices size. Is there any way to solve this problem. I am wondering if I can build only the shown cards and not burden the memory with the other cards.? How to ensure smooth scrolling?
SingleChildScrollView(
child: Center(
child: Container(
width: devWidth,
margin: EdgeInsets.only(top: 15, bottom: 15),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
runSpacing: devWidth * 0.03,
children: myList.map<Widget>((item) {
return itemCard(item);
}).toList()),
),
),
)
you should use ListView.seperated() instead of Wrap
ListView.separated(
itemCount: myList.length,
separatorBuilder: (BuildContext Context, int index) {
return SizedBox(height: devWidth * 0.03);
},
itemBuilder: (BuildContext context, int index) {
return itemCard(myList[index]);
},
)
for using multiple items per row use code below:
ListView.separated(
itemCount: myList.length / 3,
separatorBuilder: (BuildContext Context, int index) {
return SizedBox(height: devWidth * 0.03);
},
itemBuilder: (BuildContext context, int index) {
int idx = index * 3;
return Row(
children : <Widget>[
itemCard(myList[idx]),
itemCard(myList[idx + 1]),
itemCard(myList[idx + 2])
]
);
},
)

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