首页 > 解决方案 > 展开图块时如何滚动ExpansionTile / Listview?

问题描述

我们有一堆ExpansionTile内一个ListView(到目前为止还不算太疯狂,对吧?)。我们的用户希望我们在打开列表时“自动滚动”列表,ExpansionTile这样新的展开图块现在位于屏幕的顶部(或尽可能位于顶部),这样他们就不必手动滚动查看我们刚刚打开的。

我认为我们需要类似on-- 并注册一个ScrollControlleronExpansionChanged ListView.. 来做一些事情。这对我来说有点模糊。:)

任何有关如何执行此操作的指示或帮助将不胜感激!

蒂亚!

标签: dartflutterflutter-layout

解决方案


如果我得到了你的要求,基本上是一种自动ExpansionTile向上滚动的方式,这样用户就不必手动查看它的内容,对吧?

然后,您实际上可以ScrollController在您的中使用 aListView并利用 的onExpansionChanged回调ExpansionTile根据磁贴的大小与其当前位置相应地向上/向下滚动它。

但现在你想知道:我怎么知道要滚动多少?

为此,您可以通过给它一些约束(例如构建一个或在这种特定情况下,您不确切知道高度将需要Container(heigh: 100.0))多少像素。所以,ExpansionTile我们可以使用 aGlobalKey然后通过RenderBox在运行时检查它来检查它的渲染高度,以确切知道它在折叠时占用了多少像素,并将它乘以它的索引。

  ExpansionTile _buildExpansionTile(int index) {
    final GlobalKey expansionTileKey = GlobalKey();
    double previousOffset;

    return ExpansionTile(
      key: expansionTileKey,
      onExpansionChanged: (isExpanded) {
        if (isExpanded) previousOffset = _scrollController.offset;
        _scrollToSelectedContent(isExpanded, previousOffset, index, expansionTileKey);
      },
      title: Text('My expansion tile $index'),
      children: _buildExpansionTileChildren(),
    );
  }

好的,现在我们有了一个ListView_buildExpansionTile在其生成器中使用它的GlobalKey. 我们还保存了ListView展开图块时的滚动偏移量。但是,我们如何真正向上滚动以显示其内容?让我们看看_scrollToSelectedContent方法。

  void _scrollToSelectedContent(bool isExpanded, double previousOffset, int index, GlobalKey myKey) {
    final keyContext = myKey.currentContext;

    if (keyContext != null) {
      // make sure that your widget is visible
      final box = keyContext.findRenderObject() as RenderBox;
      _scrollController.animateTo(isExpanded ? (box.size.height * index) : previousOffset,
          duration: Duration(milliseconds: 500), curve: Curves.linear);
    }
  }

因此,我们通过其键访问渲染对象,然后使用ListView滚动控制器在展开时向上滚动,在折叠时通过将其高度乘以其索引来向下滚动回其初始位置。您不必向后滚动,这只是我对这个示例的个人偏好。这将导致如下结果:

例子

在这里你有一个完整的例子StatefulWidget

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final ScrollController _scrollController = ScrollController();

  void _scrollToSelectedContent(bool isExpanded, double previousOffset, int index, GlobalKey myKey) {
    final keyContext = myKey.currentContext;

    if (keyContext != null) {
      // make sure that your widget is visible
      final box = keyContext.findRenderObject() as RenderBox;
      _scrollController.animateTo(isExpanded ? (box.size.height * index) : previousOffset,
          duration: Duration(milliseconds: 500), curve: Curves.linear);
    }
  }

  List<Widget> _buildExpansionTileChildren() => [
        FlutterLogo(
          size: 50.0,
        ),
        Text(
          'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse vulputate arcu interdum lacus pulvinar aliquam. Donec ut nunc eleifend, volutpat tellus vel, volutpat libero. Vestibulum et eros lorem. Nam ut lacus sagittis, varius risus faucibus, lobortis arcu. Nullam tempor vehicula nibh et ornare. Etiam interdum tellus ut metus faucibus semper. Aliquam quis ullamcorper urna, non semper purus. Mauris luctus quam enim, ut ornare magna vestibulum vel. Donec consectetur, quam a mattis tincidunt, augue nisi bibendum est, quis viverra risus odio ac ligula. Nullam vitae urna malesuada magna imperdiet faucibus non et nunc. Integer magna nisi, dictum a tempus in, bibendum quis nisi. Aliquam imperdiet metus id metus rutrum scelerisque. Morbi at nisi nec risus accumsan tempus. Curabitur non sem sit amet tellus eleifend tincidunt. Pellentesque sed lacus orci.',
          textAlign: TextAlign.justify,
        ),
      ];

  ExpansionTile _buildExpansionTile(int index) {
    final GlobalKey expansionTileKey = GlobalKey();
    double previousOffset;

    return ExpansionTile(
      key: expansionTileKey,
      onExpansionChanged: (isExpanded) {
        if (isExpanded) previousOffset = _scrollController.offset;
        _scrollToSelectedContent(isExpanded, previousOffset, index, expansionTileKey);
      },
      title: Text('My expansion tile $index'),
      children: _buildExpansionTileChildren(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('MyScrollView'),
      ),
      body: ListView.builder(
        controller: _scrollController,
        itemCount: 100,
        itemBuilder: (BuildContext context, int index) => _buildExpansionTile(index),
      ),
    );
  }
}

推荐阅读