Flutter: adjust height of PageView/Horizontal ListView based on current child

有些话、适合烂在心里 提交于 2020-12-01 08:59:22

问题


I'm trying to create a carousel with items of variable heights. When using PageView or ListView with horizontal scrolling, I need to give it a constant height, like this:

class CarouselVariableHightState extends State<CarouselVariableHight> {
  double height = 200;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Carousel')),
      body: ListView(
        children: <Widget>[
          Text('My Carousel'),
          Container(
            height: height,
            child: PageView(
              children: <Widget>[
                _buildCarouselItem(),
                _buildCarouselItem(),
                _buildCarouselItem(),
                _buildCarouselItem(),
              ],
            ),
          ),
          Text('end of List')
        ],
      ),
    );
  }

  Widget _buildCarouselItem() {
    return Column(
      children: [
        Container(
          color: Colors.red,
          height: Random().nextInt(200).toDouble(),
          child: Text('Text with random length')
        )
      ]
    );
  }
}

But my items contain text and I don't know the length of it. I don't wan't to limit the height of a CarouselItem because it might cut off the text. How could I have something like a PageView, that adjust its own size based on the currently shown item?

I thought a solution could be to get the height of the CarouselItem and then set it to the height of the PageView. However I couldn't figure out a way to get the height my current Carousel item.

Any Ideas?


回答1:


There are 2 possible solutions: right one and hacky one.

  1. The Right solution: Because PageView is trying to get all posible height, we can't use it inside of any widget, that dont' have height. So if we don't have height we need to use another widget, for example StackWidget and re-create PageView behavior: swipes, animations etc. It won't be a big library, if you want to make just simple things, less than 200-300 lines I guess.

  2. The hacky solution is to check the height of all elements before you build the PageView widget. You cannot change the height of the elements when PageView is build. So if you need to have for example a server request make it before you render the carusel.

To get the height, it easier to use IndexedStack, becouse it has the height of the highest element, but show only the first one (by default).

  • use keys to get the height of IndexedStack
  • use setState to save it.
  • use obtained height as the height of PageView.

class CarouselVariableHight extends StatefulWidget {
  @override
  CarouselVariableHightState createState() => CarouselVariableHightState();
}

final widgetHeights = <double>[
  Random().nextInt(300).toDouble(),
  Random().nextInt(300).toDouble(),
  Random().nextInt(300).toDouble(),
  Random().nextInt(300).toDouble(),
];

class CarouselVariableHightState extends State<CarouselVariableHight> {
  double height;
  GlobalKey stackKey = GlobalKey();
  bool widgetHasHeigh;

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
    super.initState();
  }

  _afterLayout(_) {
    final RenderBox renderBoxRed = stackKey.currentContext.findRenderObject();
    final sizeRed = renderBoxRed.size;
    setState(() {
      height = sizeRed.height;
    });
  }

  final caruselItems = widgetHeights
      .map((height) => Column(children: [
            Container(
                color: Colors.red,
                height: Random().nextInt(300).toDouble(),
                child: Text('Text with random length'))
          ]))
      .toList();

  _buildStack() {
    Widget firstElement;
    if (height == null) {
      firstElement = Container();
    } else {
      firstElement = Container(
        height: height,
        child: PageView(
          children: caruselItems,
        ),
      );
    }

    return IndexedStack(
      key: stackKey,
      children: <Widget>[
        firstElement,
        ...caruselItems,
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Carousel')),
      body: ListView(
        children: <Widget>[
          Text('My Carousel'),
          _buildStack(),
          Text('end of List'),
        ],
      ),
    );
  }
}


来源:https://stackoverflow.com/questions/54522980/flutter-adjust-height-of-pageview-horizontal-listview-based-on-current-child

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!