1. Introduction

Flutter, as one of the most popular technologies, has attracted the attention of many technology enthusiasts due to its excellent performance and advantages of smoothing multiple differences. Some big companies, such as Xianyu, Meituan and Tencent, have already started to use Flutter. Although the ecosystem of Flutter is not yet fully mature, thanks to the support of Google, it is growing fast enough that we can expect the need for Flutter developers to grow in the future.

Whether it’s for today’s technology or future trends, 9102 years on, 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 learn Flutter and built a warehouse for practice. All subsequent codes will be hosted on it. Welcome Star to learn Flutter together. This is my series on Flutter:

  • Build a beautiful UI with Flutter – Basic Components
  • Flutter rolling container component – ListView chapter
  • Flutter grid layout – GridView article
  • Use custom ICONS in Flutter

Here are some of the basic components of Flutter that are most commonly used. They are the basic elements that make up the UI interface: containers, rows, columns, absolute positioning layout, text, images, ICONS, etc.

2. Basic components

2.1 Container(Container Components)

The Container component is one of the most commonly used layout components and can be thought of as a DIV in Web development and a View in RN development. It is often used to control size, background color, borders, shadows, inside and outside margins, and how content is arranged. 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,
})
Copy the code

2.1.1 width.height.margin.padding

The meaning of these attributes is not different from what we are already familiar with. The only thing to note is that margin and padding are not simple numbers because they have left, top, right, and bottom values. Flutter provides the EdgeInsets class to help us easily generate values in four directions. In general, there are four possible EdgeInsets constructors:

  • EdgeInsets.all(value): used to set four values in the same direction;
  • EdgeInsets.only(left: val1, top: val2, right: val3, bottom: val4): Can set the value of a direction separately;
  • EdgeInsets.symmetric(horizontal: val1, vertical: val2): used to set values in the horizontal/vertical direction;
  • EdgeInsets.fromLTRB(left, top, right, bottom): Sets the values of the four directions in the upper left and lower right order.

2.1.2 color

This property means the backgroundColor, which is equivalent to backgroundColor in web/rn. Note that Flutter has a Color class that represents a Color, not the usual string. But we can convert it very easily, for example:

In Web/RN we use ‘#FF0000’ or ‘red’ to represent red, while in Flutter we can use Color(0xFFFF0000) or colors.red.

2.1.3 alignment

This property is used to determine how the children of the Container component will be arranged (PS: don’t worry about being centered anymore). Its optional values are usually used:

  • Alignment.topLeft: the upper left
  • Alignment.topCenter: high
  • Alignment.topRightThe upper right of the:
  • Alignment.centerLeft: in the left
  • Alignment.centerCenter:
  • Alignment.centerRight: the right of
  • Alignment.bottomLeft: the lower left
  • Alignment.bottomCenterIn:
  • Alignment.bottomRightOn the left:

2.1.4 constraints

We usually use in the web/rn minWidth/maxWidth/minHeight/maxHeight properties such as high to limit the width of the container. In Flutter, you need to use BoxConstraints (BoxConstraints) to achieve this functionality.

// The size of the container will be limited to [100*100 ~ 200*200]
BoxConstraints(
  minWidth: 100,
  maxWidth: 200,
  minHeight: 100,
  maxHeight: 200.)Copy the code

2.1.5 decoration

This property is very powerful and literally means decoration because it allows you to set common attributes like borders, shadows, gradients, rounded corners, etc. BoxDecoration inherits from the Decoration class, so we usually generate an instance of BoxDecoration to set these properties.

1) border

You can use the Border. All constructor to generate four borders directly, or you can use the Border constructor to set borders in different directions separately. Surprisingly, the official border does not support dotted lines.

// Set 4 borders at the same time: 1px solid black border
BoxDecoration(
  border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
)

// Set a single border: a solid black border with 1px thickness on the top and a solid red border with 1px thickness on the right
BoxDecoration(
  border: Border(
    top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
    right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
  ),
)
Copy the code

2) the shadow

The shadow property is almost indistinguishable from boxShadow on the Web, specifying x, Y, blur, spread, color, and so on.

BoxDecoration(
  boxShadow: [
    BoxShadow(
      offset: Offset(0.0),
      blurRadius: 6,
      spreadRadius: 10,
      color: Color.fromARGB(20.0.0.0),),,)Copy the code

3) the gradient

If you don’t want the background color of the container to be monotonous, try using the gradient attribute. Flutter supports both linear and radial gradients:

// Linear gradient from left to right, red to blue
BoxDecoration(
  gradient: LinearGradient(
    begin: Alignment.centerLeft,
    end: Alignment.centerRight,
    colors: [Colors.red, Colors.blue],
  ),
)

