为每个页面创建具有动态高度的 Flutter Carousel Slider

Posted

技术标签:

【中文标题】为每个页面创建具有动态高度的 Flutter Carousel Slider【英文标题】:Create a Flutter Carousel Slider with dynamic heigth for each page 【发布时间】:2020-07-27 04:45:06 【问题描述】:

我有一个Carousel Slider,带有一个虚线指示器 as shown in this image

滑块的每个页面都是一个 Card 小部件,其中包含一个 ListView 作为子项。

如果 Card 不在 Carousel 小部件内,它会随着 Listview 内的元素增加而展开。

我想为滑块的每一页保持这种行为,但是当我将卡片放入滑块时,它们不再根据内容调整大小,而是似乎有一个预先定义的高度(即使我没有指定任何高度!)。

如何动态更新滑块内卡片的高度?

这是我的代码:

main.dart 文件

import 'package:flutter/material.dart';
import 'carousel.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Slider App',
      home: MyHomePage(),
    );
  


class GeneralEvent 
  final String title;
  final String desc;
  GeneralEvent(this.title, this.desc);


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


final List<GeneralEvent> userEvents = [
  GeneralEvent(
    "List Item 1",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 2",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 3",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 4",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 5",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
];

class _MyHomePageState extends State<MyHomePage> 
  final List<Card> cards = [
    Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Card 1',
              style: TextStyle(fontSize: 24),
            ),
          ),
          Container(
            height: 1,
            width: double.infinity,
            color: Color.fromRGBO(0, 0, 0, 0.12),
          ),
          ListView.builder(
            physics: NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            itemBuilder: (ctx, index) 
              return ListTile(
                title: Text(
                  userEvents.sublist(0, 3)[index].title,
                ),
                subtitle: Text(userEvents.sublist(0, 3)[index].desc),
              );
            ,
            itemCount: userEvents.sublist(0, 3).length,
          ),
        ],
      ),
    ),
    Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              'Card 2',
              style: TextStyle(fontSize: 24),
            ),
          ),
          Container(
            height: 1,
            width: double.infinity,
            color: Color.fromRGBO(0, 0, 0, 0.12),
          ),
          ListView.builder(
            physics: NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            itemBuilder: (ctx, index) 
              return ListTile(
                title: Text(
                  userEvents.sublist(3)[index].title,
                ),
                subtitle: Text(userEvents.sublist(3)[index].desc),
              );
            ,
            itemCount: userEvents.sublist(3).length,
          ),
        ],
      ),
    ),
  ];

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Color.fromRGBO(235, 235, 235, 1),
      appBar: AppBar(title: Text('iWantASliderAPP')),
      body: Padding(
        padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
        child: LayoutBuilder(
          builder: (context, constraints) 
            return ListView(
              children: <Widget>[
                CarouselWithIndicator(cards),
                Padding(
                  padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
                  child: Text(
                    "if the Card is not in the slider it resize properly:",
                  ),
                ),
                cards[0]
              ],
            );
          ,
        ),
      ),
    );
  
 

carousel.dart

import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';

class CarouselWithIndicator extends StatefulWidget 
  final List cards;

  CarouselWithIndicator(this.cards);

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


class _CarouselWithIndicatorState extends State<CarouselWithIndicator> 
  List<T> map<T>(List list, Function handler) 
    List<T> result = [];
    for (var i = 0; i < list.length; i++) 
      result.add(handler(i, list[i]));
    
    return result;
  

  int _current = 0;

  @override
  Widget build(BuildContext context) 
    return Column(
      children: <Widget>[
        CarouselSlider(
          // height: // NO HEIGHT SPECIFIED!
          viewportFraction: 1.0,
          enlargeCenterPage: true,
          enableInfiniteScroll: false,
          onPageChanged: (index) 
            setState(() 
              _current = index;
            );
          ,
          items: widget.cards.map((card) 
            return Builder(
              builder: (BuildContext context) 
                return Container(
                  width: MediaQuery.of(context).size.width,
                  child: card,
                );
              ,
            );
          ).toList(),
        ),
        SizedBox(
          height: 5,
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: map<Widget>(widget.cards, (index, card) 
            return Container(
              width: 10.0,
              height: 10.0,
              margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 3.0),
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: _current == index ? Colors.grey : Color.fromRGBO(200, 200, 200, 1),
              ),
            );
          ),
        ),
      ],
    );
  

