How to create a custom gridview with appropriate aspect ratio? - flutter

I am trying to create a custom image picker that looks something like this:
As you can see the images are capped at a set height however maintain their aspect ratio (i.e. vertical images are vertical and horizontal images are horizontal). I have created the entire custom image picker. However, I'm struggling with the aspect ratio part. I'd prefer not to use a library, however, if it's easier then please provide the adjusted code.
Could you please provide a solution with code?
FYI I'm using photo_manager to retrieve the images/videos.
This is what it all looks like right now:
Here is my code:
class MediaGrid extends StatefulWidget {
#override
_MediaGridState createState() => _MediaGridState();
}
class _MediaGridState extends State<MediaGrid> {
List<Widget> _mediaList = [];
int currentPage = 0;
int? lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
_fetchNewMedia() async {
lastPage = currentPage;
var result = await PhotoManager.requestPermission();
if (result) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(currentPage, 60);
print(media);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder<dynamic>(
future: asset.thumbDataWithSize(300, 300),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return Stack(
children: <Widget>[
Expanded(
child: Image.memory(snapshot.data, fit: BoxFit.cover),
),
if (asset.type == AssetType.video)
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam_rounded,
color: Colors.white,
),
),
),
],
);
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
/// if result is fail, you can call `PhotoManager.openSetting();` to open android/ios applicaton's setting to get permission
}
}
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
return _handleScrollEvent(scroll);
},
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 5.0,
crossAxisSpacing: 5.0,
),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
);
}
}

This idea will only work if you wish to give each image a single specified height. And using fit: BoxFit.cover to fill up the remaining space.
Now you must find a way to get the width of each image, in my code its of Network Images
From here use the width as the flex value.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
Future<Size> _calculateImageDimension(String url) {
Completer<Size> completer = Completer();
Image image = Image.network(url);
image.image.resolve(ImageConfiguration()).addListener(
ImageStreamListener(
(ImageInfo image, bool synchronousCall) {
var myImage = image.image;
Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
completer.complete(size);
},
),
);
return completer.future;
}
#override
Widget build(BuildContext context) {
//for odd no. of images you might have to add more conditions to your widget
final _netWorkimages = [
'https://images.pexels.com/photos/7179053/pexels-photo-7179053.jpeg?cs=srgb&dl=pexels-olya-prutskova-7179053.jpg&fm=jpg',
'https://images.pexels.com/photos/7527509/pexels-photo-7527509.jpeg?cs=srgb&dl=pexels-john-lee-7527509.jpg&fm=jpg',
'https://images.pexels.com/photos/8018591/pexels-photo-8018591.jpeg?cs=srgb&dl=pexels-inna-stellinna-8018591.jpg&fm=jpg',
'https://images.pexels.com/photos/3244513/pexels-photo-3244513.jpeg?cs=srgb&dl=pexels-andy-vu-3244513.jpg&fm=jpg',
'https://images.pexels.com/photos/694587/pexels-photo-694587.jpeg?cs=srgb&dl=pexels-samuel-silitonga-694587.jpg&fm=jpg',
'https://images.pexels.com/photos/5121986/pexels-photo-5121986.jpeg?cs=srgb&dl=pexels-marcelo-chagas-5121986.jpg&fm=jpg',
'https://images.pexels.com/photos/4519234/pexels-photo-4519234.jpeg?cs=srgb&dl=pexels-dinielle-de-veyra-4519234.jpg&fm=jpg',
'https://images.pexels.com/photos/2286385/pexels-photo-2286385.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
'https://images.pexels.com/photos/35629/bing-cherries-ripe-red-fruit.jpg?cs=srgb&dl=pexels-pixabay-35629.jpg&fm=jpg',
'https://images.pexels.com/photos/4033324/pexels-photo-4033324.jpeg?cs=srgb&dl=pexels-karolina-grabowska-4033324.jpg&fm=jpg'
];
List<Future<Size>> _niSizes = [];
_netWorkimages.forEach((url) {
_niSizes.add(_calculateImageDimension(url));
});
return FutureBuilder<List<Size>>(
future: Future.wait(_niSizes),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(
child: CircularProgressIndicator(),
);
else
return ListView.builder(
itemCount: _netWorkimages.length - 1,
itemBuilder: (context, i) {
return i.isEven
? Container(
height: 120,
child: Row(
children: [
Flexible(
flex: snapshot.data![i].width.toInt(),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Image(
image: NetworkImage(_netWorkimages[i]),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
),
)),
Flexible(
flex: snapshot.data![i + 1].width.toInt(),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Image(
image: NetworkImage(_netWorkimages[i + 1]),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
),
)),
],
),
)
: SizedBox.shrink();
});
},
);
}
}

