【问题标题】:Fix last element of ListView to the bottom of screen将 ListView 的最后一个元素固定到屏幕底部
【发布时间】:2019-08-18 22:05:49
【问题描述】:

我正在尝试使用 Flutter 实现自定义导航抽屉。我想在抽屉底部附加注销选项。问题是注销选项上方的元素数量是未知的(从 3 到 17)。

因此,如果这些小部件占据抽屉空间的一半,则注销选项将位于底部,如果它们太多并且您必须滚动才能看到它们,那么注销选项将做最后一个。

我还尝试将前两个选项设置为绿色背景色。你会推荐我哪个小部件树?我想到了ListView 小部件,它将小部件列表作为构造函数中的参数。

因此我可以解决前两项的不同背景颜色。但我仍然不知道如何将注销选项附加到底部。在这种情况下,它位于抽屉的底部,但它可能会发生,其他选项将大于屏幕尺寸,在这种情况下,它应该放在整个列表的底部。

编辑:我在问题中添加了一个设计。注销选项是称为 Odhlášení 的选项。在这种情况下,它位于抽屉的底部,但它可能会发生,其他选项将大于屏幕尺寸,在这种情况下,它应该放在整个列表的底部。

设计:

【问题讨论】:

  • 你试过什么?有代码吗?
  • 嘿,我还没有任何代码。我只是在考虑布局,我想不出任何东西。我也没有要求任何具体的代码,我所需要的只是提示要使用哪些小部件。
  • 您可以随时将您的图片上传到图片托管网站(如Imgur)并附上链接。
  • 感谢更新,我现在明白你的意思了@EllaGogo

标签: flutter navigation-drawer flutter-layout


【解决方案1】:

您可以简单地使用ListView 来管理“17”导航选项。将此ListView 包裹在Column 中。 ListView 将是Column 第二个孩子的第一个孩子,因此放置在底部,将是您的注销操作。

如果您在ListView 中使用透明小部件(如ListTile)来显示导航选项,您可以简单地将其包装在Container 中。除了许多其他小部件之外,Container 还允许您使用其color 属性设置新的背景颜色。

使用这种方法,小部件树将如下所示:

- Column                 // Column to place your LogutButton always below the ListView
  - ListView             // ListView to wrap all your navigation scrollable
    - Container          // Container for setting the color to green
      - GreenNavigation
    - Container
      - GreenNavigation
    - Navigation
    - Navigation
    - ...
  - LogOutButton

更新 1 - 粘性 LogOutButton : 要实现LogOutButton 粘贴到ListView 的末尾,您需要做两件事:

  1. Expanded 替换为Flexible
  2. ListView 内设置shrinkWrap: true

更新 2 - 间隔 LogOutButton 直到大列表: 实现所描述的行为是一个更困难的步骤。您必须检查ListView 是否超出屏幕并且可滚动。

为此,我写了这个简短的 sn-p:

  bool isListLarge() {
    return controller.positions.isNotEmpty && physics.shouldAcceptUserOffset(controller.position);
  }

如果ListView 超出其限制,它将返回true。现在我们可以根据isListViewLarge 的结果刷新视图的状态。下面又是一个完整的代码示例。


独立代码示例(更新 2:间隔 LogOutButton 直到大列表):

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        drawer: MyDrawer(),
      ),
    );
  }
}

class MyDrawer extends StatefulWidget {
  @override
  _MyDrawerState createState() => _MyDrawerState();
}

class _MyDrawerState extends State<MyDrawer> {
  ScrollController controller = ScrollController();
  ScrollPhysics physics = ScrollPhysics();

  int entries = 4;

  @override
  Widget build(BuildContext context) {
    Widget logout = IconButton(
        icon: Icon(Icons.exit_to_app),
        onPressed: () => {setState(() => entries += 4)});

    List<Widget> navigationEntries = List<int>.generate(entries, (i) => i)
        .map<Widget>((i) => ListTile(
              title: Text(i.toString()),
            ))
        .toList();

    if (this.isListLarge()) {  // if the List is large, add the logout to the scrollable list
      navigationEntries.add(logout);
    }

    return Drawer(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,  // place the logout at the end of the drawer
        children: <Widget>[
          Flexible(
            child: ListView(
              controller: controller,
              physics: physics,
              shrinkWrap: true,
              children: navigationEntries,
            ),
          ),
          this.isListLarge() ? Container() : logout // if the List is small, add the logout at the end of the drawer
        ],
      ),
    );
  }

  bool isListLarge() {
    return controller.positions.isNotEmpty && physics.shouldAcceptUserOffset(controller.position);
  }
}

独立代码示例(更新 1:Sticky LogOutButton):

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        drawer: MyDrawer(),
      ),
    );
  }
}

class MyDrawer extends StatefulWidget {
  @override
  _MyDrawerState createState() => _MyDrawerState();
}

class _MyDrawerState extends State<MyDrawer> {
  int entries = 4;

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        children: <Widget>[
          Flexible(
            child: ListView(
              shrinkWrap: true,
              children: List<int>.generate(entries, (i) => i)
                  .map((i) => ListTile(
                        title: Text(i.toString()),
                      ))
                  .toList(),
            ),
          ),
          IconButton(
              icon: Icon(Icons.exit_to_app),
              onPressed: () => {setState(() => entries += 4)})
        ],
      ),
    );
  }
}

独立代码示例(旧:粘在底部):

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        drawer: MyDrawer(),
      ),
    );
  }
}

class MyDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        children: <Widget>[
          Expanded(
            child: ListView(
              children: List<int>.generate(40, (i) => i + 1)
                  .map((i) => ListTile(
                        title: Text(i.toString()),
                      ))
                  .toList(),
            ),
          ),
          IconButton(icon: Icon(Icons.exit_to_app), onPressed: () => {})
        ],
      ),
    );
  }
}

【讨论】:

  • 您好,非常感谢您的回答,但这不是我想要实现的目标。在您的示例中,注销选项高于其他选项,我希望它与其他所有选项位于同一层。如果列表中的其他选项超过屏幕高度,则注销选项将是该列表中的最后一个元素。但是如果假设只有 4 个选项,那么注销选项仍然是最后一个,但它也会附加到屏幕底部。你明白我的意思吗?
  • 好的,没问题,我们可以直接使用shrinkWrap。我会更新我的答案。
  • (并将Expanded 替换为Flexible
  • 感谢更新,我们肯定会更接近我想要的。我设法将抽屉设计上传到 imgur。这是链接imgur.com/a/nFK4ap0
  • 那么哪些问题让我的答案对您开放?我看了你的设计,唯一真正的区别是顶部的绿色背景?
猜你喜欢
  • 1970-01-01
  • 2013-07-29
  • 2013-01-29
  • 2022-11-18
  • 2017-06-27
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多