如何限制可拖动滚动表根据其子高度在颤动中获取高度?

Posted

技术标签:

【中文标题】如何限制可拖动滚动表根据其子高度在颤动中获取高度?【英文标题】:How to limit draggable scrollable sheet to take height according to its child height in flutter? 【发布时间】:2020-06-03 09:27:05 【问题描述】:

我正在使用draggableScrollableSheet。我正在给这些参数

DraggableScrollableSheet(initialChildSize: 0.4,maxChildSize: 1,minChildSize: 0.4,builder: (BuildContext context, ScrollController scrollController) 
            return SingleChildScrollView(controller: scrollController,
              child: Theme(
                data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
                child: Opacity(
                  opacity: 1,
                  child: IntrinsicHeight(
                      child: Column(mainAxisSize: MainAxisSize.min,
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          SizedBox(height: 10,),
                          Container(
                            margin: EdgeInsets.only(right: 300),
                            decoration: BoxDecoration(
                              border: Border(
                                top: BorderSide(
                                    color: Colors.blue,
                                    width: 3,
                                    style: BorderStyle.solid),
                              ),
                            ),
                          ),
                          Card(
                            child: Row(
                              children: <Widget>[
                                Padding(
                                  padding: const EdgeInsets.all(8.0),
                                  child: Column(
                                    crossAxisAlignment:
                                    CrossAxisAlignment.start,
                                    children: <Widget>[
                                      Text(
                                        S
                                            .of(context)
                                            .we_have_found_you_a_driver,
                                        style: TextStyle(
                                            color: Colors.black,
                                            fontWeight: FontWeight.bold),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(S
                                          .of(context)
                                          .driver_is_heading_towards +
                                          ' $widget.order.foodOrders.first.food.restaurant.name')
                                    ],
                                  ),
                                ),
                              ],
                            ),
                            elevation: 5,
                          ),
                          SizedBox(height: 10,),
                          Card(
                            elevation: 5,
                            child: Row(
                                mainAxisAlignment: MainAxisAlignment.start,
                                children: <Widget>[
                                  CircleAvatar(
                                    radius: 50.0,
                                    backgroundColor: Colors.white,
                                    child:
                                    Image.asset(
                                        'assets/img/image_not_available.jpg'),
                                  ),
                                  Expanded(
                                    child: Column(mainAxisAlignment: MainAxisAlignment.start,
                                      children: <Widget>[
                                        Row(mainAxisAlignment: MainAxisAlignment.spaceAround,
                                          children: <Widget>[
                                            Expanded(
                                              child: Text('Test',
                                                  textAlign: TextAlign.start,
                                                  style: new TextStyle(
                                                    color: Colors.black,
                                                    fontSize: 16.0,
                                                  )),
                                            ),
                                            Icon(Icons.star, color: Colors.yellow.shade700,)
                                          ],
                                        ),
                                        SizedBox(height: 30,),
                                        Row(mainAxisAlignment: MainAxisAlignment.spaceAround,
                                          children: <Widget>[
                                            Expanded(
                                              child: Container(
                                                child: Text('Mobile number',
                                                    textAlign: TextAlign.start,
                                                    style: new TextStyle(
                                                      color: Colors.black,
                                                      fontSize: 16.0,
                                                    )),
                                              ),
                                            ),
                                            Icon(Icons.phone,),
                                            SizedBox(width: 10,),
                                            Icon(Icons.message),
                                          ],
                                        ),

                                      ],
                                    ),
                                  )
                                ]),
                          ),
                          SizedBox(height: 10,),
                          Card(
                            child: Align( alignment: Alignment(-1,1),
                              child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                children: <Widget>[
                                  Padding(
                                    padding: const EdgeInsets.all(8.0),
                                    child: Column(
                                      crossAxisAlignment:
                                      CrossAxisAlignment.start,
                                      children: <Widget>[
                                        Text(
                                          S
                                              .of(context)
                                              .you_ordered_from + ' $widget.order.foodOrders.first.food.restaurant.name',
                                          style: TextStyle(
                                            color: Colors.grey,
                                          ),
                                        ),
                                        SizedBox(
                                          height: 5,
                                        ),
                                        Column(children: List.generate(widget.order.foodOrders.length,(index) 
                                          return Text(
                                              '$widget.order.foodOrders[index].food.name'
                                          );
                                        ,),),
                                        Row(
                                          children: <Widget>[
                                            Column(crossAxisAlignment: CrossAxisAlignment.start,
                                              children: <Widget>[
                                                Text('See details', style: TextStyle(fontWeight: FontWeight.bold,color: Colors.blue),),

                                              ],
                                            ),


                                          ],
                                        ),

                                      ],
                                    ),
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.all(8.0),
                                    child: Column(
                                      children: <Widget>[
                                        SizedBox(height: 40,),
                                        Row(
                                          children: <Widget>[
                                            Icon(Icons.monetization_on),
                                            Text(widget.order.foodOrders
                                                .first.price
                                                .toString()),
                                          ],
                                        ),
                                      ],
                                    ),
                                  ),
                                ],
                              ),
                            ),
                            elevation: 5,
                          ),
                        ],
                      ),
                  ),
                ),
              ),
            )

我还使用了单个子滚动视图和列,以便我可以在 draggableScrollableSheet 的该列中显示我的卡片。但我希望 draggableScrollableSheet 动态获取高度而不是定义大小。就像现在我只想显示 2 到 3 张卡片,并且是全屏显示。但我希望它占据屏幕的最小高度。我们怎样才能做到这一点?

【问题讨论】:

如果你有答案,请分享! 【参考方案1】:

我已经使用 Flutter 一周,但我找到了解决方案。如果我错了,可能不合格,所以请纠正我。

所以我所做的是为底部工作表创建一个名为bsRatio 的变量。这将是子视图/小部件(或底部工作表内容)的高度除以父/屏幕的高度。此比率应设置为 DraggableScrollableSheet 的 maxChildSize 甚至可能是 initialChildSize

因此,在您的父小部件或 Widget State 类中添加类似这样的内容。

class ParentWidget extends StatefulWidget 
  ParentWidget(Key? key) : super(key: key);

  @override
  State<ParentWidget> createState() => _ParentWidgetState();


class _ParentWidgetState extends State<ParentWidget> 
    
  var bsRatio = 0.4; // Set an initial ratio
    
  @override
  Widget build(BuildContext context) 
      // The line below is used to get status bar height. Might not be required if you are not using the SafeArea 
      final statusBarHeight = MediaQuery.of(context).viewPadding.top;
      // If you are not using SafeArea Widget you can skip subtracting status bar height from the Window height
      final windowHeight = MediaQuery.of(context).size.height - statusBarHeight;
      // This below is a callback function that will be passed to the child Widget of the DraggableScrollableSheet -> 
      childHeightSetter(childHeight) 
          // setState rebuilds the UI with the new `bsRatio` value
          setState(() 
              // The new bottom sheet max height ratio is the height of the Child View/Widget divide by the screen height
              bsRatio = childHeight / windowHeight;
          );
      
      return Scaffold(
          backgroundColor: Colors.black12,
          body: SafeArea(
              child: Stack(
                  children: [
                      const SomeBackgroundView(),
                      DraggableScrollableSheet(
                          initialChildSize: bsRatio, // here you set the newly calculated ratio as the initial height of the Bottom Sheet
                          minChildSize: 0.2,
                          maxChildSize: bsRatio, // here you set the newly calculated ratio as the initial height of the Bottom Sheet
                          snap: true,
                          builder: (_, controller) 
                              return LayoutBuilder(builder: (_, box) 
                                  // Added a container here to add some curved borders and decent looking shadows via the decoration property
                                  return Container(
                                      child: SingleChildScrollView(
                                          controller: controller,
                                          // The child view/widget `MyBottomSheet` below is the actual bottom sheet view/widget
                                          child: MyBottomSheet(childHeightSetter: childHeightSetter),
                                      ),
                                      decoration: const BoxDecoration(
                                          boxShadow: [
                                              BoxShadow(
                                                  color: Colors.grey,
                                                  blurRadius: 5.0,
                                                  spreadRadius: 2.0
                                              )
                                          ],
                                          borderRadius: BorderRadius.all(Radius.circular(20.0))
                                      ),
                                  );
                              );
                          ,
                      ),
                  ],
              ),
          ),
      );
  

