Flutter быстро становится одной из ведущих технологий в разработке приложений, а потому эта платформа важна даже для тех, кто совсем недавно попал в эту среду. Опытным пользователям HTML, CSS и JavaScript может показаться странным и непривычным то, как работает Flutter. В этом мануале мы попробуем упростить всё до самых основ структурирования элементов в нашем приложении, чтобы создать простой макет с помощью компонентов Material.
Требования
Если вам нужно установить Flutter и настроить эмулятор, обратитесь к нашему мануалу Как создать свое первое приложение Flutter. Знакомство с официальной документацией также не повредит.
Скаффолд
Scaffold создает экземпляр основной структуры приложения: обычно он включает все, что согласовано в приложении (например, навигацию и другие панели). Затем мы настраиваем в body более интересную часть нашего приложения. А абстрагирование его от runApp позволит нам использовать горячую перезагрузку.
Рассмотрим такой пример файла main.dart:
import 'package:flutter/material.dart'; void main() { runApp( MaterialApp( home: Scaffold( appBar: AppBar( centerTitle: true, title: Text('I\'m an App'), backgroundColor: Colors.red[600], ), body: App(), ), ), ); } class App extends StatelessWidget { @override Widget build(BuildContext context) { return Container(); } }
В результате вы получите красный экран, вверху которого будет отладочная панель с надписью «I’m an App».
Контейнеры
Как и в случае с HTML-элементами div, мы можем обернуть контейнеры, чтобы получить больше контроля над элементами, когда мы не можем управлять ими самими (не каждый виджет имеет такие свойства, как ширина или отступ). Контейнеры поддерживают некоторые из свойств CSS, такие как height, width, padding и margin. По умолчанию родительские элементы занимают максимальное пространство своих дочерних элементов, а пустые контейнеры пытаются занять максимальное пространство своего родительского элемента.
Внутренние поля и внешние отступы
Управление внутренними и внешними полями может показаться немного странным: вместо того, чтобы напрямую устанавливать отступ или поле в пикселях, Flutter просит установить его в свойство в EdgeInsets и передать значение в пикселях. Существует также виджет Padding, в который вы можете заключить свои элементы, но вам все равно нужно передать значения так, как описано выше.
- EdgeInsets.all()
- EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0)
- EdgeInsets.symmetric(vertical: 0, horizontal: 0)
- EdgeInsets.fromLTRB(left, top, right, bottom) – просто принимает значения без их явного присваивания.
Давайте взглянем на такой файл main.dart:
class App extends StatelessWidget {
@override Widget build(BuildContext context) { return Container( color: Colors.blue, margin: EdgeInsets.only(top: 20, right: 50), child: Container( color: Colors.green, // Add 200px on top and bottom margin: EdgeInsets.symmetric(vertical: 200), child: Container( color: Colors.yellow, margin: EdgeInsets.fromLTRB(0, 20, 200, 20), ), ), ); } }
Теперь тот экран, который мы видели ранее, будет состоять из разноцветных контейнеров.
Столбцы и строки
Главная проблема контейнеров заключается в том, что у нас может быть только по одному дочернему элементу в каждом. Но виджеты столбцов и строк позволяют использовать более гибкую версию контейнеров, контролировать направление элементов. Им нельзя просто передать дочерний элемент для каждого элемента; здесь нужно установить дочерние элементы в массив типа <Widget>, а затем передать каждый из элементов.
Если мы поместим контейнер внутрь виджета Expanded, он займет максимальное пространство своего столбца или строки.
Подобно flexbox, мы можем использовать mainAxisAlignment и crossAxisAlignment, чтобы сделать то же самое, что делают justify-content и align-items. Здесь даже есть такие параметры, как start, center, end, spaceEvenly, spaceAround, spaceBetween и stretch. Давайте взглянем на такой файл main.dart:
class App extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ // Row 1 Row( children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Container( color: Colors.blue, height: 40, width: 40, child: Text('2')), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], ), // Row 2 Row( children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), //Will expand to fill all remaining space Expanded( child: Container( color: Colors.green, height: 40, width: 40, child: Text('2'))), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], ), //Row 3 Container( height: 100, child: Row( //Stretches to vertically fill its parent container crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Expanded( child: Container( color: Colors.green, height: 40, width: 40, child: Text('2'))), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], )), // Row 4 Row( //Creates even space between each item and their parent container mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], ) ]); }
}
Красный экран, который мы видели еще в первом примере, теперь будет разделен на нумерованные строки и столбцы.
Заключение
На первый взгляд работа над макетами во Flutter может показаться немного неуклюжей и сложной по сравнению с привычными технологиями. Но также следует иметь в виду, что здесь нам не приходится сталкиваться с динамическими сетками или раздутыми запросами для каждого экрана. Также иногда макеты во Flutter кажутся менее мощными, чем в CSS, к которому все привыкли. Тем не менее, всего несколько контейнеров, столбцов, строк и интервалов могут легко предоставить вам больше структур, чем вам когда-либо понадобится.