Using Flutter to Build a Beautiful UI Interface-Basic Components

  dart, flutter, Front end, ui

图片描述

1. Preface

FlutterAs one of the most popular technologies at present, it has already attracted the attention of a large number of technology lovers, even some, due to its excellent performance and smooth multi-terminal differences.Idle fish,meituan,TencentSuch as large companies have begun to use. Although its ecology is not fully mature at present, it depends on what is behind it.GoogleBlessing, its development speed has been amazing enough to predict the futureFlutterThe demand for developers will also grow.

It has been 9102 years for the sake of the current technology taste and the future trend. As a front-end developer, there seems to be no reason not to try it. It is with this mentality that the author also began to learnFlutterAt the same time, we have built a trainingWarehouse, all subsequent code will be hosted on it, welcome star, learn together. This is my Flutter series:

Today, I share some basic components commonly used in Flutter, which are the basic elements that make up the UI interface:container,Line,Column,Absolute positioning layout,Text,PictureAndIconWait.

2. Basic components

2.1 Container (Container Assembly)

容器组件

ContainerComponent is one of the most commonly used layout components, which can be considered in web development.divRn under developmentView. It can often be used to control size, background color, border, shadow, inner and outer margins, content arrangement, etc. Let’s look at its constructor first:

Container({
  Key key,
  double width,
  double height,
  this.margin,
  this.padding,
  Color color,
  this.alignment,
  BoxConstraints constraints,
  Decoration decoration,
  this.foregroundDecoration,
  this.transform,
  this.child,
})

2.1.1width,height,margin,padding

The meaning of these attributes is not different from what we already know. The only thing that needs attention is,marginAndpaddingThe assignment of is not a simple number because it hasleft,top,right,bottomValues in four directions need to be set.FlutterProvidedEdgeInsetsThis class helps us easily generate values in four directions. Usually, we may use itEdgeInsetsFour construction methods of:

  • EdgeInsets.all(value): used to set 4 values with the same direction;
  • EdgeInsets.only(left: val1, top: val2, right: val3, bottom: val4): You can set the value in a certain direction separately;
  • EdgeInsets.symmetric(horizontal: val1, vertical: val2): used to set values in the horizontal/vertical direction;
  • EdgeInsets.fromLTRB(left, top, right, bottom): Set the values in 4 directions in the order of top left and bottom right.

2.1.2color

The meaning of this attribute is the backgroundColor, which is equivalent to the background color in web/rn. It should be noted thatFlutterThere is a special color inColorClass, not the string we use frequently. However, we can make the transition very easily. For example, chestnuts:

In web/rn we will use'#FF0000'Or'red'In Flutter, we can useColor(0xFFFF0000)OrColors.redTo express.

2.1.3alignment

This attribute is used to determineContainerHow will the subcomponents of the component be arranged (PS: don’t worry about how to center anymore). The optional values are usually used:

  • Alignment.topLeft: upper left
  • Alignment.topCenter: upper middle
  • Alignment.topRight: upper right
  • Alignment.centerLeft: center left
  • Alignment.center: centered
  • Alignment.centerRight: right center
  • Alignment.bottomLeft: bottom left
  • Alignment.bottomCenter: lower middle
  • Alignment.bottomRight: bottom right

2.1.4constraints

In web/rn we usually useminWidth/maxWidth/minHeight/maxHeightProperty to limit the width and height of the container. InFlutterYou need to useBoxConstraints(box constraint) to achieve this function.

// 容器的大小将被限制在[100*100 ~ 200*200]内
BoxConstraints(
  minWidth: 100,
  maxWidth: 200,
  minHeight: 100,
  maxHeight: 200,
)

2.1.5decoration

This attribute is very powerful and literally means decoration, because through it you can setFrame,Shadow,Gradual change,Rounded cornerCommon attributes such as.BoxDecorationInherit fromDecorationClass, so we usually generate aBoxDecorationInstance to set these properties.

1) Border

Can be usedBorder.allThe constructor directly generates 4 borders, or it can use theBorderThe constructor sets the borders in different directions separately. However, it is surprising that the official border does not support it.Dotted line(issueHere).

// 同时设置4条边框:1px粗细的黑色实线边框
BoxDecoration(
  border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
)

// 设置单边框:上边框为1px粗细的黑色实线边框,右边框为1px粗细的红色实线边框
BoxDecoration(
  border: Border(
    top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
    right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
  ),
)

2) Shadow

Shadow Properties and in the webboxShadowThere is almost no difference, you can specifyx,y,blur,spread,colorEqual attribute.

BoxDecoration(
  boxShadow: [
    BoxShadow(
      offset: Offset(0, 0),
      blurRadius: 6,
      spreadRadius: 10,
      color: Color.fromARGB(20, 0, 0, 0),
    ),
  ],
)

3) gradual change

If you don’t want the background color of the container to be monotonous, try usinggradientProperty.FlutterAt the same time supportLinear gradientAndRadial gradient

// 从左到右,红色到蓝色的线性渐变
BoxDecoration(
  gradient: LinearGradient(
    begin: Alignment.centerLeft,
    end: Alignment.centerRight,
    colors: [Colors.red, Colors.blue],
  ),
)

// 从中心向四周扩散,红色到蓝色的径向渐变
BoxDecoration(
  gradient: RadialGradient(
    center: Alignment.center,
    colors: [Colors.red, Colors.blue],
  ),
)

4) fillet

Normally, you might use itBorderRadius.circularConstructor to set rounded corners of 4 corners at the same time, orBorderRadius.onlyConstructor to set the fillet of some corners separately:

// 同时设置4个角的圆角为5
BoxDecoration(
  borderRadius: BorderRadius.circular(5),
)

// 设置单圆角:左上角的圆角为5,右上角的圆角为10
BoxDecoration(
  borderRadius: BorderRadius.only(
    topLeft: Radius.circular(5),
    topRight: Radius.circular(10),
  ),
)

2.1.6transform

transformAttributes are basically the same as those we often use in web/rn, mainly including:Translation,ZoomRotationAndTilt. In Flutter, matrix transformation classes are encapsulatedMatrix4Help us make changes:

  • translationValues(x, y, z): shift x, y, z;
  • rotationX(radians): x-axis rotation radians;
  • rotationY(radians): y-axis rotation radians;
  • rotationZ(radians): z-axis rotation radians;
  • skew(alpha, beta): X-axis inclination alpha degree, Y-axis inclination beta degree;
  • skewX(alpha): x-axis tilt alpha;
  • skewY(beta): y-axis tilt beta;

2.1.7 Summary

ContainerComponents are rich in properties. Although some of them are slightly different from web/rn in usage, they are basically the same, so there will be no obstacles to transition. In addition, due toContainerA component is a single node component, that is, only one child node is allowed. So in terms of layout, we often use itRowAndColumnComponent executionLine/ColumnLayout.

2.2 Row/Column (Row/Column Component)

行/列组件

RowAndColumnThe component is actually the same as theFlex layout(Elastic Box) Very Similar, or We Can Understand It That Way. UseFlex layoutMy classmates are rightSpindleAndSecondary axisI’m sure I’m already very familiar with the concept of,RowThe main axis of the assembly is transverse.ColumnThe main axis of the assembly is longitudinal. And their constructors are very similar (uncommon attributes have been omitted):

Row({
  Key key,
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
  MainAxisSize mainAxisSize = MainAxisSize.max,
  List<Widget> children = const <Widget>[],
})

Column({
  Key key,
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
  MainAxisSize mainAxisSize = MainAxisSize.max,
  List<Widget> children = const <Widget>[],
})

2.2.1mainAxisAlignment

The meaning of this attribute is the arrangement of main axes, which can be known from the above constructorRowAndColumnThe components start from start by default in the spindle direction, that is to sayRowComponents are arranged from left to right by default.ColumnComponents arrange subcomponents from top to bottom by default.

Of course, you can also use other optional values:

  • MainAxisAlignment.start
  • MainAxisAlignment.end
  • MainAxisAlignment.center
  • MainAxisAlignment.spaceBetween
  • MainAxisAlignment.spaceAround
  • MainAxisAlignment.spaceEvenly

2.2.2crossAxisAlignment

The meaning of this attribute is the arrangement of sub-axes, which can be known from the above constructorRowAndColumnComponents are centered by default in the minor axis direction.

There is a little need here.Special attentionDue toColumnThe component’s minor axis direction (i.e. horizontal) is aligned centrally by default, so its parent container will not be fully filled in the horizontal direction, which needs to be specified at this time.CrossAxisAlignment.stretchOnly then.

In addition, other optional values for Crossassisalign are:

  • crossAxisAlignment.start
  • crossAxisAlignment.end
  • crossAxisAlignment.center
  • crossAxisAlignment.stretch
  • crossAxisAlignment.baseline

2.2.3mainAxisSize

Literally, this attribute refers to the size on the spindle. In fact, it refers to whether to wrap its contents or to fill its parent container in the direction of the main axis. Its optional values areMainAxisSize.minAndMainAxisSize.max. Since the default values areMainAxisSize.maxTherefore, the default size in the main axis direction is to hold the parent container as full as possible.

2.2.4 Summary

Due toRow/ColumnComponents and familiar onesFlex layoutIt’s very similar, so it’s very easy to get started, with almost no learning cost.

2.3 Stack/Positoned (absolute positioning layout component)

绝对定位布局组件

Absolute positioning layout is also a layout method with high frequency in web/rn development.FlutterAlso provides the corresponding component implementation, need toStackAndPositionedComponents are used together. For example, the example below is to create a yellow box with four small red squares in its four corners.StackComponents are absolutely positioned containers.PositionedComponents pass throughleft,top,right,bottomAttribute values in four directions determine its position in the parent container.

Container(
  height: 100,
  color: Colors.yellow,
  child: Stack(
    children: <Widget>[
      Positioned(
        left: 10,
        top: 10,
        child: Container(width: 10, height: 10, color: Colors.red),
      ),
      Positioned(
        right: 10,
        top: 10,
        child: Container(width: 10, height: 10, color: Colors.red),
      ),
      Positioned(
        left: 10,
        bottom: 10,
        child: Container(width: 10, height: 10, color: Colors.red),
      ),
      Positioned(
        right: 10,
        bottom: 10,
        child: Container(width: 10, height: 10, color: Colors.red),
      ),
    ],
  ),
)

2.4 Text (Text Component)

(文本组件)

TextComponent is also one of the most commonly used basic components in daily development. We usually use it to display text information. Let’s take a look at its constructor (the uncommon attributes have been omitted):

const Text(
  this.data, {
  Key key,
  this.style,
  this.textAlign,
  this.softWrap,
  this.overflow,
  this.maxLines,
})
  • data: Text information displayed;
  • style: text style,FlutterOne is providedTextStyleClass, most commonly usedfontSize,fontWeight,color,backgroundColorAndshadowsSuch attributes are set through it;
  • textAlign: text alignment, commonly used optional values areTextAlignTheleft,right,centerAndjustify;
  • softWrap: Whether the text wraps or not;
  • overflow: When text overflows, how to handle it (default direct truncation). The optional values areTextOverflowTheclip,fade,ellipsisAndvisible;
  • maxLines: When the text exceeds the maximum number of lines and is not displayed, it will be based onoverflowProperty determines how to truncate the processing.

FlutterTheTextComponents are flexible enough to provide various attributes for us to customize, but in general, we only need a few lines of code below to do more:

Text(
  '这是测试文本',
  style: TextStyle(
    fontSize: 13,
    fontWeight: FontWeight.bold,
    color: Color(0xFF999999),
  ),
)

In addition to the above application scenarios, sometimes we also encounterRich text(i.e. different font styles may be required in a piece of text). For example, in some UI designs, prices are often expressed.Symbol ratiomoneyThe font size is smaller. For such requirements, we can useFlutterProvidedText.richConstructor to create the corresponding text component:

Text.rich(TextSpan(
  children: [
    TextSpan(
      '¥',
      style: TextStyle(
        fontSize: 12,
        color: Color(0xFFFF7528),
      ),
    ),
    TextSpan(
      '258',
      style: TextStyle(
        fontSize: 15,
        color: Color(0xFFFF7528),
      ),
    ),
  ]
))

2.5 Image (Picture Component)

图片组件

ImageAs one of the basic components of rich content, picture components are frequently used in daily development. Take a look at its constructor (uncommon attributes have been omitted):

Image({
  Key key,
  @required this.image,
  this.width,
  this.height,
  this.color,
  this.fit,
  this.repeat = ImageRepeat.noRepeat,
})
  • image: picture source, the most commonly used to the main two (AssetImageAndNetworkImage)。 UseAssetImageBefore, need to be inpubspec.yamlThe picture resource is declared in the file before it can be used. AndNextworkImageSpecify the network address of the picture, which is mainly used when loading some network pictures;
  • width: picture width;
  • height: picture height;
  • color: the background color of the picture, which will be displayed before the network picture is loaded.
  • fit: This property can be used when we want the picture to fit according to the size of the container instead of specifying a fixed width-height value. The optional values areBoxFitThefill,contain,cover,fitWidth,fitHeight,noneAndscaleDown;
  • repeat: Determines whether to use repeating effects when the actual size of the picture is less than the specified size.

In addition,FlutterAlso provided areImage.networkAndImage.assetThe constructor is actually a syntax sugar. For example, the results of the following two sections of code are exactly the same:

Image(
  image: NetworkImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg'),
  width: 100,
  height: 100,
)

Image.network(
  'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg',
  width: 100,
  height: 100,
)

2.6Icon(Icon Component)

图标组件

IconIcon components have the advantage of magnification and no distortion compared with pictures, and are often used in daily development.FlutterBut also directly built in a set ofMaterialStyle iconHerePreview all icon types). Look at the constructor:

const Icon(
  this.icon, {
  Key key,
  this.size,
  this.color,
})
  • icon: icon type;
  • size: icon size;
  • color: icon color.

3. Layout of actual combat

Through the introduction in the previous section, we are rightContainer,Row,Column,Stack,Positioned,Text,ImageAndIconComponents have a preliminary understanding. Next, let’s deepen our understanding and memory through a practical example.

布局实战

3.1 Preparation-Data Types

According to the contents of the above card, we can define some fields. In order to standardize the development process, we first define a data type class for the card, so as to better Mock and manage the data in the subsequent development process:

class PetCardViewModel {
  /// 封面地址
  final String coverUrl;

  /// 用户头像地址
  final String userImgUrl;

  /// 用户名
  final String userName;

  /// 用户描述
  final String description;

  /// 话题
  final String topic;

  /// 发布时间
  final String publishTime;

  /// 发布内容
  final String publishContent;

  /// 回复数量
  final int replies;

  /// 喜欢数量
  final int likes;

  /// 分享数量
  final int shares;

  const PetCardViewModel({
    this.coverUrl,
    this.userImgUrl,
    this.userName,
    this.description,
    this.topic,
    this.publishTime,
    this.publishContent,
    this.replies,
    this.likes,
    this.shares,
  });
}

3.2 Build skeleton and split the layout

布局拆分

According to the given visual diagram, we can divide the whole into 4 parts:Cover,UserInfo,PublishContentAndInteractionArea. To this end, we can put up the basic skeleton of the code:

class PetCard extends StatelessWidget {
  final PetCardViewModel data;

  const PetCard({
    Key key,
    this.data,
  }) : super(key: key);

  Widget renderCover() {
    
  }

  Widget renderUserInfo() {
    
  }

  Widget renderPublishContent() {
  
  }

  Widget renderInteractionArea() {
   
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(8),
        boxShadow: [
          BoxShadow(
            blurRadius: 6,
            spreadRadius: 4,
            color: Color.fromARGB(20, 0, 0, 0),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          this.renderCover(),
          this.renderUserInfo(),
          this.renderPublishContent(),
          this.renderInteractionArea(),
        ],
      ),
    );
  }
}

3.3 Cover Area

In order to better highlight the effect of the picture, a covering layer is added here, so it can be used here.Stack/PositionedLayout andLinearGradientGradual change, Dom structure is as follows:

封面区域

Widget renderCover() {
  return Stack(
    fit: StackFit.passthrough,
    children: <Widget>[
      ClipRRect(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(8),
          topRight: Radius.circular(8),
        ),
        child: Image.network(
          data.coverUrl,
          height: 200,
          fit: BoxFit.fitWidth,
        ),
      ),
      Positioned(
        left: 0,
        top: 100,
        right: 0,
        bottom: 0,
        child: Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [
                Color.fromARGB(0, 0, 0, 0),
                Color.fromARGB(80, 0, 0, 0),
              ],
            ),
          ),
        ),
      ),
    ],
  );
}

3.4 User Information Area

The user information area is very suitable for useRowAndColumnComponent to layout, Dom structure is as follows:

用户信息区域

