简体   繁体   English

Flutter 中垂直滚动视图内的水平列表视图

[英]Horizontal ListView inside a Vertical ScrollView in Flutter

I am trying to achieve a very common behavior nowadays which is to have a horizontal List within another widget that is at the same time scrollable.我正在尝试实现一种非常常见的行为,即在另一个小部件中同时具有可滚动的水平列表。 Think something like the home screen of the IMDb app:想想像 IMDb 应用程序的主屏幕:

在此处输入图像描述

So I want to have a widget that scrolls vertically with few items on them.所以我想要一个垂直滚动的小部件,上面有几个项目。 At the top of it, there should be a horizontal ListView , followed up with some items called motivationCard .在它的顶部,应该有一个水平的ListView ,然后是一些名为motivationCard的项目。 There are some headers in between the list and the cards as well.列表和卡片之间也有一些标题。

I got something like this on my Widget :我的Widget上有这样的东西:

@override
  Widget build(BuildContext context) => BlocBuilder<HomeEvent, HomeState>(
        bloc: _homeBloc,
        builder: (BuildContext context, HomeState state) => Scaffold(
              appBar: AppBar(),
              body: Column(
                children: <Widget>[
                  Text(
                    Strings.dailyTasks,
                  ),
                  ListView.builder(
                    scrollDirection: Axis.horizontal,
                    itemCount: tasks.length,
                    itemBuilder: (BuildContext context, int index) =>
                        taskCard(
                          taskNumber: index + 1,
                          taskTotal: tasks.length,
                          task: tasks[index],
                        ),
                  ),
                  Text(
                    Strings.motivations,
                  ),
                  motivationCard(
                    motivation: Motivation(
                        title: 'Motivation 1',
                        description:
                        'this is a description of the motivation'),
                  ),
                  motivationCard(
                    motivation: Motivation(
                        title: 'Motivation 2',
                        description:
                        'this is a description of the motivation'),
                  ),
                  motivationCard(
                    motivation: Motivation(
                        title: 'Motivation 3',
                        description:
                        'this is a description of the motivation'),
                  ),
                ],
              ),
            ),
      );

this is the error I get:这是我得到的错误:

I/flutter (23780): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (23780): The following assertion was thrown during performResize():
I/flutter (23780): Horizontal viewport was given unbounded height.
I/flutter (23780): Viewports expand in the cross axis to fill their container and constrain their children to match
I/flutter (23780): their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of
I/flutter (23780): vertical space in which to expand.

I have tried:我努力了:

  • Wrapping the ListView with an Expanded widgetExpanded小部件包装 ListView

  • Wrapping the Column with SingleChildScrollView > ConstrainedBox > IntrinsicHeightSingleChildScrollView > ConstrainedBox > IntrinsicHeight包裹列

  • Having CustomScrollView as a parent, with a SliverList and the List within a SliverChildListDelegateCustomScrollView作为父级,具有SliverListSliverChildListDelegate中的 List

None of these work and I continue getting the same kind of error.这些都不起作用,我继续遇到同样的错误。 This is a very common thing and shouldn't be any hard, somehow I just cannot get it to work:(这是一件很常见的事情,应该不难,不知怎的,我就是无法让它工作:(

Any help would be much appreciated, thanks!任何帮助将不胜感激,谢谢!

Edit:编辑:

I thought this could help me but it didn't.我认为可以帮助我,但它没有。

Well, Your Code Work Fine with wrapping your- ListView.builder with Expanded Widget & setting mainAxisSize: MainAxisSize.min, of Column Widget.好吧,您的代码可以很好地使用Expanded小部件包装您的ListView.builder并设置mainAxisSize: MainAxisSize.min, of Column Widget。

Ex Code of what you Have.你所拥有的前代码。

 body: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Text(
            'Headline',
            style: TextStyle(fontSize: 18),
          ),
          Expanded(
            child: ListView.builder(
              shrinkWrap: true,
              scrollDirection: Axis.horizontal,
              itemCount: 15,
              itemBuilder: (BuildContext context, int index) => Card(
                    child: Center(child: Text('Dummy Card Text')),
                  ),
            ),
          ),
          Text(
            'Demo Headline 2',
            style: TextStyle(fontSize: 18),
          ),
          Expanded(
            child: ListView.builder(
              shrinkWrap: true,
              itemBuilder: (ctx,int){
                return Card(
                  child: ListTile(
                      title: Text('Motivation $int'),
                      subtitle: Text('this is a description of the motivation')),
                );
              },
            ),
          ),
        ],
      ),

在此处输入图片说明

Update:更新:

Whole page Is Scroll-able with - SingleChildScrollView.整个页面可以使用 - SingleChildScrollView.滚动SingleChildScrollView.

body: SingleChildScrollView(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
      Text(
        'Headline',
        style: TextStyle(fontSize: 18),
      ),
      SizedBox(
        height: 200.0,
        child: ListView.builder(
          physics: ClampingScrollPhysics(),
          shrinkWrap: true,
          scrollDirection: Axis.horizontal,
          itemCount: 15,
          itemBuilder: (BuildContext context, int index) => Card(
                child: Center(child: Text('Dummy Card Text')),
              ),
        ),
      ),
      Text(
        'Demo Headline 2',
        style: TextStyle(fontSize: 18),
      ),
      Card(
        child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
      ),
      Card(
        child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
      ),
      Card(
        child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
      ),
      Card(
        child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
      ),
      Card(
        child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
      ),
    ],
  ),
),

在此处输入图片说明

Screenshot:截屏:

在此处输入图片说明


class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemCount: 7,
        itemBuilder: (_, i) {
          if (i < 2)
            return _buildBox(color: Colors.blue);
          else if (i == 3)
            return _horizontalListView();
          else
            return _buildBox(color: Colors.blue);
        },
      ),
    );
  }

  Widget _horizontalListView() {
    return SizedBox(
      height: 120,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemBuilder: (_, __) => _buildBox(color: Colors.orange),
      ),
    );
  }

  Widget _buildBox({Color color}) => Container(margin: EdgeInsets.all(12), height: 100, width: 200, color: color);
}

We have to use SingleScrollView inside another SingleScrollView , using ListView will require fixed height我们必须使用SingleScrollView另一个里面SingleScrollView ,使用ListView需要固定的高度

SingleChildScrollView(
   child: Column(
      children: <Widget>[
        SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
          children: [Text('H1'), Text('H2'), Text('H3')])),
        Text('V1'),
        Text('V2'),
        Text('V3')]))

To solved the vertical ListView in the horizontal ListView this way以这种方式解决水平ListView中垂直ListView

ListView.builder(
  itemBuilder: (context, index) =>
      Column(children: [
        _userProfileLayout(reviewPage.items[index]),
        Container(
          height: 100,
          child:
          ListView.builder( scrollDirection: Axis.horizontal,
              itemBuilder: (context, index) => Text("dddd")),
        ),
        FadeInImage.assetNetwork(
            placeholder: "images/default_review_img.png",
            image:
            "${reviewPage.items[index].multimedias[0]
                .url}"),
        _placeLayout(reviewPage.items[index])
      ]),
)
 

Also you can do it like this way To Switch to a Row inside a SingleChildScrollview :您也可以像这样切换到SingleChildScrollview内的Row

ListView.builder(
  itemCount: 3,
  scrollDirection: Axis.vertical,
  itemBuilder: (context, position) {
    if (position == 0) {
      return Container(
        child: Text("First rwo"),
      );
    } else if (position == 1) {
      return Container(
        child: Text("second  rwo"),
      );
    } else if (position == 2) {
      return SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Row(
          children: [Text("List"), Text("List"), Text("List"), Text("List")],
        ),
      );
    }
  },
)

Second one credit is Jordan Davies第二个功劳是乔丹戴维斯

I tried in this code and I fixed my problem I hope solved your want it.我在这段代码中试过,我解决了我的问题,希望能解决你想要的问题。

       SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: [
              item(),
              item(),
              item(),
              item(),
            ],
          ),
        ),

