如何在颤动中计算 GridView.builder 的 childAspectRatio

Posted

技术标签:

【中文标题】如何在颤动中计算 GridView.builder 的 childAspectRatio【英文标题】:How to calculate childAspectRatio for GridView.builder in flutter 【发布时间】:2020-10-21 06:34:08 【问题描述】:

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

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

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

原创设计在这里

【问题讨论】:

子纵横比基本上是网格的宽度/高度。因此,假设您希望每个网格的宽度为 30,高度为 20,您可以将纵横比设置为 3/2。 还没有解决我的问题。我必须显示 1:1 图像,并且在每个图像的底部要添加一个具有 100 像素高度的完整可用宽度的容器。请建议,我们该怎么做。谢谢。 【参考方案1】:

您可以使用设备的宽度和高度来动态计算纵横比。我添加了一个基于您的代码示例,展示了如何做到这一点。请注意,您可能需要稍微调整提供的纵横比以满足您的特定要求。

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

如果您还没有看过以下答案,您可能会发现以下答案对有关 GridView 小部件的纵横比的更多信息很有用。

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

【讨论】:

我必须以 1:1(宽:高)的比例显示网格,但高度应该额外增加 100 像素。例如。我能怎么做?请建议。谢谢。【参考方案2】:

我找到了一种方法,可以动态地将最大孩子的纵横比设置为我们的 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:1 图像,并且在每个图像的底部要添加一个具有 100 像素高度的完整可用宽度的容器。请建议,我们该怎么做。谢谢。【参考方案3】:

子纵横比基本上是网格相对于设备的宽度/高度。

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

我建议你直接从 Flutter 团队观看这个video。虽然它是关于 AspectRatio 小部件的,但关于设置 aspectRatio 的部分适用于这个问题

【讨论】:

还没有解决我的问题。我必须显示 1:1 图像,并且在每个图像的底部要添加一个具有 100 像素高度的完整可用宽度的容器。请建议,我们该怎么做。谢谢。【参考方案4】:

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

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

【讨论】:

*** 中的代码块语法不同,可以使用帮助查看代码块语法

以上是关于如何在颤动中计算 GridView.builder 的 childAspectRatio的主要内容,如果未能解决你的问题,请参考以下文章

如何阻止 Gridview.builder 的滚动条在颤动中显示

在flutter中设置childAspectRatio的正确方法

带有 Firebase 实时数据库和 futurebuilder 的 Gridview.builder

为啥 GridView.builder 返回高度扩展的元素,而 ListView.Seperated 返回具有正常高度的元素?

将 ListView 转换为 ListView.builder

如何在 Flutter 中将 Header 添加到 GridView?