将 ListView 的最后一个元素固定到屏幕底部

Fix last element of ListView to the bottom of screen

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

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

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

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

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

设计:

您可以简单地使用 ListView to manage the "17" navigation options. Wrap this ListView inside an ColumnListView 将是 Column 的第一个 child,第二个 child,因此放在底部,将是您的注销操作。

如果您使用的是透明小部件(如 ListTile) inside your ListView to display the navigation options, you can simply wrap it inside a 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 坚持到 ListView 的末尾,您需要做两件事:

  1. 替换Expanded with an Flexible
  2. ListView
  3. 里面设置shrinkWrap: true

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

为此,我写了这个简短的片段:

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

如果 ListView 超出其限制,它将 return 为真。现在我们可以根据 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: () => {})
        ],
      ),
    );
  }
}