这将是您的子视图/小部件(也是您的 BottomSheet 视图/小部件)

class MyBottomSheet extends StatefulWidget 
  // This below is the local callback variable. The `?` is because it may not be set if not required
  final ValueSetter<double>? childHeightSetter;

  const MyBottomSheet(Key? key, this.childHeightSetter) : super(key: key);

  @override
  _MyBottomSheetState createState() => _MyBottomSheetState();


class _LoginBottomSheetState extends State<LoginBottomSheet> 
  // bsKey is the key used to reference the Child widget we are trying to calculate the height of. Check the `Card` container below
  GlobalKey bsKey = GlobalKey();

  // this method will me used to get the height of the child content and passed to the callback function so it can be triggered and the ratio can be calculated and set in the parent widget
  _getSizes() 
    final RenderBox? renderBoxRed =
      bsKey.currentContext?.findRenderObject() as RenderBox?;
    final cardHeight = renderBoxRed?.size.height;
    if (cardHeight != null) 
      super.widget.childHeightSetter?.call(cardHeight);
  

  // This is the function to be called after the Child has been drawn
  _afterLayout(_) 
    _getSizes();
  

  @override
  void initState() 
    super.initState();
    // On initialising state pass the _afterLayout method as a callback to trigger after the child Widget is drawn
    WidgetsBinding.instance?.addPostFrameCallback(_afterLayout);
  

  @override
  Widget build(BuildContext context) 
    return Card(
      key: bsKey, // This is the key mentioned above used to calculate it's height
      color: Colors.white,
      shadowColor: Colors.black,
      elevation: 40.0,
      margin: EdgeInsets.zero,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0))),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          // Random children for bottom sheet content
          const SizedBox(height: 10.0),
          Center(
            child: Container(
              child: const SizedBox(width: 40.0, height: 5.0),
              decoration: BoxDecoration(
                color: Colors.grey[400],
                borderRadius: BorderRadius.circular(5.0)
              ),
            ),
          ),
          const SizedBox(height: 10.0),
          const AnotherBottomSheetContentView()
        ],
      ),
    );
  

【讨论】:

【参考方案2】:

initialChildSize 是 ScrollView 在实际滚动之前的高度,这意味着您实际上可以决定它的外观。 这是一个例子![the draggable scrollsheet here has initialChildSize: 0.1,maxChildSize: 1,minChildSize: 0.1, ]1

【讨论】:

您如何为 draggablescrollablesheet 指定高度。就像在你的情况下,如果我添加另一个 textformfield 然后布局会动态调整自己,或者我们必须静态设置高度? im 使用 SingleChildScrollView 和 Column ,列主轴大小设置为最小,这意味着您的列大小相对于您的孩子会增长 我刚刚重新编辑了我的问题。现在我还在我的问题中包含了代码。我也给了柱子的最小高度。你能看看我哪里做错了吗? 子卡列未设置为最小值 糟糕的答案。没有例子

以上是关于如何限制可拖动滚动表根据其子高度在颤动中获取高度?的主要内容,如果未能解决你的问题,请参考以下文章

如何在颤动中创建可滚动的行

当项目具有动态高度时,如何根据设备方向创建动态颤动网格布局

如何在 SwiftUI 中根据其子级高度设置 GeometryReader 高度?

在可拖动事件上,元素随着滚动条窗口顶部的高度向下移动

如何在颤动中隐藏布局溢出消息

echarts滚动条高度和字体大小