Flutter垂直PageView动态高度

问题描述 投票:0回答:1

我正在尝试制作具有垂直捕捉滚动的入门屏幕。问题是小部件的高度不相等,我想让它尽可能占用最小的空间。 Result i'm trying to get

我找到了这个包Expandable_page_view,但它不支持垂直滚动。

简而言之,我想要这个Expandable_page_view,但是是垂直的。

flutter expandablelistview vertical-scrolling flutter-pageview
1个回答
0
投票

对于带有快速滚动的垂直动态项目高度,我已经解决了这个问题

import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Snap List Demo',
      home: MyHomePage(),
    );
  }
}

final List<GlobalKey> itemKeys = List.generate(20, (index) => GlobalKey());

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  final List<GlobalKey> itemKeys = List.generate(20, (index) => GlobalKey());
  final List<double> itemOffsets = [];
  double totalHeight = 0;

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Snap List with Dynamic Heights'),
      ),
      body: ListView.builder(
        physics: SnapScrollPhysics(itemOffsets: itemOffsets),
        itemCount: 20,
        itemBuilder: (context, index) {
          return MeasureSize(
            key: itemKeys[index],
            onSizeChanged: (size) {
              if (index >= itemOffsets.length) {
                itemOffsets.add(totalHeight);
                totalHeight += size.height;
              }
            },
            child: Container(
              height: (index % 3 == 0)
                  ? size.height * 0.5
                  : (index % 2 == 0)
                      ? size.height * 0.25
                      : size.height * 0.8,
              color: Colors.grey[(index % 10 + 1) * 100],
              child: Center(child: Text('Item $index')),
            ),
          );
        },
      ),
    );
  }
}

class SnapScrollPhysics extends ScrollPhysics {
  final List<double> itemOffsets;

  const SnapScrollPhysics({required this.itemOffsets, ScrollPhysics? parent})
      : super(parent: parent);

  @override
  ScrollPhysics applyTo(ScrollPhysics? ancestor) {
    return SnapScrollPhysics(itemOffsets: itemOffsets, parent: parent);
  }

  double _getSnapTarget(double offset) {
    if (itemOffsets.isEmpty) return 0.0;

    final distances = itemOffsets.map((e) => (e - offset).abs()).toList();
    final minDistance = distances.reduce(min);
    final closestIndex = distances.indexOf(minDistance);

    return itemOffsets[closestIndex];
  }

  @override
  Simulation createBallisticSimulation(
      ScrollMetrics position, double velocity) {
    final tolerance = toleranceFor(position);
    final targetPosition = _getSnapTarget(position.pixels);

    if (velocity.abs() < tolerance.velocity) {
      return ScrollSpringSimulation(
        spring,
        position.pixels,
        targetPosition,
        velocity,
        tolerance: tolerance,
      );
    }
    return super.createBallisticSimulation(position, velocity) ??
        ScrollSpringSimulation(
          spring,
          position.pixels,
          targetPosition,
          velocity,
          tolerance: tolerance,
        );
  }
}

typedef OnSizeChanged = void Function(Size size);
class MeasureSize extends StatefulWidget {
  final OnSizeChanged onSizeChanged;
  final Widget child;

  const MeasureSize({
    Key? key,
    required this.onSizeChanged,
    required this.child,
  }) : super(key: key);

  @override
  MeasureSizeState createState() => MeasureSizeState();
}

class MeasureSizeState extends State<MeasureSize> {
  Size? oldSize;
  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) => _checkSize());
    return widget.child;
  }

  _checkSize() {
    final size = (context.findRenderObject() as RenderBox).size;
    if (oldSize != size) {
      widget.onSizeChanged(size);
    }
    oldSize = size;
  }
}

© www.soinside.com 2019 - 2024. All rights reserved.