首页 > 解决方案 > 将 ListView 的最后一个元素固定到屏幕底部

问题描述

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

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

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

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

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

设计: 设计实例

标签: flutternavigation-drawerflutter-layout

解决方案


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

如果您在内部使用透明小部件(如ListTileListView来显示导航选项,则可以简单地将其包装在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 - Sticky LogOutButton : 要实现LogOutButton坚持到最后,ListView您需要做两件事:

  1. 灵活的替换扩展
  2. 设置shrinkWrap: true在里面ListView

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

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

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

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


独立代码示例(更新 2:Spaced LogOutButton until large List):

演示更新 2

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: () => {})
        ],
      ),
    );
  }
}

推荐阅读