Developing a UI with Flutter

Hi, Habr! We present to your attention the translation of the article " Building Layouts ".

Today we will find out:


  • How UI build mechanics work on Flutter
  • How to typeset screens horizontally and vertically
  • How to roll up the screen using Flutter

The result of today's lesson will be the following screen

image


Step 0: Configure the project


First, create a new project File -> New Flutter Project -> next, next, next ...
Next, you need to create the images directory in the project root and put the file named lake.jpg - you can download the file from here - link

You also need to correct the configuration the pubspec.yaml file (something like gradle for android and cocoa pods in iOS, in it we can add external dependencies). The very text of the file can be downloaded here. The

source code of the project can be downloaded here - it must be placed in the file main.dart

Step 1: Screen Diagram


To begin, we divide the layout into simple elements.

  • Define rows and columns
  • Determine whether the layout of the grid?
  • Are there any overlapping elements
  • Need tabs for user interface?
  • Pay attention to areas that need alignment or padding.

First, we define the main large elements. In this example, 4 elements are arranged in the form of a column: a picture, two rows and a test block.

image

Next, we analyze each row. The first line, which is called the "Title", has 3 children - a column of text, an icon of a star and a number. The first column contains 2 rows. The first column takes up a lot of space, so you should wrap it in an extensible widget.

image

The next row, called the button section, also has 3 children. Each of them contains a picture and text.

image

Finally we laid out the layout into simple elements. The easiest way is to use a bottom-up approach for display layout. In order to avoid complex structure, break the UI into variables and functions.

Step 2: Build a header series


First, we need to build the left column of the header section. Inserting a column inside an expandable widget stretches the column to use all the remaining space in the row. Set the crossAxisAlignment property for CrossAxisAlignment.start to align the column to the beginning of the row.

Placing a line of text inside the container allows you to activate indents. The second child element in the column is also text, it is displayed in gray. The last two elements of the star icon are drawn in red and the text with the value “41”. Place the whole line in the container and add indents of 32 pixels on each side. The code to perform these actions is presented below.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Widget titleSection = Container(
      padding: const EdgeInsets.all(32.0),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: const 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'),
        ],
      ),
    );
  //...
}

Step 3: Build a row of buttons


The button section consists of 3 columns, which are built according to a similar principle - the icon above the text line. The column in this line is filled evenly and the text and icons are drawn in the main color, which is chosen blue in our project in the build () method.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
    //...
}

Since the build code of each row will be almost identical, it will be most efficient to use a nested function, such as buildButtonColumn (), which includes an icon and text and returns a column with this widget.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...
    Column buildButtonColumn(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: const EdgeInsets.only(top: 8.0),
            child: Text(
              label,
              style: TextStyle(
                fontSize: 12.0,
                fontWeight: FontWeight.w400,
                color: color,
              ),
            ),
          ),
        ],
      );
    }
  //...
}

The build function adds an icon directly to the column. Place the text in the container in order to add indents and separate it from the icon. Construct each row of these columns by calling a function, and passing the icon and text inside the column. Align the column along the main axis using MainAxisAlignment.spaceEvenly, arranging free space before, between and after each column.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...
    Widget buttonSection = Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          buildButtonColumn(Icons.call, 'CALL'),
          buildButtonColumn(Icons.near_me, 'ROUTE'),
          buildButtonColumn(Icons.share, 'SHARE'),
        ],
      ),
    );
  //...
}

Step 4: Build the description section


We define a description section that is rather long. Place the text in the container and add padding 32 pixels from each edge.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...
    Widget textSection = Container(
      padding: const EdgeInsets.all(32.0),
      child: 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.
        ''',
        softWrap: true,
      ),
    );
  //...
}

Step 5: Build a section with an image


Three of the four columns have already been built, it remains to make only the image column. The image that is used in this project is available online under the Creative Commons license. But it is big and it will load slowly. In step 0, we added an image to our project and updated the configuration file, now we add a link to it in our code.

return MaterialApp(
//...
body: ListView(
  children: [
    Image.asset(
      'images/lake.jpg',
      height: 240.0,
      fit: BoxFit.cover,
    ),
    // ...
  ],
),
//...
);

BoxFit.cover tells the Flutter framework that the image should be as small as possible, but cover the entire rendering area.

Step 6: Combine It All Together


In the final step we will collect all the pieces of our code together. The widget is organized in a ListView, not a Column, because the ListView automatically scrolls while scrolling on a small device.

//...
return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Top Lakes'),
        ),
        body: ListView(
          children: [
            Image.asset(
              'images/lake.jpg',
              width: 600.0,
              height: 240.0,
              fit: BoxFit.cover,
            ),
            titleSection,
            buttonSection,
            textSection,
          ],
        ),
      ),
    );
//...

Also popular now: