Flutter 常用布局

1,998 阅读6分钟

Flutter 常用布局

在UI 的编程世界里面,一个个view 都是嵌套在布局里面,最终呈现出合适的UI效果。今天就一起来看看 Flutter 中的布局有哪些。

由于在Flutter 的世界中,一切都是Widget ,自然布局也不例外。Flutter 提供了 31 种布局 Widget,对布局控件的划分非常详细,一些相同(或相似)的视觉效果可以通过多种布局控件实现,因此布局类型相比原生 Android、iOS 平台多了不少。根据以往的经验,可以把布局分为几类:单子Widget 布局、多子Widget 布局。

单子Widget 布局

单子 Widget 布局类容器比较简单,一般用来对其唯一的子 Widget 进行样式包装,比如限制大小、添加背景色样式、内间距、旋转变换等。这一类布局 Widget,包括 Container、Padding 与 Center 三种。

这些布局都有哪些呢?Container、Padding 与 Center等 具体参考单子widget布局

Container

Container 是最常用的布局之一,可以通过设置 宽高、margin、padding、背景色、形状、子widget的对齐方式等。

image.png

Padding

当只需要给 widget 设置padding,这时候可以使用更简单的布局--Padding。

image.png

Center

想要让一个widget 在父容器居中,使用 Center就可以。

image.png

多子Widget 布局

看完了三个常用的单子widget 布局后,一定觉得很简单,下面我们一起进入到多子widget 布局的学习吧。 在Flutter 中,常用的多子widget 布局有,Row、Column 与 Expanded。

Row

Row:表示的是一行,子widget 横向排列,我们先看看Row中的构造函数

  Row({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,//主轴的对齐方式
    MainAxisSize mainAxisSize = MainAxisSize.max,//宽度,MainAxisSize.max表示占满父容器,MainAxisSize.min表示自适应,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,// 纵轴的对齐方式
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline = TextBaseline.alphabetic,
    List<Widget> children = const <Widget>[],
  }) 

主轴:就是表示容器依次摆放子 Widget 的方向;纵轴,则是与主轴垂直的另一个方向。 我们来看个例子:

image.png

Column

Column:表示的是一列,子widget 横向排列,我们先看看Column中的构造函数

  Column({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline,
    List<Widget> children = const <Widget>[],
  })

发现和Row完全一样,使用方式也是一样,这里就不过多介绍了,

Expanded

单纯使用 Row 和 Column 控件,在子 Widget 的尺寸较小时,无法将容器填满,视觉样式比较难看。对于这样的场景,我们可以通过 Expanded 控件,来制定分配规则填满容器的剩余空间。相当Android LinearLayout 中的权重 layout_weight来设置widget 所占剩余控件的比重。

image.png

层叠 Widget 布局:Stack 与 Positioned

Stack 容器与前端中的绝对定位、Android 中的 Frame 布局非常类似,子 Widget 之间允许叠加,还可以根据父容器上、下、左、右四个角的位置来确定自己的位置。 Stack 提供了层叠布局的容器,而 Positioned 则提供了设置子 Widget 位置的能力。

image.png

MaterialApp

是我们app开发中常用的符合MaterialApp Design设计理念的入口Widget,从源码可以看出该widget的构造方法中有多个参数. MaterialApp

MaterialApp({
  Key key,
  this.title = '', // 设备用于为用户识别应用程序的单行描述
  this.home, // 应用程序默认路由的小部件,用来定义当前应用打开的时候,所显示的界面
  this.color, // 在操作系统界面中应用程序使用的主色。
  this.theme, // 应用程序小部件使用的颜色。
  this.routes = const <String, WidgetBuilder>{}, // 应用程序的顶级路由表
  this.navigatorKey, // 在构建导航器时使用的键。
  this.initialRoute, // 如果构建了导航器,则显示的第一个路由的名称
  this.onGenerateRoute, // 应用程序导航到指定路由时使用的路由生成器回调
  this.onUnknownRoute, // 当 onGenerateRoute 无法生成路由(initialRoute除外)时调用
  this.navigatorObservers = const <NavigatorObserver>[], // 为该应用程序创建的导航器的观察者列表
  this.builder, // 用于在导航器上面插入小部件,但在由WidgetsApp小部件创建的其他小部件下面插入小部件,或用于完全替换导航器
  this.onGenerateTitle, // 如果非空,则调用此回调函数来生成应用程序的标题字符串,否则使用标题。
  this.locale, // 此应用程序本地化小部件的初始区域设置基于此值。
  this.localizationsDelegates, // 这个应用程序本地化小部件的委托。
  this.localeListResolutionCallback, // 这个回调负责在应用程序启动时以及用户更改设备的区域设置时选择应用程序的区域设置。
  this.localeResolutionCallback, // 
  this.supportedLocales = const <Locale>[Locale('en', 'US')], // 此应用程序已本地化的地区列表 
  this.debugShowMaterialGrid = false, // 打开绘制基线网格材质应用程序的网格纸覆盖
  this.showPerformanceOverlay = false, // 打开性能叠加
  this.checkerboardRasterCacheImages = false, // 打开栅格缓存图像的棋盘格
  this.checkerboardOffscreenLayers = false, // 打开渲染到屏幕外位图的图层的棋盘格
  this.showSemanticsDebugger = false, // 打开显示框架报告的可访问性信息的覆盖
  this.debugShowCheckedModeBanner = true, // 在选中模式下打开一个小的“DEBUG”横幅,表示应用程序处于选中模式
}) 

