Flutter开发三 浅谈Flutter UI布局

1 Flutter布局浅述

对于一个应用来说,开发UI界面是很基础的一个工作。Flutter中的布局是直接写在代码中的,没有像Android一样使用xml来布局,这一点与RN中使用jsx来布局类似,它遵循的也是一切都是widget的思想。因此Flutter 中UI界面的布局就是组合各种widget的过程。我们可以参考下面的一段话

    In Android, the View is the foundation of everything that shows up on the screen. Buttons, toolbars, and inputs, everything is a View. In Flutter, the rough equivalent to a View is a Widget. Widgets don’t map exactly to Android views, but while you’re getting acquainted with how Flutter works you can think of them as “the way you declare and construct UI”.

大意是在Android中,View是屏幕上显示的所有内容的基础, 按钮、工具栏、输入框等一切都是View。 在Flutter中,View相当于是Widget。所以在Android中我们把所有的UI元素都看成View,在Flutter中widget就类似于View

与Android不同的是,Flutter提供的widget是在是太多了,对于相同的UI界面也可以使用不同的widget来实现,不过在Flutter中有一个原则,那就是尽量使用轻量级的widget来实现

关于如何在Flutter中进行布局,我们还是来参考官方的例子吧
https://flutter.io/docs/development/ui/layout
Flutter开发三 浅谈Flutter UI布局_第1张图片
打开这个例子可以看到Flutter如何教我们布局,下面大体说以下步骤

2 布局拆分

第一步是将布局拆分成基本的元素:
找出行和列.
布局包含网格吗?
有重叠的元素吗?
是否需要选项卡?
注意需要对齐、填充和边框的区域.

首先,确定更大的元素。在这个例子中,四个元素排列成一列:一个图像,两个行和一个文本块
Flutter开发三 浅谈Flutter UI布局_第2张图片

其实这个和Android中的布局类似,如上的布局,在Android中我们也会采用一个线性布局,child分别是imageview,线性布局 线性布局 TextView。

3 确定根布局

由于我们一般会遵循MD设计,因此可以先写出如下的根布局

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    
    // TODO: implement build
    return MaterialApp(
      title: "Flutter Layout Demo",
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text("Flutter Layout Demo"),
        ),
        body: ,
      ),
    );
  }
}

下面开始布局body部分了,body部分我们可以采用一个Column或者ListView,官方是ListView 可能主要是怕屏幕空间不够吧。

4 布局Image

我们还是按照官方的布局来。首先布局ImageView

body: ListView(
          children: [
            Image.asset(
              "assets/images/lake.jpg",
              height: 240.0,
              fit: BoxFit.cover,
            ),
          ],
        ),

这里首先把lake.jpg拷贝到工程的assets/images目录下,并且在pubspec.yaml 文件中修改如下

  assets:
    - assets/images/lake.jpg

运行Demo,我们的效果如下:

5 布局标题行

看下图的标题行
Flutter开发三 浅谈Flutter UI布局_第3张图片
可以看到,首先是一个Row widget,它有三个child一列文字,一个星形图标和一个数字,第一个child是一个Column布局,有两个child,包含2行文字
另外,第一列占用大量空间,所以它必须包装在Expanded widget中。
因此布局如下:

Widget titleSection = Container(
      padding: EdgeInsets.all(32.0),
      child: Row(
        //3个child 水平排列
        children: [
          //占满剩余空间
          Expanded(
            // 2 个child竖直排列
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: EdgeInsets.only(bottom: 8.0),
                  child: Text(
                    "Oeschinen Lake Campground",
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
                Text(
                  "Kandersteg, Switzerland",
                  style: TextStyle(color: Colors.grey[500]),
                )
              ],
            ),
          ),

          Icon(
            Icons.star,
            color: Colors.red[500],
          ),

          Text("41"),
        ],
      ),
    );

接着,修改我们的ListView如下:

// TODO: implement build
    return MaterialApp(
      title: "Flutter Layout Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text("Flutter Layout Demo"),
        ),
        body: ListView(
          children: [
            Image.asset(
              "assets/images/lake.jpg",
              height: 240.0,
              fit: BoxFit.cover,
            ),
            titleSection,
          ],
        ),
      ),
    );

