Flutter灵活布局,垂直溢出
Flutter flexible layout, vertical overflow
我正在开始我的第一个真实世界的 flutter 应用程序,到目前为止,我还不能全神贯注于如何解决我所拥有的这个非常基本的用例。
网上找了各种资料,但是none还真的解决了。这个用例对我来说似乎很常见,所以必须有一个很好的标准方法来做到这一点。
我有一个栏布局。为了说明这一点,有一个徽标(蓝色)、一个表单(红色)、一个按钮(绿色)和底部的一些自定义导航(黑色)。
代码如下:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 300.0,
),
),
const Spacer(),
Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
)
],
),
),
),
);
}
}
我在 Pixel 5 模拟器上运行。尺寸不是 100% 准确,但足以解释我认为的情况。
此屏幕是具有类似屏幕的流程的一部分,因此我想从底部向上布局自定义导航和按钮。 IE。当我导航到下一个屏幕时,我希望按钮和导航位于同一位置。
从上到下的标志和主要内容也是如此。因此,我在中间放了一个 spacer,它将占用中间的 space。
问题。我在表单中进行了一些验证。当按下按钮并破坏验证时,表单将在每个字段下方显示一堆消息。我通过将表单占位符的高度增加到 500 来模拟这一点。
所以现在所有的组件都不再适合屏幕了,我在底部有一些溢出。没有什么奇怪的。
在研究解决这个问题的最惯用方法时,我发现它是添加一个有意义的 SingleChildScrollView
。
但是添加 SingleChildScrollView
会破坏代码,因为我在那里有一个 Spacer
。我理解那部分。既然我们现在说“尽可能多地占用 space,我会让它滚动”,我们有无限的 space 供我们使用。同时 Spacer
基本上说,“我会尽可能地把所有东西都压低,直到没有 space 剩下”。所以这也是有道理的。让我们删除 spacer.
代码如下:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 500.0,
),
),
Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
)
],
),
),
),
),
);
}
}
它按预期工作。我没有收到任何错误,我可以滚动查看按钮和导航。
现在总结一下。当我的表单恢复到原来的高度 300px 时会发生什么?当然,因为 spacer 被删除了,我不再有我想要的那种行为,按钮和导航是从底部开始的。他们现在被拉到表格中。当然,这也是有道理的。
我明白为什么所有不同的场景都会如此。但是我怎样才能创建一个灵活的布局,其中一些东西从底部定位,一些从顶部定位,但仍然防止溢出?
我是否必须开始计算何时以及是否会发生溢出并动态添加滚动视图?我还没有走那条路,因为我希望有一种更明确、更标准的方式来处理这个问题。
这应该可以解决问题:
return Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: ListView(children: [
Text('Logo'),
SizedBox(height: 1000,), //large placeholder
Text('bottom')
],)),
ElevatedButton(onPressed: () {}, child: Text('Your button'))
],
),
);
所以基本上你开始用你的按钮从底部填充你的屏幕,然后你用扩展的小部件填充剩余的可用 space,然后你用像 ListView 这样的可滚动小部件填充(对于多个子部件,这比 SingleChildscrollView 更好。
要将此概念应用到您的代码中:
return Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 500.0,
),
),
],
),
),
),
),
Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
)
],
),
),
);
试试下面的代码希望对你有帮助。将您的专栏添加到 SingleChildScrollView
Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: SingleChildScrollView(
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 900.0,
),
),
SingleChildScrollView(
child: Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
),
),
],
),
),
),
),
// If you want your bottom widget is constant then used bottomSheet (uncomment below bottomSheet code)
/*
bottomSheet: Container(
height: 40,
color: Colors.red,
),
*/
);
我正在开始我的第一个真实世界的 flutter 应用程序,到目前为止,我还不能全神贯注于如何解决我所拥有的这个非常基本的用例。
网上找了各种资料,但是none还真的解决了。这个用例对我来说似乎很常见,所以必须有一个很好的标准方法来做到这一点。
我有一个栏布局。为了说明这一点,有一个徽标(蓝色)、一个表单(红色)、一个按钮(绿色)和底部的一些自定义导航(黑色)。
代码如下:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 300.0,
),
),
const Spacer(),
Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
)
],
),
),
),
);
}
}
我在 Pixel 5 模拟器上运行。尺寸不是 100% 准确,但足以解释我认为的情况。
此屏幕是具有类似屏幕的流程的一部分,因此我想从底部向上布局自定义导航和按钮。 IE。当我导航到下一个屏幕时,我希望按钮和导航位于同一位置。
从上到下的标志和主要内容也是如此。因此,我在中间放了一个 spacer,它将占用中间的 space。
问题。我在表单中进行了一些验证。当按下按钮并破坏验证时,表单将在每个字段下方显示一堆消息。我通过将表单占位符的高度增加到 500 来模拟这一点。
所以现在所有的组件都不再适合屏幕了,我在底部有一些溢出。没有什么奇怪的。
在研究解决这个问题的最惯用方法时,我发现它是添加一个有意义的 SingleChildScrollView
。
但是添加 SingleChildScrollView
会破坏代码,因为我在那里有一个 Spacer
。我理解那部分。既然我们现在说“尽可能多地占用 space,我会让它滚动”,我们有无限的 space 供我们使用。同时 Spacer
基本上说,“我会尽可能地把所有东西都压低,直到没有 space 剩下”。所以这也是有道理的。让我们删除 spacer.
代码如下:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 500.0,
),
),
Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
)
],
),
),
),
),
);
}
}
它按预期工作。我没有收到任何错误,我可以滚动查看按钮和导航。
现在总结一下。当我的表单恢复到原来的高度 300px 时会发生什么?当然,因为 spacer 被删除了,我不再有我想要的那种行为,按钮和导航是从底部开始的。他们现在被拉到表格中。当然,这也是有道理的。
我明白为什么所有不同的场景都会如此。但是我怎样才能创建一个灵活的布局,其中一些东西从底部定位,一些从顶部定位,但仍然防止溢出?
我是否必须开始计算何时以及是否会发生溢出并动态添加滚动视图?我还没有走那条路,因为我希望有一种更明确、更标准的方式来处理这个问题。
这应该可以解决问题:
return Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: ListView(children: [
Text('Logo'),
SizedBox(height: 1000,), //large placeholder
Text('bottom')
],)),
ElevatedButton(onPressed: () {}, child: Text('Your button'))
],
),
);
所以基本上你开始用你的按钮从底部填充你的屏幕,然后你用扩展的小部件填充剩余的可用 space,然后你用像 ListView 这样的可滚动小部件填充(对于多个子部件,这比 SingleChildscrollView 更好。
要将此概念应用到您的代码中:
return Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 500.0,
),
),
],
),
),
),
),
Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
)
],
),
),
);
试试下面的代码希望对你有帮助。将您的专栏添加到 SingleChildScrollView
Scaffold(
backgroundColor: Colors.amber,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: SingleChildScrollView(
child: Column(
children: [
const Placeholder(
color: Colors.blue,
fallbackHeight: 100.0,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Placeholder(
color: Colors.red,
fallbackHeight: 900.0,
),
),
SingleChildScrollView(
child: Column(
children: [
const Placeholder(
color: Colors.green,
fallbackHeight: 40.0,
),
Container(
margin: const EdgeInsets.only(top: 40.0),
child: const Placeholder(
color: Colors.black,
fallbackHeight: 40.0,
),
),
],
),
),
],
),
),
),
),
// If you want your bottom widget is constant then used bottomSheet (uncomment below bottomSheet code)
/*
bottomSheet: Container(
height: 40,
color: Colors.red,
),
*/
);