I hope this is thing you are looking for, replace container with image.
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'dart:math' as math;
class TestScreen extends StatefulWidget {
TestScreen({Key? key}) : super(key: key);
#override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final _items = List.generate(
100,
(index) => ClipRRect(
borderRadius: BorderRadius.circular(
8,
),
child: Container(
height: 124,
color: Color(
(math.Random().nextDouble() * 0xFFFFFF).toInt(),
).withOpacity(1.0),
),
));
#override
Widget build(BuildContext context) {
return Container(
child: StaggeredGridView.countBuilder(
mainAxisSpacing: 2,
crossAxisSpacing: 2,
crossAxisCount: 6,
itemCount: 100,
itemBuilder: (context, index) {
return _items[index];
},
staggeredTileBuilder: (index) {
if (index % 6 == 0 || index % 6 == 3) {
return StaggeredTile.count(2, 1);
} else if (index % 6 == 1 || index % 6 == 2) {
return StaggeredTile.count(4, 1);
} else
return StaggeredTile.count(3, 1);
},
),
);
}
}

You can use https://pub.dev/packages/flutter_staggered_grid_view
This plugin has options to modify the aspect ratio

Related

How to load more items to a list when reach the bottom of results flutter

I have the code below which feed a list with 10 results from firebase. In this case it shows only the 10 items, now I wanna, when user gets the bottom of results, it loads more 10 items and add it to the list. I already have the scrollController and it works.. I receive the log "LOAD HERE" when I get the bottom of the results.
My doubt is how to add the new 10 items in the list?
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
print('LOAD HERE');
}
}
#override
void initState() {
scrollController.addListener(scrollListener);
super.initState();
}
#override
void dispose() {
scrollController.removeListener(scrollListener);
super.dispose();
}
loadList(submenu ,callback, context, deviceSize){
return FutureBuilder(
future: ctrlLab.loadList(submenu, 10),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.error != null) {
print(snapshot.error);
return Center(child: Text('ERROR!'));
}else {
return GridView.builder(
padding: EdgeInsets.all(10.0),
controller: scrollController,
itemCount: snapshot.data.length,
itemBuilder: (ctx, i) {
Item item = snapshot.data[i];
if (i < snapshot.data.length) {
return Dismissible(
key: UniqueKey(),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.all(10.0),
color: Colors.grey[800],
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: Icon(
Icons.delete,
color: Colors.white,
size: 40,
),
),
),
onDismissed: (DismissDirection direction) {
ctrl.onDismissed(callback, item);
},
child: GestureDetector(
child: Card(
elevation: 5.0,
child: Padding(
padding: EdgeInsets.all(10.0),
child: GridTile(
child: Hero(
tag: "${item}",
child: item.imageUrl == null
? setIconLab(item)
: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: setIconLab(item),
placeholder: (ctx, url) =>
Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Image.asset('assets/images/noPhoto.jpg',
fit: BoxFit.cover),
),
),
footer: Container(
padding: EdgeInsets.all(8.0),
color: Colors.white70,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item.name
),
),
],
),
),
),
),
),
),
);
}
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndLoading(
itemCount: snapshot.data.length + 1,
crossAxisCount: deviceSize.width < 600 ? 2 : 3,
childAspectRatio: 0.7,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
),
);
}
},
);
}
Infinite Scrolling in ListView
I have achieved this case by using the local field instead of getting data from firebase. Hope it will give you some idea.
import 'package:flutter/material.dart';
class ListViewDemo extends StatefulWidget {
ListViewDemo({Key key}) : super(key: key);
#override
_ListViewDemoState createState() => _ListViewDemoState();
}
class _ListViewDemoState extends State<ListViewDemo> {
ScrollController controller;
int count = 15;
#override
void initState() {
super.initState();
controller = ScrollController()..addListener(handleScrolling);
}
void handleScrolling() {
if (controller.offset >= controller.position.maxScrollExtent) {
setState(() {
count += 10;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List view'),
),
body: ListView.builder(
controller: controller,
itemCount: count,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
),
);
}
#override
void dispose() {
controller.removeListener(handleScrolling);
super.dispose();
}
}
You have to add another 10 data to the crtLap.loadList(subMenu, 20) and call setState inside the scrollListener to rebuild the widget about the changes.
var data = crtLap.loadList(subMenu, 10);
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
setState((){
data = crtLap.loadList(subMenu, 20);
});
}
}
and use this data field to the FutureBuilder directly,
loadList(submenu ,callback, context, deviceSize){
return FutureBuilder(
future: data,
builder: (ctx, snapshot) {
.....
...
..
}

how to create an icon on image with inkwell after Click on the image in flutter

I made a grid view in the flutter app. But like the Pictures on the below link , I want to create an icon on the picture and change the background color After tap the picture,
I've been looking for ways, but I've finally got a question. I'd appreciate it from the bottom of my heart if you'd let me know.
Please enter img link(below)
https://firebasestorage.googleapis.com/v0/b/instaclone-2-fd9de.appspot.com/o/post%2F12344.png?alt=media&token=89d46c03-83ba-4d30-b716-e9b718c1340b
Widget _bodyBuilder() {
// TODO : 그 예시를 어떻해 stream View로 보여줄것인가
return StreamBuilder <QuerySnapshot>(
stream: _commentStream(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(!snapshot.hasData){
return Center(child: CircularProgressIndicator());
}
var items = snapshot.data?.documents ??[];
var fF = items.where((doc)=> doc['style'] == "오피스룩").toList();
var sF = items.where((doc)=> doc['style'] == "로맨틱").toList();
var tF = items.where((doc)=> doc['style'] == "캐주").toList();
fF.addAll(sF);
fF.addAll(tF);
fF.shuffle();
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.6,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0),
itemCount: fF.length,
itemBuilder: (BuildContext context, int index) {
return _buildListItem(context, fF[index]);
});
},
);
}
Widget _buildListItem(context, document) {
return Ink.image(
image : NetworkImage(document['thumbnail_img']),
fit : BoxFit.cover,
child: new InkWell(
//I think we need to get something in here....
onTap: (){},
),
);
}
You should create List of Image which have isSelected value and when the user clicks on item them set true/false base of the old value which have imageURL and isSelected variable. First, you should store value in List Of Image obj. which coming from Firebase/API then flow below step. I have created a demo and post here. Please take reference.
Example code
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
List<ImageData> imageList;
#override
void initState() {
super.initState();
imageList = ImageData.getImage();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 0.5,
crossAxisCount: 5,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0),
itemCount: imageList.length,
itemBuilder: (builder, index) {
return InkWell(
onTap: () {
setState(() {
imageList[index].isSelected = !imageList[index].isSelected;
});
},
child: Stack(
children: [
_getImage(imageList[index].imageURL),
Opacity(
opacity: imageList[index].isSelected ? 1 : 0,
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
color: Colors.black38,
),
Center(
child: CircleAvatar(
backgroundColor: Colors.greenAccent,
child: Icon(
Icons.check,
color: Colors.white,
),
),
)
],
),
)
],
));
},
),
);
}
_getImage(url) => Image.network(
url,
height: 500,
fit: BoxFit.fitHeight,
);
#override
void dispose() {
super.dispose();
}
}
class ImageData {
String imageURL;
bool isSelected;
int id;
ImageData(this.imageURL, this.isSelected, this.id);
static List<ImageData> getImage() {
return [
ImageData('https://picsum.photos/200', false, 1),
ImageData('https://picsum.photos/100', false, 2),
ImageData('https://picsum.photos/300', false, 3),
ImageData('https://picsum.photos/400', false, 4),
ImageData('https://picsum.photos/500', false, 5),
ImageData('https://picsum.photos/600', false, 6),
ImageData('https://picsum.photos/700', false, 7),
ImageData('https://picsum.photos/800', false, 8),
ImageData('https://picsum.photos/900', false, 9),
];
}
}
Output