// Diffuse from center to periphery, red to blue radial gradient
BoxDecoration(
  gradient: RadialGradient(
    center: Alignment.center,
    colors: [Colors.red, Colors.blue],
  ),
)
Copy the code

4) rounded corners

In general, you might use the borderradius. circular constructor to round four corners at once, or the borderradius. only constructor to round several corners individually:

// Set the rounded corners of the four corners to 5
BoxDecoration(
  borderRadius: BorderRadius.circular(5),// Set a single rounded corner: the top left rounded corner is 5, and the top right rounded corner is 10
BoxDecoration(
  borderRadius: BorderRadius.only(
    topLeft: Radius.circular(5),
    topRight: Radius.circular(10),),)Copy the code

2.1.6 transform

The transform property is basically the same as the ones we often use in Web/RN, including translation, scaling, rotation, and skew. In Flutter, the matrix transform class Matrix4 is encapsulated to help us transform:

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

2.1.7 summary

The Attributes of the Container component are very rich. Although some of the attributes are slightly different from those of Web/Rn, they are basically the same, so there is no obstacle to transition. Also, because the Container component is a monadic node component, only one child node is allowed. So in layout, a lot of times we use Row and Column components for Row/Column layout.

2.2 Row/Column

The Row and Column components are actually very similar to Flex layouts (elastic boxes) in Web/RN, or so we can put it. Flex layout users are already familiar with the concept of axis and sub-axis. The main axis of a Row component is horizontal, and the main axis of a Column component is vertical. 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>[],
})
Copy the code

2.2.1 mainAxisAlignment

By default, Row and Column components start from start in the main axis direction. That is, Row components start from left to right and Column components start from top to bottom by default.

Of course, there are other optional values you can use:

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

2.2.2 crossAxisAlignment

This property means the subaxis arrangement, and you know from the constructor above that Row and Column components are centered in the subaxis by default.

Here have a bit need special attention: due to the Column component on the time axis (horizontal) default is center aligned, so a horizontal containers won’t stay with his father, at this point you need to specify the CrossAxisAlignment. Stretch.

In addition, crossAxisAlignment other optional values are:

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

2.2.3 mainAxisSize

Literally, this property refers to the size on the main axis. In fact, it refers to whether it wraps its contents in the main axis or fills its parent container. Its optional values are MainAxisSize. Min and MainAxisSize. Max. Since the default is mainAxissize.max, the default size in the main axis direction is as full as possible to the parent container.

2.2.4 summary

Because the Row/Column component is very similar to the familiar Flex layout, it is very easy to get started with almost no learning cost.

2.3 Stack/Positoned

The absolute positioning layout is also one of the most frequently used layouts in Web/RN development. The Flutter also provides a corresponding component implementation, which requires the Stack and the Positioned component to be used together. For example, the following example creates a yellow box and places four small red squares in its four corners. The Stack assembly is an absolutely Positioned container. The Positioned Stack assembly determines its position in the parent container by the values of its properties in the left, top, right and bottom directions.

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),
      ),
    ],
  ),
)
Copy the code

2.4 Text(Text Component)

The Text component is also one of the most commonly used foundation components in daily development, and is often used to present Text information. Take a look at its constructor (unusual attributes have been omitted) :

const Text(
  this.data, {
  Key key,
  this.style,
  this.textAlign,
  this.softWrap,
  this.overflow,
  this.maxLines,
})
Copy the code
  • data: Displays text information.
  • style: Text style,FlutterProvided aTextStyleClass, the most commonly usedfontSize.fontWeight.color.backgroundColorandshadowsAnd other properties are set through it;
  • textAlign: Text alignment. Possible values areTextAligntheleft.right.centerandjustify;
  • softWrap: Whether the text is wrapped;
  • overflow: How to handle text overflow (direct truncation by default). Optional values areTextOverflowtheclip.fade.ellipsisandvisible;
  • maxLines: When the text exceeds the maximum number of lines and is not displayed, theoverflowProperty determines how to truncate processing.

The Text component of Flutter is flexible enough to provide a variety of properties to customize, but in general the following lines of code are more than enough:

Text(
  'Here's the test text',
  style: TextStyle(
    fontSize: 13,
    fontWeight: FontWeight.bold,
    color: Color(0xFF999999),),)Copy the code

In addition to the above application scenarios, we sometimes encounter the need for rich text (that is, different font styles may be required within a single piece of text). For example, in some UI design, it is common to encounter the ¥symbol is smaller than the amount font. For this type of requirement, we can use the text. rich constructor provided by Flutter to create the corresponding Text component:

Text.rich(TextSpan(
  children: [
    TextSpan(
      A '$',
      style: TextStyle(
        fontSize: 12,
        color: Color(0xFFFF7528),
      ),
    ),
    TextSpan(
      '258',
      style: TextStyle(
        fontSize: 15,
        color: Color(0xFFFF7528(), (), [))Copy the code

2.5 Image(Image Component)

Image component, as one of the basic components of rich content, is frequently used in daily development. Take a look at its constructor (unusual attributes have been omitted) :

Image({
  Key key,
  @required this.image,
  this.width,
  this.height,
  this.color,
  this.fit,
  this.repeat = ImageRepeat.noRepeat,
})
Copy the code
  • image: Image sources, most commonly used in two main types (AssetImageandNetworkImage). useAssetImageBefore, you need to be inpubspec.yamlDeclare the image resource in the file, and then use it; whileNextworkImageSpecify the network address of the picture, mainly used when loading some network pictures;
  • width: picture width;
  • height: image height;
  • color: The background color of the picture, which will be displayed before the network picture is loaded.
  • fit: This property is used when we want images to fit according to the container size rather than specifying a fixed width and height. Its optional values areBoxFitthefill.contain.cover.fitWidth.fitHeight.noneandscaleDown;
  • repeat: Determines whether to use the duplicate effect when the actual image size is below the specified size.

Flutter also provides the Image.network and image. asset constructors, which are syntax sugars. For example, the following two pieces of code produce exactly the same result:

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

Image.network(
  'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109, & FM 27 & gp = = 0. 4157195964 JPG',
  width: 100,
  height: 100.)Copy the code

2.6 Icon(Icon component)

Compared with pictures, the Icon component has the advantage of magnification without distortion, and is often used in daily development. Flutter has a set of Material style ICONS built directly into it (you can preview all of them here). Take a look at the constructor:

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

3. Smart mattress

From the introduction of the last section, we have a preliminary understanding of the Container, Row, Column, Stack, toy, Text, Image and Icon components. Let’s use a practical example to deepen our understanding and memory.

3.1 Preparations – Data type

Based on the above card, we can define some fields. To standardize the development process, let’s first define a class of data type for the card so that we can Mock and manage the data better later in the development process:

class PetCardViewModel {
  /// cover address
  final String coverUrl;

  /// User profile picture address
  final String userImgUrl;

  / / / user name
  final String userName;

  // user description
  final String description;

  / / / topics
  final String topic;

  /// release time
  final String publishTime;

  /// Publish content
  final String publishContent;

  /// The number of replies
  final int replies;

  /// like quantity
  final int likes;

  /// Share quantity
  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,
  });
}
Copy the code

3.2 Build the skeleton and split the layout

According to the given visual diagram, we can split the whole into four parts: Cover, UserInfo, PublishContent and InteractionArea. To do this, we can build 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(), ], ), ); }}Copy the code

3.3 Cover Area

In order to provide a better glimpse of the image, a mask is added, so that just the Stack/Positioned position and the LinearGradient can be used. The 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(() [(() [(() [() [() [() [() }Copy the code

3.4 User Information Area

The user information area is a good place to use Row and Column components for layout, and the 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(() [() [() [() [() }Copy the code

3.5 Publishing Content Area

With the UI exercise in this area, we can practice setting different borderRadius values for the Container component and truncating when the Text content of the Text component is exceeded. The 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(() [() [() [() [() }Copy the code

3.6 Interactive Area

In this module, we will use the Icon component, which can control its size and color and other properties. 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(() [[() [[() [() [() [() }Copy the code

3.7 summary

In the example above, we managed to take a seemingly complex UI apart step by step, using all of the components mentioned above and getting good results. In fact, more than 90 percent of daily development requirements depend on the basic components mentioned above. Therefore, with a little practice and familiarity with the basic components of Flutter, you have already taken a big step

There are also bank card and circle of friends UI practice examples, due to the length of the reason not to post the code, you can go to github warehouse to see.

4. To summarize

This article begins with an introduction to the most commonly used basic components of Flutter to build UI interfaces (containers, rows, columns, absolute positioning layouts, text, images, and ICONS). Next, I introduced a more complex UI example in action. By deconstructing the Dom structure layer by layer, the components mentioned above can be used comprehensively, which can also be regarded as consolidating the concept knowledge learned previously.

But at the end of the day, the nesting of Flutter is really uncomfortable… It would be an absolute nightmare to have a UI layout without module splitting. And unlike Web/RN development styles, which can be isolated, the way that Flutter treats styles as attributes is really difficult to understand at first glance. It can take some time for new developers to understand the structure of the DOM…

All the code for this article is hosted here, or follow my Blog.