Widget renderUserInfo() {
  return Container(
    margin: EdgeInsets.only(top: 16),
    padding: EdgeInsets.symmetric(horizontal: 16),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Row(
          children: <Widget>[
            CircleAvatar(
              radius: 20,
              backgroundColor: Color(0xFFCCCCCC),
              backgroundImage: NetworkImage(data.userImgUrl),
            ),
            Padding(padding: EdgeInsets.only(left: 8)),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  data.userName,
                  style: TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                    color: Color(0xFF333333),
                  ),
                ),
                Padding(padding: EdgeInsets.only(top: 2)),
                Text(
                  data.description,
                  style: TextStyle(
                    fontSize: 12,
                    color: Color(0xFF999999),
                  ),
                ),
              ],
            ),
          ],
        ),
        Text(
          data.publishTime,
          style: TextStyle(
            fontSize: 13,
            color: Color(0xFF999999),
          ),
        ),
      ],
    ),
  );
}

3.5 Publishing Content Area

Through UI exercises in this area, we can practiceContainerComponent settings are differentborderRadius, andTextTruncation processing when component text content exceeds, Dom structure is as follows:

发布内容区域

Widget renderPublishContent() {
  return Container(
    margin: EdgeInsets.only(top: 16),
    padding: EdgeInsets.symmetric(horizontal: 16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Container(
          margin: EdgeInsets.only(bottom: 14),
          padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
          decoration: BoxDecoration(
            color: Color(0xFFFFC600),
            borderRadius: BorderRadius.only(
              topRight: Radius.circular(8),
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
          ),
          child: Text(
            '# ${data.topic}',
            style: TextStyle(
              fontSize: 12,
              color: Colors.white,
            ),
          ),
        ),
        Text(
          data.publishContent,
          maxLines: 2,
          overflow: TextOverflow.ellipsis,
          style: TextStyle(
            fontSize: 15,
            fontWeight: FontWeight.bold,
            color: Color(0xFF333333),
          ),
        ),
      ],
    ),
  );
}

3.6 interactive areas

In this module, we will useIconIcon component, can control its size, color and other attributes, Dom structure is as follows:

互动区域

Widget renderInteractionArea() {
  return Container(
    margin: EdgeInsets.symmetric(vertical: 16),
    padding: EdgeInsets.symmetric(horizontal: 16),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Row(
          children: <Widget>[
            Icon(
              Icons.message,
              size: 16,
              color: Color(0xFF999999),
            ),
            Padding(padding: EdgeInsets.only(left: 6)),
            Text(
              data.replies.toString(),
              style: TextStyle(
                fontSize: 15,
                color: Color(0xFF999999),
              ),
            ),
          ],
        ),
        Row(
          children: <Widget>[
            Icon(
              Icons.favorite,
              size: 16,
              color: Color(0xFFFFC600),
            ),
            Padding(padding: EdgeInsets.only(left: 6)),
            Text(
              data.likes.toString(),
              style: TextStyle(
                fontSize: 15,
                color: Color(0xFF999999),
              ),
            ),
          ],
        ),
        Row(
          children: <Widget>[
            Icon(
              Icons.share,
              size: 16,
              color: Color(0xFF999999),
            ),
            Padding(padding: EdgeInsets.only(left: 6)),
            Text(
              data.shares.toString(),
              style: TextStyle(
                fontSize: 15,
                color: Color(0xFF999999),
              ),
            ),
          ],
        ),
      ],
    ),
  );
}

3.7 Summary

Through the above example, we succeeded in dismantling a seemingly complicated UI interface step by step, using all the components mentioned before, and finally got good results. In fact, more than 90% of the requirements in daily development cannot be separated from the basic components mentioned above. Therefore, as long as a little practice, familiar withFlutterThe usage of basic components in is already a big step.

There are alsoBank cardAndCircle of friendsAs an example of UI exercises, due to space reasons, you can go toGithub warehouseLook.

4. Summary

This article first introducedFlutterThe most commonly used basic components for building UI interfaces (container,Line,Column,Absolute positioning layout,Text,PictureAndIcon) usage. Then, a more complicated UI actual combat example is introduced. Through the layer-by-layer disassembly of Dom structure, the components mentioned above have been comprehensively applied, which is to consolidate the conceptual knowledge learned earlier.

But finally had to spit out 1:FlutterThe nesting is really hard to bear. . . If the UI layout is not divided into modules, it is definitely a nightmare experience. Moreover, unlike web/rn development styles, which can be separated separately,FlutterThis way of treating styles as attributes is really hard to sort out dom structure at first sight, and it takes a little time for developers who are newly taking over the code to understand. . .

All code in this article is managed inHere, can also pay attention to myBlog.