How to calculate childAspectRatio for GridView.builder in flutter

Grid of categories with image and category name displayed below the image
Widget build(BuildContext context) {
return FutureBuilder(
future: categoriesService.getCategories(1),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.error != null) {
print('error ${snapshot.error}');
return Text(snapshot.error.toString());
}
// YOUR CUSTOM CODE GOES HERE
return Container(
// padding: const EdgeInsets.all(0.0),
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// childAspectRatio: 19 / 12,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Category category = snapshot.data[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: Image.network(
category.image,
fit: BoxFit.cover,
),
decoration: BoxDecoration(
border: Border.all(width: 1.0),
),
),
Text(category.name)
],
);
},
),
);
} else {
return new CircularProgressIndicator();
}
});
}
My child item has an image and category name. as seen in the image, currently child item is overflowing and we cant see the category name below the image, and unable to remove top space between image and border.
Original design is here
You could use the width and height of the device to calculate the aspect ratio dynamically. I have added a code sample based on yours showing how this could be done. Please note that you might need to adjust the provided aspect ratio slightly in order to fit your particular requirements.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class Category {
String name;
String image;
Category({this.name, this.image,});
}
class CategoriesService {
Future<List<Category>> getCategories(int value) async {
return <Category>[
Category(
name: 'FRESH CHICKEN',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'FRESH MUTTON',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'HALAL FROZEN FISH',
image: 'https://picsum.photos/400/300',
),
Category(
name: '2X STORE',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'FROZEN NONVEG DELIGHTS',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'FROZEN VEG DELIGHTS',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'DAL & PULSES',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'SPICES',
image: 'https://picsum.photos/400/300',
),
Category(
name: 'DRY FRUITS & NUTS',
image: 'https://picsum.photos/400/300',
),
];
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FutureBuilder(
future: CategoriesService().getCategories(1),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.error != null) {
print('error ${snapshot.error}');
return Text(snapshot.error.toString());
}
// YOUR CUSTOM CODE GOES HERE
return Container(
// padding: const EdgeInsets.all(0.0),
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 1.4),
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Category category = snapshot.data[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: Image.network(
category.image,
fit: BoxFit.cover,
),
decoration: BoxDecoration(
border: Border.all(width: 1.0),
),
),
Padding(
padding: const EdgeInsets.only(
top: 8.0,
),
child: Text(
category.name,
textAlign: TextAlign.center,
),
)
],
);
},
),
);
} else {
return new CircularProgressIndicator();
}
}),
),
);
}
}
You might find the below answers useful for additional information regarding the aspect ratio of the GridView widget if you have not seen them already.
How to set Custom height for Widget in GridView in Flutter?
How to set grid view column height?
flutter how to give height to the childrens of GridView.Builder
I find a way to set aspect ratio of biggest child to our Grid view dynamicly.
how it work ?
First please Look at this answer for . how we can find size of a widget during the build with overlayEntery
How to get a height of a Widget?
after that we set the right aspect ratio (Measured with overlayEntery ) to our Grid View in childAspectRatio.
I hope this help you .
I Make an Example ...
https://dartpad.dev/4821d71ec618d7d1f1f92f27458fde61
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class GridItemModel {
String longtext;
GlobalKey itemKey;
double width;
double height;
GridItemModel(this.longtext) {
itemKey = GlobalKey();
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return _StateHomePage();
}
}
class _StateHomePage extends State<MyHomePage> {
// this first assign very important don't change it .
// if you change this part overlayEntry cant find biggest widget correctly .(cant see not scrolled items.)
// for test change to 1/1 and see what happening.
var myDynamicAspectRatio = 1000 / 1;
OverlayEntry sticky;
List<GridItemModel> myGridList = new List();
double maxHeight = 0;
double maxWidth = 0;
#override
void initState() {
if (sticky != null) {
sticky.remove();
}
sticky = OverlayEntry(
builder: (context) => stickyBuilder(context),
);
WidgetsBinding.instance.addPostFrameCallback((_) {
Overlay.of(context).insert(sticky);
setState(() {});
});
super.initState();
}
#override
void dispose() {
WidgetsBinding.instance.addPostFrameCallback((_) {
sticky.remove();
});
super.dispose();
}
#override
Widget build(BuildContext context) {
final title = 'Grid List';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: GridView.count(
crossAxisCount: 2,
childAspectRatio: myDynamicAspectRatio,
children: List.generate(20, (index) {
myGridList.add(new GridItemModel(longTextMaker(index)));
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
key: myGridList[index].itemKey,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.teal[index*100]
),
child: Text(
'Item $index' + myGridList[index].longtext,
style: Theme.of(context).textTheme.headline5,
),
),
],
);
}),
),
),
);
}
String longTextMaker(int count){
String result = "longText";
for(int i = 0 ; i < count; i++){
result += "longText" ;
}
return result;
}
shouldUpdateGridList(){
bool isChanged =false;
for(GridItemModel gi in myGridList) {
if(gi.width != null) {
if (gi.height > maxHeight) {
maxHeight = gi.height;
maxWidth = gi.width;
isChanged = true;
}
}
}
if(isChanged) {
myDynamicAspectRatio = maxWidth / maxHeight;
print("AspectRatio" + myDynamicAspectRatio.toString());
}
}
Widget stickyBuilder(BuildContext context) {
for(GridItemModel gi in myGridList) {
if(gi.width == null) {
final keyContext = gi.itemKey.currentContext;
if (keyContext != null) {
final box = keyContext.findRenderObject() as RenderBox;
print(box.size.height);
print(box.size.width);
gi.width = box.size.width;
gi.height = box.size.height;
}
}
}
shouldUpdateGridList();
return Container();
}
}
Child aspect ratio is basically width/height of the grid relative to the device.
So let's say you want the width of each grid to be 30 and the height to be 20, you would set the aspect ratio to be 3/2.
I suggest you watch this video directly from the Flutter team. Although its about the AspectRatio widget, the part about setting the aspectRatio applies to this problem
You can provide childAspectRatio in GridView.builder like this:
GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:3,childAspectRatio: (150.0 / 220.0)
)
)
There is a flutter package that solves this problem conveniently. Check it out at https://pub.dev/packages/dynamic_height_grid_view. With this you can specify cross-axis count without respecting aspect ratio.
For most use-cases where child height is static or need to be static,
please use mainAxisExtent.
Official docs
/// The extent of each tile in the main axis. If provided it would define the
/// logical pixels taken by each tile in the main-axis.
///
/// If null, [childAspectRatio] is used instead.
final double? mainAxisExtent;
The childAspectRatio value is a value for you to set the aspect ratio, if you want to make a neat square, you would use 1 and the width will be equal to the height.
if you want it to extend to the bottom, decrease the value.
sample
childAspectRatio:0.9,
Try this
return LayoutBuilder(builder: (context, constraints) {
return GridView.count(
crossAxisCount: crossAxisCount( constraints.maxWidth,100),
)
};
);
int crossAxisCount(double maxWidth, size) {
int width= maxWidth~/ size;
return width== 0 ? 1 : width;
}
(ScreenWidth - (left&right Padding + crossAxisSpace))/ itemHeight
Eg.
I have an item with height 241
and gridview padding of left and right => 16 + 16
and crossAxisSpace 9
I will calculate like this
double extraSpace = (16+16+9);
aspectRatio : ((MediaQuery.of(context).size.width - extraSpace) / 2) / 241

