Flutter - Введение в макеты

Поскольку основная концепция Flutter заключается в том, что все является виджетом , Flutter включает функциональность компоновки пользовательского интерфейса в сами виджеты. Flutter предоставляет множество специально разработанных виджетов, таких как Контейнер, Центр, Выравнивание и т. Д., Только с целью создания пользовательского интерфейса. Виджеты, создаваемые путем компоновки других виджетов, обычно используют виджеты макета. Давайте изучим концепцию макета Flutter в этой главе.

Тип макета виджетов

Виджеты макета можно сгруппировать в две отдельные категории в зависимости от их дочерних элементов:

  • Виджет, поддерживающий одного ребенка
  • Виджет с поддержкой нескольких дочерних элементов

Давайте познакомимся с типом виджетов и их функциями в следующих разделах.

Отдельные дочерние виджеты

В этой категории виджеты будут иметь только один виджет в качестве своего дочернего, и каждый виджет будет иметь особую функциональность макета.

Например, виджет « Центр» просто центрирует свой дочерний виджет по отношению к его родительскому виджету, а виджет- контейнер обеспечивает полную гибкость для размещения его дочернего элемента в любом заданном месте внутри него с использованием различных параметров, таких как заполнение, украшение и т. Д.

Отдельные дочерние виджеты - отличные варианты для создания высококачественных виджетов с единственной функциональностью, такой как кнопка, метка и т. Д.

Код для создания простой кнопки с использованием виджета- контейнера следующий:

class MyButton extends StatelessWidget {
   MyButton({Key key}) : super(key: key); 

   @override 
   Widget build(BuildContext context) {
      return Container(
         decoration: const BoxDecoration(
            border: Border(
               top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
               bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
            ),
         ),
         child: Container(
            padding: const
            EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
            decoration: const BoxDecoration(
               border: Border(
                  top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
                  bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
               ),
               color: Colors.grey,
            ),
            child: const Text(
               'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black)
            ), 
         ), 
      ); 
   }
}

Здесь мы использовали два виджета - виджет контейнера и виджет текста . Результатом виджета является настраиваемая кнопка, как показано ниже -

Давайте проверим некоторые из наиболее важных виджетов с одним дочерним макетом, предоставляемые Flutter -

  • Padding- Используется для размещения своего дочернего виджета по заданному отступу. Здесь заполнение может быть предоставлено классом EdgeInsets .

  • Align- Выровняйте дочерний виджет внутри себя, используя значение свойства alignment . Значение свойства выравнивания может быть предоставлено классом FractionalOffset . Класс FractionalOffset определяет смещения в терминах расстояния от верхнего левого угла.

Некоторые из возможных значений смещений следующие:

  • FractionalOffset (1.0, 0.0) представляет верхний правый угол.

  • FractionalOffset (0,0, 1,0) представляет нижний левый угол.

Пример кода о смещениях показан ниже -

Center(
   child: Container(
      height: 100.0, 
      width: 100.0, 
      color: Colors.yellow, child: Align(
         alignment: FractionalOffset(0.2, 0.6),
         child: Container( height: 40.0, width:
            40.0, color: Colors.red,
         ), 
      ), 
   ), 
)
  • FittedBox - Он масштабирует дочерний виджет, а затем позиционирует его в соответствии с заданной посадкой.

  • AspectRatio - Он пытается изменить размер дочернего виджета до указанного соотношения сторон.

  • ConstrainedBox

  • Baseline

  • FractinallySizedBox

  • IntrinsicHeight

  • IntrinsicWidth

  • LiimitedBox

  • OffStage

  • OverflowBox

  • SizedBox

  • SizedOverflowBox

  • Transform

  • CustomSingleChildLayout

Наше приложение hello world использует виджеты макета на основе материалов для разработки домашней страницы. Давайте изменим наше приложение hello world, чтобы создать домашнюю страницу с использованием основных виджетов макета, как указано ниже -

  • Container - Общий виджет-контейнер с одним дочерним элементом в виде ящика с выравниванием, заполнением, границей и полями, а также богатыми функциями стилизации.

  • Center - Простой виджет-контейнер с одним дочерним элементом, который центрирует его дочерний виджет.

Измененный код виджетов MyHomePage и MyApp выглядит следующим образом:

class MyApp extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return MyHomePage(title: "Hello World demo app");
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;
   @override
   Widget build(BuildContext context) {
      return Container(
         decoration: BoxDecoration(color: Colors.white,),
         padding: EdgeInsets.all(25), child: Center(
            child:Text(
               'Hello World', style: TextStyle(
                  color: Colors.black, letterSpacing: 0.5, fontSize: 20,
               ),
               textDirection: TextDirection.ltr,
            ),
         )
      );
   }
}

