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()
),
],
)
)
)
]
)
)
);
}
}
Окончательный вывод приложения выглядит следующим образом -