Flutter 中简直所有的货色都是一个小部件,当你编写小部件时,你能够构建一个布局。例如,您能够在列小部件中增加多个小部件以创立垂直布局。随着您持续增加更多小部件,您的 Flutter 应用程序布局将变得越简单。

在本文中,我将介绍一些在布局 Flutter 应用程序时要施行的最佳实际。

在 Flutter 中应用 SizedBox 代替 Container

有许多应用状况下,你须要应用占位符。让咱们看一下上面的例子:

return _isLoaded ? Container() : YourAwesomeWidget();

Container 是一个很棒的小部件,您将在 Flutter 中宽泛应用它。 Container() 扩大以适应父级提供的束缚,并且不是 const 构造函数。

另一方面,SizedBox 是一个构造函数,创立一个固定尺寸的盒子。宽度和高度参数能够为空,以示意盒子的尺寸不应受到相应维度的限度。

因而,当咱们实现占位符时,应该应用 SizedBox 而不是 Container

return _isLoaded ? SizedBox() : YourAwesomeWidget();

应用 if 条件而不是三元运算符语法

在布局 Flutter 应用程序时,通常须要有条件地渲染不同的小部件。您可能须要依据平台生成一个小部件,例如:

Row(  children: [    Text("Majid"),    Platform.isAndroid ? Text("Android") : SizeBox(),    Platform.isIOS ? Text("iOS") : SizeBox(),  ]);

在这种状况下,您能够删除三元运算符并利用 Dart 的内置语法在数组中增加 if 语句。

Row(  children: [    Text("Majid"),    if (Platform.isAndroid) Text("Android"),    if (Platform.isIOS) Text("iOS"),  ]);

您还能够应用扩大运算符扩大此性能,并依据须要加载多个小部件。

Row(  children: [    Text("Majid"),    if (Platform.isAndroid) Text("Android"),    if (Platform.isIOS) ...[      Text("iOS_1")      Text("iOS_2")    ],  ]);

思考 Flutter 中 build() 办法的老本

Flutter 小部件中的 build 办法可能会在先人小部件重建小部件时被频繁调用。防止在 build() 办法中反复和低廉的工作很重要。

例如,当您应用办法而不是在应用程序中创立小部件时。让我具体阐明:

class MyAwesomeWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Column(        mainAxisSize: MainAxisSize.min,        children: [          _buildHeaderWidget(),          _buildBodyWidget(context),          _buildFooterWidget(),        ],      ),    );  }  Widget _buildHeaderWidget() {    return Padding(      padding: const EdgeInsets.all(10.0),      child: FlutterLogo(          size: 50.0,      ),    );  }  Widget _buildBodyWidget(BuildContext context) {    return Expanded(      child: Container(        child: Center(          child: Text(            'Majid Hajian, Flutter GDE',          ),        ),      ),    );  }  Widget _buildFooterWidget() {    return Padding(      padding: const EdgeInsets.all(10.0),      child: Text('Footer'),    );  }}

这种办法的毛病是,当 MyAwesomeWidget 须要再次重建时--这可能会常常产生--所有在办法中创立的widget也会被重建,导致节约CPU周期,也可能节约内存。

因而,最好通过以下形式将这些办法转换为 StatelessWidgets

class MyAwesomeWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Column(        mainAxisSize: MainAxisSize.min,        children: [          HeaderWidget(),          BodyWidget(),          FooterWidget(),        ],      ),    );  }}class HeaderWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(10.0),      child: FlutterLogo(          size: 50.0,      ),    );  }}class BodyWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Expanded(      child: Container(        child: Center(          child: Text(            'Majid Hajian, Flutter GDE',          ),        ),      ),    );  }}class FooterWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(10.0),      child: Text('Footer'),    );  }}

所有 StatefulWidgetsStatelessWidgets,基于 key、widget 类型和属性,都有一个非凡的缓存机制,只在必要时重建。咱们甚至能够通过增加 const 来优化这些小部件,这将导致咱们进入本文的下一部分。

尽可能应用 const 小部件

在Dart中,尽可能应用 const 构造函数是很好的做法,记住编译器会优化你的代码。当初,让咱们回顾一下下面的例子。通过一个间接的步骤,咱们能够使 build 办法更无效地工作。

class MyAwesomeWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Column(        mainAxisSize: MainAxisSize.min,        children: [          const HeaderWidget(),          const BodyWidget(),          const FooterWidget(),        ],      ),    );  }}class HeaderWidget extends StatelessWidget {  const HeaderWidget();  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(10.0),      child: FlutterLogo(          size: 50.0,      ),    );  }}class BodyWidget extends StatelessWidget {  const BodyWidget();  @override  Widget build(BuildContext context) {    return Expanded(      child: Container(        child: Center(          child: Text(            'Majid Hajian, Flutter GDE',          ),        ),      ),    );  }}class FooterWidget extends StatelessWidget {  const FooterWidget();  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(10.0),      child: Text('Footer'),    );  }}

此更改可能看起来很简略,但它能够帮忙咱们防止从新构建 const 小部件。

在 ListView 中为长列表编码 itemExtent

为了了解如何最好地应用 itemExtent,假如咱们有一个有几千个元素的列表,当一个动作被触发时,例如点击一个按钮,咱们须要跳到最初一个元素。这时 itemExtent 能够极大地提高 ListView 的布局性能。

指定 itemExtent 比让 children元素 确定他们的范畴更无效,因为滚动机器能够应用对children元素范畴的预知来节俭工作,如下所示:

class LongListView extends StatelessWidget {  final _scrollController = ScrollController();  @override  Widget build(BuildContext context) {    return Scaffold(      floatingActionButton: FloatingActionButton(onPressed:() {        _scrollController.jumpTo(          _scrollController.position.maxScrollExtent,        );      }),      body: ListView(        controller: _scrollController,        children: List.generate(10000, (index) => Text('Index: $index')),        itemExtent: 400,      ),    );  }}

防止应用大Tree

对于何时将小部件拆分为较小的小部件,没有硬性规定。然而,最好防止应用大树,因为它有以下益处:

  • 促成可重用性
  • 提供更简洁的代码
  • 加强可读性
  • 启用封装
  • 提供缓存机制

因而,你应该尽可能地将你的代码宰割成不同的小部件。

理解 Flutter 中的束缚

每个 Flutter 开发人员都必须晓得的 Flutter 布局的黄金法令是:束缚降落,尺寸回升,父级设置地位。

让咱们来剖析一下。

小部件从其父级取得本人的束缚。一个束缚只是一组四个双精度:最小和最大宽度,以及最小和最大高度。

而后,小部件会遍历其本人的子级列表。小部件一个接一个地通知它的孩子他们的束缚是什么(每个孩子可能不同),而后询问每个孩子它想要的大小。

接下来,小部件将其子项(程度在 x 轴上,垂直在 y 轴上)一一定位。最初,小部件通知它的父级它本人的大小(当然是在原始束缚范畴内)。

在Flutter中,所有的小部件都是基于父级或它们的盒子束缚来渲染本人。这带来了一些限度。例如,设想一下,你在一个父部件内有一个子部件,你想决定它的大小。这个小组件不能有任何尺寸! 它的尺寸必须在它的父对象所设定的束缚范畴内。

与第一个示例相似,小部件无奈晓得本人在屏幕中的地位,因为这是父小部件的决定。

也就是说,如果一个子部件决定了与它的父部件不同的尺寸,而父部件没有足够的信息来对齐它,那么子部件的尺寸可能被疏忽。

好的,让咱们看看这个。

void main() {  runApp(MyApp());}class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MyWidget();  }}class MyWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return ConstrainedBox(       constraints: const BoxConstraints(         maxHeight: 400,         minHeight: 100,         minWidth: 100,         maxWidth: 400,       ),      child: Container(        color: Colors.green,      ),    );  }}

如果您违心,您能够疏忽 ConstrainedBox 并将高度和小部件增加到 Container 中。

class MyWidget extends StatelessWidget {  @override  Widget build(BuildContext context) {    return  Container(      height: 400,       width: 400,      color: Colors.green,    );  }}

您可能心愿下面的代码渲染一个最大高度和宽度为 400 的绿色 Container。然而,当您运行此代码时,您会感到诧异。

整个屏幕将是纯绿色!我不会在这里深入探讨细节,但在构建 Flutter 布局时,您可能会遇到几个与此类似的问题。

让咱们看看这里产生了什么。在下面的示例中,树如下所示:

    - `MyApp`    - `MyWidget`    - `ConstrainedBox`    - `Container`

束缚规定将从父控件传递给子控件,因而子控件能够在其父控件的给定束缚范畴内决定其大小。因而,束缚实用。

因而,Flutter 将严格束缚传递给 MyApp(),而后 MyApp() 将其严格束缚传递给 ConstrainedBox。而后,ConstrainedBox 被迫疏忽它本人的束缚并应用它的父级,在这种状况下,它是全屏大小,这就是为什么你会看到一个全屏的绿色框。

通常,你会发现增加一个 Center小部件可能会解决这个问题。让咱们试一试吧。

class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Center(      child: MyWidget()    );  }}

瞧!这就对了。它曾经修好了!

Center 小部件从 MyApp() 中获取一个严格的束缚,并将其转换为对其子项的涣散束缚,即 ConstrainedBox。因而,Container 遵循 ConstrainedBox 给出的束缚,以便 Container 利用最小和最大大小。

在咱们实现本节之前,让我疾速解释一下什么是紧束缚和松束缚。

一个严格的束缚提供了一种可能性——一个准确的尺寸,这意味着它的最大宽度等于它的最小宽度,它的最大高度等于它的最小高度。

如果你转到 Flutter 的 box.dart 文件并搜寻 BoxConstraints 构造函数,你会发现以下内容:

BoxConstraints.tight(Size size)   : minWidth = size.width,     maxWidth = size.width,     minHeight = size.height,     maxHeight = size.height;

另一方面,涣散束缚设置最大宽度和高度,但容许小部件尽可能小。它的最小宽度和高度都等于 0

BoxConstraints.loose(Size size)   : minWidth = 0.0,     maxWidth = size.width,     minHeight = 0.0,     maxHeight = size.height;

如果你再看下面的例子,它通知咱们 Center 容许绿色 Container比屏幕更小,但不能更大。当然,Center通过将涣散的束缚传递给 Container来做到这一点。

完结

在这篇文章中,我提到了一些你在开始构建Flutter应用程序时应该施行的许多最佳实际。然而,还有更多的--更高级的--实际须要思考,我倡议你去看看Flutter的详尽文档。编码欢快。


近期文章

  • 终于!我的第一个产品:集美美图上架了,小而美的美女壁纸精选 APP
  • 你的利用应该如何免费?它的价值是什么?