Вот,

  • Виджет- контейнер - это виджет верхнего уровня или корневой виджет. Контейнер настраивается с помощью свойства украшения и заполнения для размещения его содержимого.

  • BoxDecoration имеет множество свойств, таких как цвет, граница и т. Д., Для украшения виджета « Контейнер», и здесь цвет используется для установки цвета контейнера.

  • обивка из контейнера виджета устанавливается с помощью dgeInsets класса, который предоставляет возможность указать значение заполнения.

  • Центр - это дочерний виджет виджета « Контейнер» . Опять же, Text является дочерним элементом виджета Center . Текст используется для отображения сообщения, а Центр используется для центрирования текстового сообщения относительно родительского виджета, Контейнера .

Конечным результатом приведенного выше кода является образец макета, как показано ниже -

Несколько дочерних виджетов

В этой категории у данного виджета будет несколько дочерних виджетов, и макет каждого виджета уникален.

Например, виджет « Строка» позволяет размещать свои дочерние элементы в горизонтальном направлении, тогда как виджет « Столбец» позволяет размещать дочерние элементы в вертикальном направлении. Комбинируя строку и столбец , можно построить виджет любого уровня сложности.

Давайте изучим некоторые из часто используемых виджетов в этом разделе.

  • Row - Позволяет расположить своих детей горизонтально.

  • Column - Позволяет расположить своих детей вертикально.

  • ListView - Позволяет расположить его дочерние элементы в виде списка.

  • GridView - Позволяет оформить его детей как галерею.

  • Expanded - Используется для того, чтобы дочерние элементы виджетов Row и Column занимали максимально возможную площадь.

  • Table - Табличный виджет.

  • Flow - Виджет на основе потока.

  • Stack - Виджет на основе стека.

Приложение Advanced Layout

В этом разделе давайте узнаем, как создать сложный пользовательский интерфейс списка продуктов с индивидуальным дизайном, используя как один, так и несколько дочерних виджетов макета.

Для этого следуйте приведенной ниже последовательности -

  • Создайте новое приложение Flutter в студии Android, product_layout_app .

  • Замените код main.dart следующим кодом -

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override 
   Widget build(BuildContext context) {
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData( 
         primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page'),
      ); 
   } 
} 
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
      
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),), 
         body: Center(child: Text( 'Hello World', )), 
      ); 
   }
}
  • Here,

  • Мы создали виджет MyHomePage , расширив StatelessWidget вместо StatefulWidget по умолчанию, а затем удалили соответствующий код.

  • Теперь создайте новый виджет ProductBox в соответствии с указанным дизайном, как показано ниже -

  • Код для ProductBox следующий.

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) 
      : super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 

   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), height: 120,  child: Card( 
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
                  Image.asset("assets/appimages/" +image), Expanded(
                     child: Container(
                        padding: EdgeInsets.all(5), child: Column(
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[ 
                              
                              Text(this.name, style: TextStyle(fontWeight: 
                                 FontWeight.bold)), Text(this.description), 
                              Text("Price: " + this.price.toString()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
  • Пожалуйста, обратите внимание на следующее в коде -

  • ProductBox использовал четыре аргумента, как указано ниже -

    • name - Название продукта

    • description - Описание продукта

    • price - Цена товара

    • image - Изображение товара

  • ProductBox использует семь встроенных виджетов, как указано ниже -

    • Container
    • Expanded
    • Row
    • Column
    • Card
    • Text
    • Image
  • ProductBox разработан с использованием вышеупомянутого виджета. Расположение или иерархия виджета указана на схеме, показанной ниже -

  • Теперь поместите какое-то фиктивное изображение (см. Ниже) для информации о продукте в папку с ресурсами приложения и настройте папку с ресурсами в файле pubspec.yaml, как показано ниже -

assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png

iPhone.png

Pixel.png

Ноутбук.png

Tablet.png

Pendrive.png

Floppy.png

Наконец, используйте ProductBox виджет в MyHomePage виджет , как указано ниже -

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title:Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget> [
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
  • Здесь мы использовали ProductBox как дочерние элементы виджета ListView .

  • Полный код (main.dart) приложения макета продукта (product_layout_app) выглядит следующим образом:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', theme: ThemeData(
            primarySwatch: Colors.blue,
         ), 
         home: MyHomePage(title: 'Product layout demo home page'), 
      );
   }
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   @override 
   Widget build(BuildContext context) { 
      return Scaffold( 
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[ 
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox( 
                  name: "Pixel",    
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox( 
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 120, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column(    
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ),
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}

Окончательный вывод приложения выглядит следующим образом -