运行效果如下:

6 实现Button布局

对于Button布局,可以看出这首先是一个Row布局,然后有三个child
Flutter开发三 浅谈Flutter UI布局_第4张图片

这里我们参考官方的例子,写出一个函数来创建布局

  /// 构建button
  Column _buildButtonColumn(BuildContext context, IconData icon, String label) {
    Color color = Theme.of(context).primaryColor;

    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      //列布局
      children: [
        Icon(
          icon,
          color: color,
        ),
        Container(
          margin: EdgeInsets.only(top: 8.0),
          child: Text(
            label,
            style: TextStyle(
              fontSize: 12.0,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        )
      ],
    );
  }

函数调用如下:

    Widget buttonSection = Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          _buildButtonColumn(context, Icons.call, "Call"),
          _buildButtonColumn(context, Icons.near_me, "Route"),
          _buildButtonColumn(context, Icons.share, "Share"),
        ],
      ),
    );

需要说明一下的是官方主轴方向通过 MainAxisAlignment.spaceEvenly 平均的分配每个列占据的行空间,我这里是MainAxisAlignment.spaceAround,注意这二者之间的细微差别。最后运行效果如下:

7 布局Text文本

关于Text文本的布局就很简单了,这里不再细说了,可以参考官方例子。下面看一下完成后的例子
代码

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  var text =  '''
Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese Alps. Situated 1,578 meters above sea level, it is one of the larger Alpine Lakes. A gondola ride from Kandersteg, followed by a half-hour walk through pastures and pine forest, leads you to the lake, which warms to 20 degrees Celsius in the summer. Activities enjoyed here include rowing, and riding the summer toboggan run.
        ''';

  @override
  Widget build(BuildContext context) {
    Widget titleSection = Container(
      padding: EdgeInsets.all(32.0),
      child: Row(
        //3个child 水平排列
        children: [
          //占满剩余空间
          Expanded(
            // 2 个child竖直排列
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: EdgeInsets.only(bottom: 8.0),
                  child: Text(
                    "Oeschinen Lake Campground",
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
                Text(
                  "Kandersteg, Switzerland",
                  style: TextStyle(color: Colors.grey[500]),
                )
              ],
            ),
          ),

          Icon(
            Icons.star,
            color: Colors.red[500],
          ),

          Text("41"),
        ],
      ),
    );

    Widget buttonSection = Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          _buildButtonColumn(context, Icons.call, "Call"),
          _buildButtonColumn(context, Icons.near_me, "Route"),
          _buildButtonColumn(context, Icons.share, "Share"),
        ],
      ),
    );

    Widget textSection = Container(
      padding: EdgeInsets.all(32.0),
      child: Text(
        text,
        softWrap: true,
      ),
    );

    // TODO: implement build
    return MaterialApp(
      title: "Flutter Layout Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text("Flutter Layout Demo"),
        ),
        body: ListView(
          children: [
            Image.asset(
              "assets/images/lake.jpg",
              height: 240.0,
              fit: BoxFit.cover,
            ),
            titleSection,
            buttonSection,
            textSection,

          ],
        ),
      ),
    );
  }

  /// 构建button
  Column _buildButtonColumn(BuildContext context, IconData icon, String label) {
    Color color = Theme.of(context).primaryColor;

    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      //列布局
      children: [
        Icon(
          icon,
          color: color,
        ),
        Container(
          margin: EdgeInsets.only(top: 8.0),
          child: Text(
            label,
            style: TextStyle(
              fontSize: 12.0,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        )
      ],
    );
  }
}

运行效果

8 Flutter布局总结

1 Flutter的布局一切都是widget,布局过程就是widget的组合
2 Flutter布局中需要清楚的划分Row Column Stack ListView GridView等
3 布局过程中的margin padding等可考虑使用Container
4 对于有状态的Widget需要使用StatefulWidget子类来实现
5 对于同一布局能用多种不同的widget来实现的,尽量使用轻量级的widget

参考 https://flutter.io/docs/development/ui/layout

你可能感兴趣的:(Flutter)