我正在尝试创建高度可变的轮播。将PageView或ListView与水平滚动一起使用时,我需要给它一个恒定的高度,如下所示:
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')
)
]
);
}
}
但是我的物品包含文字,我不知道其长度。我不想限制CarouselItem的高度,因为它可能会截断文本。我该如何使用PageView之类的东西来根据当前显示的项目调整其自身大小?
我认为解决方案可能是获取CarouselItem的高度,然后将其设置为PageView的高度。但是我想不出一种方法来增加我当前的轮播商品的高度。
有什么想法吗?
答案 0 :(得分:4)
有2种可能的解决方案:正确的一种和hacky的一种。
正确的解决方案: 因为PageView试图获取所有可能的高度,所以我们不能在没有高度的任何小部件内使用它。因此,如果我们没有高度,则需要使用其他小部件,例如StackWidget并重新创建PageView行为:滑动,动画等。如果您只想制作简单的东西,而不会比我猜大概200-300行。
有问题的解决方案是在构建PageView小部件之前检查所有元素的高度。构建PageView时,不能更改元素的高度。因此,例如,如果您需要服务器请求,请在渲染转盘之前发出请求。
要获取高度,使用IndexedStack会更容易,因为它具有最高元素的高度,但是仅显示第一个元素(默认情况下)。
void OnActivate(RouterState previous, RouterState current) {
var search = Search.fromJson(current.queryParameters);
}
答案 1 :(得分:0)
下面您可以找到一个PageView的示例,该示例可以使其页面高度适应当前显示的子级。带有动画:
想法是:
SizeReportingWidget
。_heights
,该列表对应于孩子的索引。_currentPage
PageView
包裹在SizedBox
中,并赋予其当前显示子项的高度。我添加了一个方便的名为_currentHeight
的吸气剂来获取高度。SizedBox
包裹在TweenAnimationBuilder
中,以使高度变化平滑且具有动画效果,而不是弯曲的跳跃。ExpandablePageView
class ExpandablePageView extends StatefulWidget {
final List<Widget> children;
const ExpandablePageView({
Key key,
@required this.children,
}) : super(key: key);
@override
_ExpandablePageViewState createState() => _ExpandablePageViewState();
}
class _ExpandablePageViewState extends State<ExpandablePageView> with TickerProviderStateMixin {
PageController _pageController;
List<double> _heights;
int _currentPage = 0;
double get _currentHeight => _heights[_currentPage];
@override
void initState() {
_heights = widget.children.map((e) => 0.0).toList();
super.initState();
_pageController = PageController() //
..addListener(() {
final _newPage = _pageController.page.round();
if (_currentPage != _newPage) {
setState(() => _currentPage = _newPage);
}
});
}
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
curve: Curves.easeInOutCubic,
duration: const Duration(milliseconds: 100),
tween: Tween<double>(begin: _heights[0], end: _currentHeight),
builder: (context, value, child) => SizedBox(height: value, child: child),
child: PageView(
controller: _pageController,
children: _sizeReportingChildren
.asMap() //
.map((index, child) => MapEntry(index, child))
.values
.toList(),
),
);
}
List<Widget> get _sizeReportingChildren => widget.children
.asMap() //
.map(
(index, child) => MapEntry(
index,
OverflowBox(
//needed, so that parent won't impose its constraints on the children, thus skewing the measurement results.
minHeight: 0,
maxHeight: double.infinity,
alignment: Alignment.topCenter,
child: SizeReportingWidget(
onSizeChange: (size) => setState(() => _heights[index] = size?.height ?? 0),
child: Align(child: child),
),
),
),
)
.values
.toList();
}
SizeReportingWidget
class SizeReportingWidget extends StatefulWidget {
final Widget child;
final ValueChanged<Size> onSizeChange;
const SizeReportingWidget({
Key key,
@required this.child,
@required this.onSizeChange,
}) : super(key: key);
@override
_SizeReportingWidgetState createState() => _SizeReportingWidgetState();
}
class _SizeReportingWidgetState extends State<SizeReportingWidget> {
Size _oldSize;
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _notifySize());
return widget.child;
}
void _notifySize() {
final size = context?.size;
if (_oldSize != size) {
_oldSize = size;
widget.onSizeChange(size);
}
}
}