Wechat Pay page is very suitable for function navigation. This article uses ListView and GridView to imitate the wechat Pay page, and introduces how to decorate the background and edge style of a component.

On the left is the wechat Pay interface, and on the right is the finished development effect, with ICONS downloaded from iconfont. First, I’ll introduce the components covered in this article.

Container with decorative effect

In practice, we often encounter a container that needs additional styling, such as rounded corners, background color, etc. In Flutter, there is a decoration attribute for various containers, which can be used to decorate containers. Typical uses include setting background colors, rounded corners, borders, and shadows. The background color can be a gradient. BoxDecoration is a decoration object. The most common one is BoxDecoration. The BoxDecoration properties are as follows:

const BoxDecoration({
    this.color,
    this.image,
    this.border,
    this.borderRadius,
    this.boxShadow,
    this.gradient,
    this.backgroundBlendMode,
    this.shape = BoxShape.rectangle,
  }) 
Copy the code

“BorderRadius” is the border corner of the border. “boxShadow” is the shadow of the container. “Gradient” is the gradient. BackgroundBlendMode is the mixed model with the container, default is overlay, shape is the background shape, default is rectangle. We usually only choose one type of background. Take the green arc background above as an example, and add a little gradient (multiple gradients are supported, which can be adjusted as needed). The sample code is as follows:

return Container(
      / /...
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(4.0),
        gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFF56AF6D),
              Color(0xFF56AA6D),),),/ /...
    );
Copy the code

Here we set the corner as an arc with a radius of 4 and use a gradient to fill. The gradient direction is from top center to bottom center. There are two gradients.

Row layout and Column layout

This was discussed earlier in List 5, where Row stands for Row layout (that is, the children are arranged in a Row) and Column stands for Column layout (that is, the children are arranged in a Column). See Introduction to Flutter and Combat (5) for an illustrated list.

ListView List component

The list view, as in the previous article, is used differently in this article to enable the entire page to be scrolled as a list. Instead of using an array to build list elements, the list components are placed directly into the children property of the list, similar to the use of the scroll view.

GridView Grid component

The GridView is used to divide a container into rows and columns. You can specify the number of elements on the main axis (depending on the scrolling direction), and then automatically fill the grid with the total number of elements. For example, when scrolling vertically, you can specify the number of grids in a row direction, one element per grid. If the number of rows exceeds the limit, the system automatically changes to another row. The simplest way to use it is to build a GridView using the gridview. count method.

GridView.count(
   crossAxisSpacing: gridSpace,
   mainAxisSpacing: gridSpace,
   crossAxisCount: crossAxisCount,
   // Set the following two parameters to disable the GridView scrolling and prevent conflicts with the ListView
   shrinkWrap: true,
   physics: NeverScrollableScrollPhysics(),
   children: buttons.map((item) {
      return _getMenus(item['icon'], item['name'], color: textColor);
    }).toList(),
);
Copy the code

Here crossAxisSpacing is the spacing between elements that are perpendicular to the scrolling direction, or the spacing between elements in horizontal rows if scrolling vertically (the default). MainAxisSpacing is the spacing of elements in the same direction as the scroll. Children are elements in the grid. Note here that since the GridView is nested within the ListView in this case, both components scroll vertically, which would cause a conflict and the layout would not satisfy the constraint. Therefore, set the shrinkWrap here is true and physics as NeverScrollableScrollPhysics, to prohibit the GridView rolling, so as to satisfy the constraints.

Code implementation

  1. First, we will analyze the layout. All menu buttons have the same layout. We can use a unified column layout to complete the menu buttons and improve the reusability. Menu buttons from top to next are icon, spacing (between icon and text), and menu name. The implementation code is as follows:
Column _getMenus(String icon, String name, {Color color = Colors.black}) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      SizedBox(
        child: Image.asset(icon),
        width: 50,
        height: 50,
      ),
      SizedBox(
        height: 5,
      ),
      Text(name, style: TextStyle(fontSize: 14.0, color: color, height: 2)),,);Copy the code

A single menu is implemented by transferring an icon name, a menu name, and an optional font color (the top area is different from the rest of the text).

  1. Next look at the top area, the top area has only two buttons, using decorative containers to achieve the background decoration and rounded corners. Then use row layout to arrange the two menu buttons horizontally evenly. Also, use the Center layout to keep the two menus centered. The height of the container is specified here because it is too short for aesthetics and the actual development will depend on the UI design draft.
Widget _headerGridButtons() {
    double height = 144;
    List<Map<String.String>> buttons = GridMockData.headerGrids();
    return Container(
      height: height,
      margin: EdgeInsets.fromLTRB(MARGIN, MARGIN, MARGIN, MARGIN / 2),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(4.0),
        gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFF56AF6D),
              Color(0xFF56AA6D),
            ]),
      ),
      child: Center(
        child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: buttons
                .map((item) =>
                    _getMenus(item['icon'], item['name'], color: Colors.white))
                .toList()),
      ),
    );
  }
Copy the code
  1. All other menu layouts are the same, except for the area title, the number of menus, and the contents of the menu. Therefore, you can encapsulate a common way to build any type of menu, and set the font style, rounded corner background, and other properties of the area title. The menu layout is implemented using GridView, and because the menu layout is the same, you can encapsulate a general method to specify the number of buttons in a row of grid, button font color and other properties, to achieve code reuse.
Widget _dynamicGridButtons(List<Map<String.String>> buttons, String title,
      {int crossAxisCount = 4{})return Container(
      margin: EdgeInsets.fromLTRB(MARGIN, MARGIN, MARGIN, MARGIN / 2),
      padding: EdgeInsets.all(MARGIN),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(4.0),
        color: Colors.white,
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: TextStyle(color: Colors.grey[700]),
          ),
          SizedBox(height: 20),
          _gridButtons(buttons, crossAxisCount, textColor: Colors.black),
        ],
      ),
    );
  }

GridView _gridButtons(List<Map<String.String>> buttons, int crossAxisCount,
      {Color textColor = Colors.white}) {
    double gridSpace = 5.0;
    return GridView.count(
      crossAxisSpacing: gridSpace,
      mainAxisSpacing: gridSpace,
      crossAxisCount: crossAxisCount,
      // Set the following two parameters to disable the GridView scrolling and prevent conflicts with the ListView
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      children: buttons.map((item) {
        return _getMenus(item['icon'], item['name'], color: textColor); }).toList(), ); }}Copy the code
  1. ListView builds the entire page: The actual page is as simple as putting each area into the ListView’s children property. As you can see here, making the subcomponents as detailed as possible not only improves code reusability, but also reduces the nesting level and improves code readability and maintainability.
@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          _headerGridButtons(),
          _dynamicGridButtons(GridMockData.financeGrids(), 'Financial Planning'),
          _dynamicGridButtons(GridMockData.serviceGrids(), 'Life Services'),
          _dynamicGridButtons(GridMockData.thirdpartyGrids(), 'Shopping consumption'),,),); }Copy the code
  1. Mock Data Preparation

In this case, we just return a List<Map<String, String>> array object with the icon file name for each menu and the menu name. Below is the menu Mock method for the financial service region.

static List<Map<String.String>> financeGrids() {
    return[{'name': 'Credit Card payment'.'icon': 'images/grid-buttons/grid-1-1.png'},
      {'name': 'money'.'icon': 'images/grid-buttons/grid-1-2.png'},
      {'name': 'money'.'icon': 'images/grid-buttons/grid-1-3.png'},
      {'name': 'insurance'.'icon': 'images/grid-buttons/grid-1-4.png'},]; }Copy the code
  1. Other areas for improvement: As you can see from the code, the button is accessed using the Map key, which requires [‘name’] or [‘icon’] to access, which is very difficult to code and easy to misspell. Therefore, the actual use should be to convert the Json object (i.e., Map) into an entity class, so that you can access the attributes of the entity class to set the menu parameters, which is actually easier to maintain.

Conclusion: The basic UI component library Flutter provides can satisfy most complex page layouts by combining various layout components. Therefore, it is important to be familiar with the features of the underlying layout components. At the same time, we should pay attention to the split and pull out of components to complete the encapsulation of sub-components, improve the reusability and avoid the problem of too deep nesting level.