将 ListView 的最后一个元素固定到屏幕底部 [英] Fix last element of ListView to the bottom of screen

查看:35
本文介绍了将 ListView 的最后一个元素固定到屏幕底部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我还尝试将前两个选项设置为绿色背景色.你会推荐我哪个小部件树?我想到了

解决方案

你可以简单地使用

import 'package:flutter/material.dart';无效的主要()=>运行应用程序(我的应用程序());MyApp 类扩展 StatelessWidget {@覆盖小部件构建(BuildContext 上下文){返回材料应用程序(家:脚手架(应用程序栏:应用程序栏(),抽屉:我的抽屉(),),);}}类 MyDrawer 扩展 StatefulWidget {@覆盖_MyDrawerState createState() =>_MyDrawerState();}类 _MyDrawerState 扩展状态<MyDrawer>{ScrollController 控制器 = ScrollController();ScrollPhysics 物理 = ScrollPhysics();int 条目 = 4;@覆盖小部件构建(BuildContext 上下文){小部件注销 = IconButton(图标:图标(Icons.exit_to_app),onPressed: () =>{setState(() => 条目 += 4)});列表<小部件>navigationEntries = List.generate(entries, (i) => i).map((i) => ListTile(标题:文本(i.toString()),)).toList();if (this.isListLarge()) {//如果 List 很大,则将注销添加到可滚动列表中navigationEntries.add(注销);}返回抽屉(孩子:列(mainAxisAlignment: MainAxisAlignment.spaceBetween,//将注销放在抽屉的末尾孩子们:<小部件>[灵活的(孩子:列表视图(控制器:控制器,物理学:物理学,收缩包装:是的,孩子:导航条目,),),this.isListLarge() ?Container() : logout//如果 List 很小,则在抽屉末尾添加 logout],),);}bool isListLarge() {返回 controller.positions.isNotEmpty &&物理.shouldAcceptUserOffset(controller.position);}}

<小时>

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

import 'package:flutter/material.dart';无效的主要()=>运行应用程序(我的应用程序());MyApp 类扩展 StatelessWidget {@覆盖小部件构建(BuildContext 上下文){返回材料应用程序(家:脚手架(应用程序栏:应用程序栏(),抽屉:我的抽屉(),),);}}类 MyDrawer 扩展 StatefulWidget {@覆盖_MyDrawerState createState() =>_MyDrawerState();}类 _MyDrawerState 扩展状态<MyDrawer>{int 条目 = 4;@覆盖小部件构建(BuildContext 上下文){返回抽屉(孩子:列(孩子们:<小部件>[灵活的(孩子:列表视图(收缩包装:是的,孩子:列表<int>.generate(entries, (i) => i).map((i) => ListTile(标题:文本(i.toString()),)).toList(),),),图标按钮(图标:图标(Icons.exit_to_app),onPressed: () =>{setState(() => 条目 += 4)})],),);}}

<小时>

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

import 'package:flutter/material.dart';无效的主要()=>运行应用程序(我的应用程序());MyApp 类扩展 StatelessWidget {@覆盖小部件构建(BuildContext 上下文){返回材料应用程序(家:脚手架(应用程序栏:应用程序栏(),抽屉:我的抽屉(),),);}}MyDrawer 类扩展 StatelessWidget {@覆盖小部件构建(BuildContext 上下文){返回抽屉(孩子:列(孩子们:<小部件>[展开(孩子:列表视图(孩子:列表<int>.generate(40, (i) => i + 1).map((i) => ListTile(标题:文本(i.toString()),)).toList(),),),IconButton(icon: Icon(Icons.exit_to_app), onPressed: () => {})],),);}}

I am trying to implement a custom navigation drawer using Flutter. I would like to attach log out option to the bottom of the drawer. The problem is that number of elements above log out option is unknow (from 3 to 17).

So if these widgets take half of the space of a drawer, then log out option will be on the bottom, and if there is too much of them and you have to scroll to see them all, then the log out option will be simply the last.

I am also trying to give the first two options a green background color. Which widget tree would you recommend me? I had a thought about the ListView widget, it takes List of widgets as an argument in constructor.

Therefore I can solve the different background color for the first two items. But I still can't figure out how to attach the log out option to the bottom. In this case it's at the bottom of drawer, but it can happen, that other options will be bigger than screen size and in that case, it should be placed at the bottom of whole list.

EDIT: I've add a design to the question. The logout option is the one called Odhlášení. In this case it's at the bottom of drawer, but it can happen, that other options will be bigger than the screen size and in that case, it should be placed at the bottom of whole list.

Design:

解决方案

You can simply use ListView to manage the "17" navigation options. Wrap this ListView inside an Column. The ListView will be the first child of the Column the second child, therefore placing at the bottom, will be your logout action.

If you are using transparent widgets (like ListTile) inside your ListView to display the navigation options, you can simply wrap it inside a Container. The Container, besides many other widgets, allows you to set a new background color with its color attribute.

Using this approach the widget tree would look like the following:

- 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


Update 1 - Sticky LogOutButton : To achieve the LogOutButton sticking to the end of the ListView you'll neeed to do two things:

  1. Replace the Expanded with an Flexible
  2. Set shrinkWrap: true inside the ListView


Update 2 - Spaced LogOutButton until large List: Achieving the described behavior is a more difficult step. You'll have to check if the ListView exceeds the screen and is scrollable.

To do this I wrote this short snippet:

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

It will return true if the ListView exceeds its limitations. Now we can refresh the state of the view, depending on the result of isListViewLarge. Below again a full code example.


Standalone code example (Update 2: Spaced LogOutButton until large List):

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);
  }
}


Standalone code example (Update 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)})
        ],
      ),
    );
  }
}


Standalone code example (Old: Sticking to bottom):

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

这篇关于将 ListView 的最后一个元素固定到屏幕底部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