【问题讨论】:

【参考方案1】:

我终于在运行时高度确定的帮助下找到了解决方案。 运行时大小确定问题的链接:How to get height of a Widget?

只需定义一个高度变量并在 Measure size 的 onChanged 函数中更新它。

MeasureSizeRenderObject.dart

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

typedef void OnWidgetSizeChange(Size size);

class MeasureSizeRenderObject extends RenderProxyBox 
  Size oldSize;
  final OnWidgetSizeChange onChange;

  MeasureSizeRenderObject(this.onChange);

  @override
  void performLayout() 
    super.performLayout();

    Size newSize = child.size;
    if (oldSize == newSize) return;

    oldSize = newSize;
    WidgetsBinding.instance.addPostFrameCallback((_) 
      onChange(newSize);
    );
  


class MeasureSize extends SingleChildRenderObjectWidget 
  final OnWidgetSizeChange onChange;

  const MeasureSize(
    Key key,
    @required this.onChange,
    @required Widget child,
  ) : super(key: key, child: child);

  @override
  RenderObject createRenderObject(BuildContext context) 
    return MeasureSizeRenderObject(onChange);
  

导入上面的 dart 文件就可以了。

在我的例子中,我有一个日期列表和一个 Map 每天的工作时间。因此,每当添加一个新的营业时间时,ListView 的长度就会增加。

List _days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

地图 _operationalHours = ;

我正在从 Firebase Cloud Firestore 填充工作时间。

CarouselSlider 代码:

CarouselSlider.builder(
            itemCount: days.length,
            itemBuilder: (BuildContext context, int index, int realIdx) 
              return Card(
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(20)),
                color: Color(0xff3b3b3b),
                child: Container(
                  width: displayWidth(context),
                  padding: EdgeInsets.all(15.0),
                  child: Column(
                    children: [
                      Text(
                        days[index],
                        style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                            fontSize: 20),
                      ),
                      SizedBox(
                        height: 15,
                      ),
                      Column(
                        children: [
                           MeasureSize(
                                      onChange: (size) 
                                        height = size.height;
                                        print("log" + height.toString());
                                        setState(() );
                                      ,
                                      child: ListView.builder(
                                          shrinkWrap: true,
                                          itemCount: operationalHours[days[index]]
.length,
                                              itemBuilder: (context, i) 
                                                return getTimeSlot(
                                             operationalHours[days[index]][i]);
                                              ),
                                        )
                            ],
                          )
                        ],
                      ),
                    ),
                  );
                ,
                options: new CarouselOptions(
                    autoPlay: false,
                    viewportFraction: 1.0,
                    height: 100 + height,
                    enlargeCenterPage: false,
                    enableInfiniteScroll: false,
                    onPageChanged: (index, reason) 
                      setState(() 
                        _current = index;
                      );
                    ),
              ),

如果我在将代码从 IDE 复制到 *** 时出现任何拼写错误或错误,请告诉我。我会更新答案。

【讨论】:

在我的情况下高度不断增加 你能提供一个示例代码或github或任何其他链接到你的项目吗? 我使用了您提供的相同示例 哦,你的意思是每个轮播的高度都在增加,然后对于较小的轮播没有缩小? 它会在每次构建时添加您在 carouselOptions 中添加的 100 个像素和高度。【参考方案2】:

这是库的open issue,但您仍然可以通过以下代码部分实现。carousel.dart

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';

class CarouselWithIndicator extends StatefulWidget 
  final List cards;

  CarouselWithIndicator(this.cards);

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


class _CarouselWithIndicatorState extends State<CarouselWithIndicator> 
  List<T> map<T>(List list, Function handler) 
    List<T> result = [];
    for (var i = 0; i < list.length; i++) 
      result.add(handler(i, list[i]));
    
    return result;
  

  int _current = 0;

  @override
  Widget build(BuildContext context) 
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Wrap(
          children: <Widget>[
            CarouselSlider(
              height: double.maxFinite,
              viewportFraction: 1.0,
              enlargeCenterPage: true,
              enableInfiniteScroll: false,
              onPageChanged: (index) 
                setState(() 
                  _current = index;
                );
              ,
              items: widget.cards.map((card) 
                return Builder(
                  builder: (BuildContext context) 
                    return Container(
                      width: MediaQuery.of(context).size.width,
                      child: card,
                    );
                  ,
                );
              ).toList(),
            ),
          ],
        ),
        SizedBox(
          height: 5,
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: map<Widget>(widget.cards, (index, card) 
            return Container(
              width: 10.0,
              height: 10.0,
              margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 3.0),
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: _current == index
                    ? Colors.grey
                    : Color.fromRGBO(200, 200, 200, 1),
              ),
            );
          ),
        ),
      ],
    );
  

main.dart 文件

import 'package:flutter/material.dart';

import 'carousel.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Slider App',
      home: MyHomePage(),
    );
  


class GeneralEvent 
  final String title;
  final String desc;
  GeneralEvent(this.title, this.desc);


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


final List<GeneralEvent> userEvents = [
  GeneralEvent(
    "List Item 1",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 2",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 3",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 4",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 5",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
];

class _MyHomePageState extends State<MyHomePage> 
  List<Widget> cards = [
    Wrap(
      children: <Widget>[
        Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'Card 1',
                  style: TextStyle(fontSize: 24),
                ),
              ),
              Container(
                height: 1,
                width: double.infinity,
                color: Color.fromRGBO(0, 0, 0, 0.12),
              ),
              ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (ctx, index) 
                  return ListTile(
                    title: Text(
                      userEvents.sublist(0, 3)[index].title,
                    ),
                    subtitle: Text(userEvents.sublist(0, 3)[index].desc),
                  );
                ,
                itemCount: userEvents.sublist(0, 3).length,
              ),
            ],
          ),
        ),
      ],
    ),
    Wrap(
      children: <Widget>[
        Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'Card 2',
                  style: TextStyle(fontSize: 24),
                ),
              ),
              Container(
                height: 1,
                width: double.infinity,
                color: Color.fromRGBO(0, 0, 0, 0.12),
              ),
              ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (ctx, index) 
                  return ListTile(
                    title: Text(
                      userEvents.sublist(3)[index].title,
                    ),
                    subtitle: Text(userEvents.sublist(3)[index].desc),
                  );
                ,
                itemCount: userEvents.sublist(3).length,
              ),
            ],
          ),
        ),
      ],
    ),
  ];

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Color.fromRGBO(235, 235, 235, 1),
      appBar: AppBar(title: Text('iWantASliderAPP')),
      body: Padding(
        padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
        child: LayoutBuilder(
          builder: (context, constraints) 
            return ListView(
              children: <Widget>[
                Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    CarouselWithIndicator(cards),
                  ],
                ),
                Padding(
                  padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
                  child: Text(
                    "if the Card is not in the slider it resize properly:",
                  ),
                ),
                cards[0]
              ],
            );
          ,
        ),
      ),
    );
  

我希望这对您有所帮助。 :)

【讨论】:

【参考方案3】:

这是库中的一个已知问题。 这是lib Currently there is no way to define a dynamic height for each carousel item. But I think it can be done by creating a dynamic height container in the carousel item.的创建者的回答@

对于整个对话,请查看问题:https://github.com/serenader2014/flutter_carousel_slider/issues/106

【讨论】:

以上是关于为每个页面创建具有动态高度的 Flutter Carousel Slider的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 使用 Stack 或任何其他方法重叠具有动态高度的多个小部件

为每个具有类名的 div 动态创建按钮,并创建显示/隐藏单击功能

Flutter - 选项卡上的动态高度

具有 recyclerview 和 tablayout 的动态高度查看器

如何在带有 Flutter 的可滚动视图中拥有具有可变高度内容的 TabView?

如何为具有动态高度的表格单元格添加底部阴影?