If someone gets the renderview port was exceeded error.如果有人得到渲染视图端口被超出错误。 warp your ListView in a Container widget and give it the height and width property to fix the issue在 Container 小部件中扭曲您的 ListView 并为其提供高度和宽度属性以解决问题

Column(
      children: <Widget>[
        Text(
          Strings.dailyTasks,
        ),
      Container(
       height: 60,
       width: double.infinity,
        child: ListView.builder(
          scrollDirection: Axis.horizontal,
          itemCount: tasks.length,
          itemBuilder: (BuildContext context, int index) =>
            taskCard(
              taskNumber: index + 1,
              taskTotal: tasks.length,
              task: tasks[index],
             ),
         ),
       )
      ]
)

for Web Chome you have to add MaterialScrollBehavior for horizontal scrolling to work.对于 Web Chome,您必须添加 MaterialScrollBehavior 才能使水平滚动工作。 see( Horizontal listview not scrolling on web but scrolling on mobile ) I demonstrate how to use the scrollcontroller to animate the list both left and right.请参阅( 水平列表视图不在 web 上滚动但在移动设备上滚动)我演示了如何使用滚动控制器为左右列表设置动画。

import 'package:flutter/gestures.dart';
class MyCustomScrollBehavior extends MaterialScrollBehavior {
  // Override behavior methods and getters like dragDevices
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}

return MaterialApp(
      title: 'Flutter Demo',
      scrollBehavior: MyCustomScrollBehavior(),
)


class TestHorizontalListView extends StatefulWidget {
  TestHorizontalListView({Key? key}) : super(key: key);


  @override
  State<TestHorizontalListView> createState() => _TestHorizontalListViewState();
}

class _TestHorizontalListViewState extends State<TestHorizontalListView> {
  List<String> lstData=['A','B','C','D','E','F','G'];
  
  final ScrollController _scrollcontroller = ScrollController();

_buildCard(String value)
{
  return Expanded(child:Container(
    margin: const EdgeInsets.symmetric(vertical: 20.0),
    width:300,height:400,child:Card(child: Expanded(child:Text(value,textAlign: TextAlign.center, style:TextStyle(fontSize:30))),)));
}

void _scrollRight() {
    _scrollcontroller.animateTo(
      _scrollcontroller.position.maxScrollExtent,
      duration: Duration(seconds: 1),
      curve: Curves.fastOutSlowIn,
    );
  }

void _scrollLeft() {
    _scrollcontroller.animateTo(
      0,
      duration: Duration(seconds: 1),
      curve: Curves.fastOutSlowIn,
    );
  }
_segment1()
{
  return     SingleChildScrollView(child:
    Expanded(child:
        Container(height:300,
          width:MediaQuery.of(context).size.width,
          child:Row(children: [
          FloatingActionButton.small(onPressed: _scrollRight, child: const Icon(Icons.arrow_right),),
          Expanded(child:Scrollbar(child:ListView.builder(
            itemCount: lstData.length,
            controller: _scrollcontroller,
            scrollDirection: Axis.horizontal,
            itemBuilder:(context,index)
            {
              return _buildCard(lstData[index]);
            })
          ,),
        ),
        FloatingActionButton.small(onPressed: _scrollLeft, child: const Icon(Icons.arrow_left),),
    ]))
    ,
    )
    );

}
@override
  void initState() {
    //   TODO: implement initState
    super.initState();

  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text("horizontal listview",)),body: 
    segment1(),
);
  }
}

You just have to fix your height of your Listview (by wrapping it in a SizedBox for example).您只需要修复Listview的高度(例如,通过将其包装在SizedBox中)。

This is because the content of your listview can't be known before the frame is drawn.这是因为在绘制框架之前无法知道列表视图的内容。 Just imagine a list of hundreds of items.. There is no way to directly know the maximum height among all of them.想象一下数百个项目的列表。没有办法直接知道所有项目的最大高度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM