将 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 Column。 ListView
将是 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
的末尾,您需要做两件事:
更新 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: () => {})
],
),
);
}
}
我正在尝试使用 Flutter 实现自定义导航抽屉。我想将注销选项附加到抽屉底部。问题是注销选项上方的元素数量未知(从 3 到 17)。
因此,如果这些小部件占据抽屉的一半 space,那么注销选项将位于底部,如果它们太多,您必须滚动才能看到它们,那么注销选项将只是最后一个。
我也在尝试为前两个选项设置绿色背景色。你会推荐我哪个小部件树?我想到了 ListView 小部件,它将小部件列表作为构造函数中的参数。
因此我可以解决前两个项目的不同背景颜色。但我仍然不知道如何将注销选项附加到底部。在这种情况下,它位于抽屉的底部,但也可能发生其他选项大于屏幕尺寸的情况,在这种情况下,它应该放在整个列表的底部。
编辑:我已经为问题添加了设计。注销选项是名为 Odhlášení 的选项。在这种情况下,它位于抽屉的底部,但也有可能其他选项会比屏幕尺寸大,在这种情况下,它应该放在整个列表的底部。
设计:
您可以简单地使用 ListView to manage the "17" navigation options. Wrap this ListView
inside an Column。 ListView
将是 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
的末尾,您需要做两件事:
更新 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: () => {})
],
),
);
}
}