Draw outside listview bounds in Flutter

I want to transform my item that it is bigger than the listview itself. (intention for focused navigation)
My List:
Container(
height: 100,
child: ListView.builder(
itemBuilder: (context, index) => HomeItem(title: '$index'),
scrollDirection: Axis.horizontal,
),
),
My Item:
class HomeItem extends StatelessWidget {
final String title;
final bool expand;
const HomeItem({
#required this.title,
this.expand = false,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: ThemeDimens.padding8),
child: Transform.scale(
scale: expand ? 1.5 : 1,
child: AnimatedContainer(
width: 50,
height: 100,
color: expand ? ThemeColors.accent : ThemeColors.primary,
duration: ThemeDurations.shortAnimationDuration(),
child: Center(
child: Text(title),
),
),
),
);
}
}
Current behaviour
Expected behaviour
If you try to use OverflowBox or Transform, content of an item will still clip and won't be drawn outside of its bounding box. But it's possible to use Overlay to draw an element on top of list and position it on a specific list item, though it's a bit complicated.
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
final elements = List.generate(12, (i) => i);
int selectedIndex;
OverlayEntry overlayEntry;
List<LayerLink> layerLinks;
#override
void initState() {
super.initState();
// Creating a layer link for each list cell
layerLinks = List.generate(elements.length, (i) => LayerLink());
}
void createOverlayEntry(int i, BuildContext context) {
// Removing an overlay entry, if there was one
overlayEntry?.remove();
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
// Creating a new overlay entry linked to specific list element
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
left: 0,
top: 0,
child: CompositedTransformFollower(
link: layerLinks[i],
showWhenUnlinked: false,
offset: Offset(-20, 0),
child: Material(
color: Colors.yellow,
child: InkWell(
onTap: () {
setState(() {
selectedIndex = null;
});
overlayEntry?.remove();
overlayEntry = null;
},
child: Container(
alignment: Alignment.center,
width: 70,
height: elementHeight,
child: Text('$i')
),
)
),
)
)
);
// Inserting an entry
Overlay.of(context).insert(overlayEntry);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: elementHeight,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: elements.length,
itemBuilder: (c, i) {
return CompositedTransformTarget(
link: layerLinks[i],
child: Material(
color: Colors.red,
child: InkWell(
onTap: () {
setState(() {
selectedIndex = i;
});
createOverlayEntry(i, context);
},
child: Container(
alignment: Alignment.center,
width: 30,
child: Text('${elements[i]}'),
),
),
),
);
},
separatorBuilder: (c, i) {
return Container(width: 10, height: 10);
},
),
),
);
}
}