里面有很多的参数,在使用的时候可以根据需要选择配置,不用太多,我们常用的是 :this.theme,this.home等

Scaffold

Scaffold 翻译过来就是脚手架的意思,它实现了基本的 Material Design 可视化布局结构。此类提供了用于显示drawer、snackbar和底部sheet的API。

const Scaffold({
  Key key,
  this.appBar, // 标题栏
  this.body,  // 用于显示当前界面主要内容的Widget
  this.floatingActionButton, // 一个悬浮在body上的按钮,默认显示在右下角
  this.floatingActionButtonLocation, // 用于设置floatingActionButton显示的位置
  this.floatingActionButtonAnimator, // floatingActionButton移动到一个新的位置时的动画
  this.persistentFooterButtons, // 多状态按钮
  this.drawer, // 左侧的抽屉菜单
  this.endDrawer, //  右'侧的抽屉菜单
  this.bottomNavigationBar,// 底部导航栏。
  this.bottomSheet, // 显示在底部的工具栏
  this.backgroundColor,// 内容的背景颜色
  this.resizeToAvoidBottomPadding = true, // 控制界面内容 body 是否重新布局来避免底部被覆盖,比如当键盘显示的时候,重新布局避免被键盘盖住内容。
  this.primary = true,// Scaffold是否显示在页面的顶部
}) 

看看例子:

Scaffold(
          appBar: AppBar(
            title: Text("单一widget布局"),
            bottom: TabBar(
              tabs: [
                Text("Container"),
                Text("Padding"),
                Text("Center"),
              ],
            ),
          ),
          body: TabBarView(
            children: [
              ContainerDemo(),
              PaddingDemo(),
              CenterDemo(),
            ],
          ),
        ),

DefaultTabController

顶部导航组件TabBar,TabBar选项卡一般位于AppBar下方,通常和TabBar(顶部导航选项卡)一起使用的有TabBarView和TabController。

DefaultTabController({
    Key key,
    @required this.length,//Tab 的个数
    this.initialIndex = 0,
    @required this.child,//显示的主要页面内容
  })

TabBar

const TabBar({
    Key key,
    @required this.tabs,//显示的标签内容,一般使用Tab对象,也可以是其他Widget
    this.controller,//TabController对象
    this.isScrollable = false,//是否可以滚动
    this.indicatorColor,//指示器颜色
    this.indicatorWeight = 2.0,//指示器的高度
    this.indicatorPadding = EdgeInsets.zero,//指示器底部的padding
    this.indicator,//指示器decoration,例如边框等
    this.indicatorSize,//指示器大小的计算方式,TabBarIndicatorSize.tab:跟每个tab等宽,TabBarIndicatorSize.label:跟文字等宽
    this.labelColor,//选中label的颜色
    this.labelStyle,//选中label的样式
    this.labelPadding,每个label的padding
    this.unselectedLabelColor,//未选中label的颜色
    this.unselectedLabelStyle,//未选中label的样式
  }) 

TabBarView

const TabBarView({
    Key key,
    @required this.children,//Tab页内容组件的集合
    this.controller,//TabController对象
    this.physics,
  }) 

看看例子:

Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            title: Text("单一widget布局"),
            bottom: TabBar(
              tabs: [
                Text("Container"),
                Text("Padding"),
                Text("Center"),
              ],
            ),
          ),
          body: TabBarView(
            children: [
              ContainerDemo(),
              PaddingDemo(),
              CenterDemo(),
            ],
          ),
        ),
      ),
    );