如何在Flutter中为GridView.builder计算childAspectRatio

时间:2020-06-30 21:11:22

标签: flutter

类别网格,其中图像和类别名称显示在图像下方

 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();
      }
    });

}

我的子项具有图像和类别名称。如图所示,当前子项溢出,我们无法在图像下方看到类别名称,并且无法删除图像和边框之间的顶部空间。

enter image description here

原始设计在这里

enter image description here

4 个答案:

答案 0 :(得分:3)

我找到了一种动态设置最大子项的纵横比到我们的Grid视图的方法。 它是如何工作的 ? 首先,请查看此答案。如何在overlayEntery

的构建过程中找到小部件的大小

How to get a height of a Widget?

之后,我们在childAspectRatio的网格视图中设置正确的宽高比(使用overlayEntery进行测量)。

希望这对您有所帮助。

我做个例子...

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();
  }
}

答案 1 :(得分:3)

儿童长宽比基本上是网格相对于设备的宽度/高度。

因此,假设您希望每个网格的宽度为30,高度为20,则将纵横比设置为3/2。

我建议您直接从Flutter团队观看video。尽管它与AspectRatio小部件有关,但有关设置AspectRatio的部分适用于此问题

答案 2 :(得分:2)

您可以使用设备的宽度和高度来动态计算宽高比。我根据您的代码添加了一个代码示例,展示了如何完成此操作。请注意,您可能需要略微调整所提供的宽高比,以符合您的特定要求。

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

iPad Pro iPhone X Galaxy S5

如果尚未查看GridView小部件的宽高比,您可能会发现以下答案很有用。

答案 3 :(得分:-1)

您可以像这样在GridView.builder中提供childAspectRatio:

GridView.builder(
  gridDelegate:
     SliverGridDelegateWithFixedCrossAxisCount(
         crossAxisCount:3,childAspectRatio: (150.0 / 220.0)
     )
)