Create image slider with pageView

i tried create a image slider like this image
i tried create this slider with PageView but a have a problem with pageview
and my code :
class SlideMusic extends StatefulWidget{
final Size _size;
final List<String> _listArts;
double itemExtent;
SlideMusic(this._size,this._listArts){
itemExtent = _size.width / 3;
}
#override
SlideMusicState createState() => SlideMusicState();
}
class SlideMusicState extends State<SlideMusic> {
double currentPage = 0;
PageController _pageController = PageController(
viewportFraction: 0.3333,
);
#override
void setState(fn) {
super.setState(fn);
_pageController.addListener((){
currentPage = _pageController.page;
});
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: widget._size.width,
height: widget.itemExtent,
child: Container(
color: Colors.green,
child: NotificationListener<ScrollNotification>(
onNotification: _onNotification,
child: PageView.builder(
controller: _pageController,
physics: BouncingScrollPhysics(),
itemCount: widget._listArts.length,
itemBuilder: (context, index){
return itemArt(widget._listArts[index], index);
},
),
)
)
);
}
bool _onNotification(ScrollNotification notification){
setState(() {
currentPage = _pageController.page;
print(currentPage);
});
}
double itemOffset(int index){
return (index - currentPage ).abs()* widget.itemExtent;
}
Widget itemArt(String image , int index){
Offset offset = Offset.zero;
if(index > currentPage.round())
offset = Offset(-itemOffset(index)/2,0);
else
offset = Offset(itemOffset(index)/2,0);
return Align(
alignment: Alignment.center,
child: Transform.translate(
offset: offset,
child: Container(
color: Colors.lightBlue,
child: LayoutBuilder(
builder: (context,constrat){
return SizedBox(
width: constrat.maxWidth - itemOffset(index)/3.5,
height: constrat.maxHeight - itemOffset(index) /3.5,
child: Center(
child: Image.asset(image)
),
);
},
)
),
)
);
}
}
Check out the Picture slider widget made with carousel_slider.
Don't mind the jankyness of GIF.
Code:
class PictureSlideShow extends StatefulWidget {
#override
_PictureSlideShowState createState() => _PictureSlideShowState();
}
class _PictureSlideShowState extends State<PictureSlideShow> {
#override
Widget build(BuildContext context) {
return Center(
child: CarouselSlider(
height: 200.0,
enlargeCenterPage: true,
items: [1, 2, 3, 4, 5].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: 400,
height: 400,
margin: EdgeInsets.all(0.5),
decoration:
BoxDecoration(color: Colors.lightBlue[100 * (i % 5)]),
child: Center(
child: Text(
'text $i',
style: TextStyle(fontSize: 16.0),
),
),
);
},
);
}).toList(),
),
);
}
}
This is a similiar design to what you wanted.