1. Введение
Flutter
Как одна из самых популярных технологий в настоящее время, она уже привлекла внимание большого количества энтузиастов технологий, и даже некоторых闲鱼
,美团
,腾讯
и другие крупные компании начали использовать его. Хотя его экология еще не полностью созрела,Google
Благо, скорость его развития уже достаточно поразительна, можно предвидеть, что в будущемFlutter
Спрос на разработчиков также будет расти.
Будь то ранние последователи текущей технологии или будущая тенденция, ей 9102 года. Именно с таким менталитетом автор и начал изучатьFlutter
, и построилсклад, весь последующий код будет размещаться на нем, добро пожаловать звездочка, учитесь вместе. Это серия статей о Flutter, которую я написал:
- Создание красивых интерфейсов пользовательского интерфейса с помощью Flutter — основные компоненты
- Компонент контейнера прокрутки Flutter — ListView
- Компоновка сетки Flutter — статьи GridView
- Использование пользовательского значка во Flutter
Сегодня я поделюсь некоторыми из наиболее часто используемых базовых компонентов во Flutter, которые являются основными элементами, составляющими интерфейс пользовательского интерфейса:容器
,行
,列
,绝对定位布局
,文本
,图片
а также图标
Ждать.
2. Основные компоненты
2.1 Контейнер (контейнерный компонент)
Container
Компонент является одним из наиболее часто используемых компонентов макета и может рассматриваться как один из элементов веб-разработки.div
, р-н в разработкеView
. Его часто можно использовать для управления размером, цветом фона, границами, тенями, полями и расположением содержимого. Давайте сначала посмотрим на его конструктор:
Container({
Key key,
double width,
double height,
this.margin,
this.padding,
Color color,
this.alignment,
BoxConstraints constraints,
Decoration decoration,
this.foregroundDecoration,
this.transform,
this.child,
})
2.1.1 width
,height
,margin
,padding
Смысл этих свойств ничем не отличается от того, что мы уже знаем. Единственное, что следует отметить, это то, чтоmargin
а такжеpadding
Присвоение не является простым числом, потому что оно имеетleft
, top
, right
, bottom
Необходимо установить значения для четырех направлений.Flutter
при условииEdgeInsets
Этот класс помогает нам легко генерировать значения в четырех направлениях. Обычно мы можем использоватьEdgeInsets
4 метода строительства:
-
EdgeInsets.all(value)
: используется для установки одного и того же значения в 4 направлениях; -
EdgeInsets.only(left: val1, top: val2, right: val3, bottom: val4)
: значение определенного направления может быть установлено индивидуально; -
EdgeInsets.symmetric(horizontal: val1, vertical: val2)
: используется для установки значения в горизонтальном/вертикальном направлении; -
EdgeInsets.fromLTRB(left, top, right, bottom)
: Установите значения в 4 направлениях в порядке сверху слева и снизу справа.
2.1.2 color
Смысл этого свойства — цвет фона, который эквивалентен backgroundColor в web/rn. должен быть в курсеFlutter
Есть специальный цветColor
class вместо нашей обычной строки. Но мы можем сделать преобразование очень легко, например:
В web/rn мы будем использовать'#FF0000'
или'red'
для представления красного цвета, а во Flutter мы можем использоватьColor(0xFFFF0000)
илиColors.red
Представлять.
2.1.3 alignment
Это свойство используется для определенияContainer
Как будут расположены подкомпоненты компонента (PS: больше не беспокойтесь о центрировании). Обычно используется его необязательное значение:
-
Alignment.topLeft
: верхний левый -
Alignment.topCenter
: На -
Alignment.topRight
: в правом верхнем углу -
Alignment.centerLeft
: слева посередине -
Alignment.center
: центр -
Alignment.centerRight
: среднее право -
Alignment.bottomLeft
: внизу слева -
Alignment.bottomCenter
: нижняя середина -
Alignment.bottomRight
: Нижний правый
2.1.4 constraints
В web/rn мы обычно используемminWidth
/maxWidth
/minHeight
/maxHeight
и другие свойства для ограничения ширины и высоты контейнера. существуетFlutter
В, вам нужно использоватьBoxConstraints
(Box Constraint) для достижения этой функции.
// 容器的大小将被限制在[100*100 ~ 200*200]内
BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 100,
maxHeight: 200,
)
2.1.5 decoration
Это свойство очень мощное и буквально означает украшение, потому что с его помощью можно установить边框
,阴影
,渐变
,圆角
и другие общие свойства.BoxDecoration
унаследовано отDecoration
класс, поэтому мы обычно генерируемBoxDecoration
экземпляр для установки этих свойств.
1) Граница
Можно использоватьBorder.all
Конструктор напрямую генерирует 4 границы, или вы можете использоватьBorder
Конструктор самостоятельно устанавливает границы в разные стороны. Но удивительно, что официально предоставленная граница не поддерживает虚线
(issueэто здесь).
// 同时设置4条边框:1px粗细的黑色实线边框
BoxDecoration(
border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
)
// 设置单边框:上边框为1px粗细的黑色实线边框,右边框为1px粗细的红色实线边框
BoxDecoration(
border: Border(
top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
),
)
2) Тень
Теневые свойства и сетьboxShadow
Разницы почти нет и можно уточнитьx
,y
,blur
,spread
,color
и другие свойства.
BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(0, 0),
blurRadius: 6,
spreadRadius: 10,
color: Color.fromARGB(20, 0, 0, 0),
),
],
)
3) Градиент
Если вы не хотите, чтобы цвет фона контейнера был монотонным, попробуйте использоватьgradient
Атрибуты.Flutter
Также поддерживает线性渐变
а также径向渐变
:
// 从左到右,红色到蓝色的线性渐变
BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.red, Colors.blue],
),
)
// 从中心向四周扩散,红色到蓝色的径向渐变
BoxDecoration(
gradient: RadialGradient(
center: Alignment.center,
colors: [Colors.red, Colors.blue],
),
)
4) округлые углы
Как правило, вы можете использоватьBorderRadius.circular
конструктор, чтобы установить закругленные углы для всех 4 углов одновременно, илиBorderRadius.only
Конструктор для индивидуальной настройки закругления определенных углов:
// 同时设置4个角的圆角为5
BoxDecoration(
borderRadius: BorderRadius.circular(5),
)
// 设置单圆角:左上角的圆角为5,右上角的圆角为10
BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(10),
),
)
2.1.6 transform
transform
Атрибуты в основном такие же, как те, которые мы часто используем в web/rn, в том числе:平移
,缩放
,旋转
а также倾斜
. Во Flutter класс матричных преобразований инкапсулированMatrix4
Помогите нам преобразовать:
-
translationValues(x, y, z)
: перевести х, у, г; -
rotationX(radians)
: вращение по оси X, радианы, радианы; -
rotationY(radians)
: ось Y поворачивает радианы в радианах; -
rotationZ(radians)
: ось Z поворачивает радианы в радианах; -
skew(alpha, beta)
: ось X наклонена на альфа-градусы, а ось Y наклонена на бета-градусы; -
skewX(alpha)
: альфа-градус наклона оси X; -
skewY(beta)
: бета-градус наклона оси Y;
2.1.7 Резюме
Container
Атрибуты компонентов очень богаты, хотя некоторые варианты использования немного отличаются от web/rn, но в основном они одинаковы, поэтому при переходе не будет никаких препятствий. Кроме того, посколькуContainer
Компонент является компонентом с одним дочерним узлом, то есть разрешен только один дочерний узел. Итак, в макете много раз мы будем использоватьRow
а такжеColumn
компоненты行
/列
макет.
2.2 Ряд/столбец (строка/столбец в сборе)
Row
а такжеColumn
Компонент на самом деле такой же, как и в web/rnFlex布局
(Flexbox) особенно похож, или мы можем понять это так. использоватьFlex布局
одноклассники主轴
а также次轴
Понятие , должно быть очень знакомо.Row
Основная ось компонента горизонтальна,Column
Основной осью сборки является продольное направление. И их конструкторы очень похожи (нечастые свойства опущены):
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
MainAxisSize mainAxisSize = MainAxisSize.max,
List<Widget> children = const <Widget>[],
})
Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
MainAxisSize mainAxisSize = MainAxisSize.max,
List<Widget> children = const <Widget>[],
})
2.2.1 mainAxisAlignment
Смысл этого свойства в расположении главных осей, которое можно узнать из приведенного выше конструктораRow
а такжеColumn
Компоненты начинаются с начала по умолчанию в направлении главной оси, то естьRow
Компоненты по умолчанию располагают подкомпоненты слева направо,Column
Компоненты по умолчанию располагают подкомпоненты сверху вниз.
Конечно, вы также можете использовать другие необязательные значения:
- MainAxisAlignment.start
- MainAxisAlignment.end
- MainAxisAlignment.center
- MainAxisAlignment.spaceBetween
- MainAxisAlignment.spaceAround
- MainAxisAlignment.spaceEvenly
2.2.2 crossAxisAlignment
Смысл этого свойства в расположении второстепенной оси, что можно узнать из приведенного выше конструктораRow
а такжеColumn
По умолчанию компоненты центрируются на вторичной оси.
Вот немного нужнообращать внимание:из-заColumn
Направление вторичной оси компонента (то есть горизонтальное) по умолчанию центрировано, поэтому его родительский контейнер не будет заполняться в горизонтальном направлении.CrossAxisAlignment.stretch
Только тогда может.
Кроме того, другие необязательные значения crossAxisAlignment:
- crossAxisAlignment.start
- crossAxisAlignment.end
- crossAxisAlignment.center
- crossAxisAlignment.stretch
- crossAxisAlignment.baseline
2.2.3 mainAxisSize
Буквально это свойство относится к размеру на главной оси. На самом деле, это относится к тому, оборачивает ли он свое содержимое или заполняет родительский контейнер в направлении главной оси. Его необязательные значенияMainAxisSize.min
а такжеMainAxisSize.max
. Поскольку его значение по умолчанию равноMainAxisSize.max
, поэтому размер по умолчанию в направлении главной оси должен максимально заполнять родительский контейнер.
2.2.4 Резюме
из-заRow
/Column
компоненты и то, с чем мы знакомыFlex布局
Очень похоже, поэтому очень легко начать работу практически без затрат на обучение.
2.3 Стек/Позиционирование (абсолютно позиционированные компоненты макета)
Макет с абсолютным позиционированием также часто используется в веб-разработке.Flutter
Также предоставляется соответствующая реализация компонента, которую необходимоStack
а такжеPositioned
Компоненты используются вместе. Например, в приведенном ниже примере создается желтая коробка и размещаются 4 маленьких красных квадрата в ее четырех углах.Stack
Компоненты — это абсолютно позиционированные контейнеры.Positioned
компоненты проходятleft
,top
,right
,bottom
Значения свойства в четырех направлениях определяют его положение в родительском контейнере.
Container(
height: 100,
color: Colors.yellow,
child: Stack(
children: <Widget>[
Positioned(
left: 10,
top: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
right: 10,
top: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
left: 10,
bottom: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
right: 10,
bottom: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
],
),
)
2.4 Текст (текстовый компонент)
Text
Компоненты также являются одним из наиболее часто используемых базовых компонентов в повседневной разработке, и мы обычно используем его для отображения текстовой информации. Давайте взглянем на его конструктор (нечастые свойства опущены):
const Text(
this.data, {
Key key,
this.style,
this.textAlign,
this.softWrap,
this.overflow,
this.maxLines,
})
-
data
: Отображаемая текстовая информация; -
style
: стиль текста,Flutter
предоставилTextStyle
класс, наиболее часто используемыйfontSize
,fontWeight
,color
,backgroundColor
а такжеshadows
через него задаются и другие свойства; -
textAlign
: Выравнивание текста, часто используемые необязательные значенияTextAlign
изleft
,right
,center
а такжеjustify
; -
softWrap
: переносится ли текст; -
overflow
: Когда текст переполняется, как с этим бороться (по умолчанию прямо обрезается). Необязательные значенияTextOverflow
изclip
,fade
,ellipsis
а такжеvisible
; -
maxLines
: Если текст превышает максимальное количество строк и не был отображен, он будет отображаться в соответствии сoverflow
Свойство определяет, как обрабатывается усечение.
Flutter
изText
Компоненты достаточно гибки, чтобы предоставлять нам различные свойства для настройки, но в целом нам нужны только следующие строки кода:
Text(
'这是测试文本',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Color(0xFF999999),
),
)
В дополнение к вышеперечисленным сценариям применения иногда мы также сталкиваемся富文本
потребности (например, фрагмент текста, для которого может потребоваться другой стиль шрифта). Например, в некоторых дизайнах пользовательского интерфейса часто встречается цена,¥
соотношение знаков金额
меньший размер шрифта. Для таких нужд можно использоватьFlutter
который предоставилText.rich
конструктор для создания соответствующего текстового компонента:
Text.rich(TextSpan(
children: [
TextSpan(
'¥',
style: TextStyle(
fontSize: 12,
color: Color(0xFFFF7528),
),
),
TextSpan(
'258',
style: TextStyle(
fontSize: 15,
color: Color(0xFFFF7528),
),
),
]
))
2.5 Изображение (компонент изображения)
Image
Как один из основных компонентов расширенного контента, компонент изображения очень часто используется в повседневной разработке. Взгляните на его конструктор (нечастые свойства были опущены):
Image({
Key key,
@required this.image,
this.width,
this.height,
this.color,
this.fit,
this.repeat = ImageRepeat.noRepeat,
})
-
image
: Источник изображения, есть два наиболее часто используемых (AssetImage
а такжеNetworkImage
). использоватьAssetImage
Раньше надо былоpubspec.yaml
Ресурсы изображения объявляются в файле до того, как их можно будет использовать; иNextworkImage
Вы можете указать сетевой адрес образа, который в основном используется при загрузке некоторых сетевых образов; -
width
: ширина изображения; -
height
: высота изображения; -
color
: Цвет фона картинки, который будет отображаться перед загрузкой сетевой картинки; -
fit
: 当我们希望图片根据容器大小进行适配而不是指定固定的宽高值时,可以通过该属性来实现。 Его необязательные значенияBoxFit
изfill
,contain
,cover
,fitWidth
,fitHeight
,none
а такжеscaleDown
; -
repeat
: определяет, использовать ли эффект повтора, когда фактический размер изображения меньше указанного размера.
Кроме того,Flutter
также обеспечиваетImage.network
а такжеImage.asset
Конструкторы на самом деле являются синтаксическим сахаром. Например, следующие два фрагмента кода имеют одинаковый результат:
Image(
image: NetworkImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg'),
width: 100,
height: 100,
)
Image.network(
'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg',
width: 100,
height: 100,
)
2.6 Icon
(компонент значка)
Icon
Сборка иконок чаще используется в повседневной разработке, чем картинка, которая чаще используется в повседневной разработке.Flutter
Это прямо встроенныйMaterial
стилизованные значки (вы можетездесьпредварительный просмотр всех типов значков). Взгляните на конструктор:
const Icon(
this.icon, {
Key key,
this.size,
this.color,
})
-
icon
: тип значка; -
size
: размер значка; -
color
: Цвет значка.
3. Макет реального боя
Благодаря введению в предыдущем разделе мы имеемContainer
,Row
,Column
,Stack
,Positioned
,Text
,Image
а такжеIcon
Компоненты имеют предварительное понимание. Далее давайте углубим наше понимание и память на практическом примере.
3.1 Подготовка — типы данных
Основываясь на содержимом приведенной выше карточки, мы можем определить некоторые поля. Чтобы стандартизировать процесс разработки, мы сначала определяем класс типа данных для карты, чтобы данные можно было лучше имитировать и управлять ими в последующем процессе разработки:
class PetCardViewModel {
/// 封面地址
final String coverUrl;
/// 用户头像地址
final String userImgUrl;
/// 用户名
final String userName;
/// 用户描述
final String description;
/// 话题
final String topic;
/// 发布时间
final String publishTime;
/// 发布内容
final String publishContent;
/// 回复数量
final int replies;
/// 喜欢数量
final int likes;
/// 分享数量
final int shares;
const PetCardViewModel({
this.coverUrl,
this.userImgUrl,
this.userName,
this.description,
this.topic,
this.publishTime,
this.publishContent,
this.replies,
this.likes,
this.shares,
});
}
3.2 Создание скелета и разделение макета
По данной визуальной карте мы можем разделить целое на 4 части:Cover
,UserInfo
,PublishContent
а такжеInteractionArea
. Для этого мы можем составить базовый скелет кода:
class PetCard extends StatelessWidget {
final PetCardViewModel data;
const PetCard({
Key key,
this.data,
}) : super(key: key);
Widget renderCover() {
}
Widget renderUserInfo() {
}
Widget renderPublishContent() {
}
Widget renderInteractionArea() {
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
blurRadius: 6,
spreadRadius: 4,
color: Color.fromARGB(20, 0, 0, 0),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
this.renderCover(),
this.renderUserInfo(),
this.renderPublishContent(),
this.renderInteractionArea(),
],
),
);
}
}
3.3 Площадь покрытия
Для того, чтобы лучше подчеркнуть эффект от картинки, сюда добавлена маска, поэтому ее можно просто использовать здесьStack
/Positioned
макет иLinearGradient
Структура Gradient, Dom выглядит следующим образом:
Widget renderCover() {
return Stack(
fit: StackFit.passthrough,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
child: Image.network(
data.coverUrl,
height: 200,
fit: BoxFit.fitWidth,
),
),
Positioned(
left: 0,
top: 100,
right: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromARGB(0, 0, 0, 0),
Color.fromARGB(80, 0, 0, 0),
],
),
),
),
),
],
);
}
3.4 Область информации о пользователе
Область пользовательской информации очень удобна для использованияRow
а такжеColumn
Компоненты используются для макета.Структура Dom выглядит следующим образом:
Widget renderUserInfo() {
return Container(
margin: EdgeInsets.only(top: 16),
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
CircleAvatar(
radius: 20,
backgroundColor: Color(0xFFCCCCCC),
backgroundImage: NetworkImage(data.userImgUrl),
),
Padding(padding: EdgeInsets.only(left: 8)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
data.userName,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
Padding(padding: EdgeInsets.only(top: 2)),
Text(
data.description,
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
],
),
],
),
Text(
data.publishTime,
style: TextStyle(
fontSize: 13,
color: Color(0xFF999999),
),
),
],
),
);
}
3.5 Область публикации содержимого
С помощью упражнений пользовательского интерфейса в этой области мы можем практиковатьContainer
Компоненты установлены по-разномуborderRadius
,так же какText
Обработка усечения, когда текстовое содержимое компонента превышает структуру Dom, выглядит следующим образом:
Widget renderPublishContent() {
return Container(
margin: EdgeInsets.only(top: 16),
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 14),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Color(0xFFFFC600),
borderRadius: BorderRadius.only(
topRight: Radius.circular(8),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: Text(
'# ${data.topic}',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
),
),
Text(
data.publishContent,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
],
),
);
}
3.6 Интерактивная область
В этом модуле мы будем использоватьIcon
Компонент Icon, вы можете управлять его свойствами, такими как размер и цвет, структура Dom выглядит следующим образом:
Widget renderInteractionArea() {
return Container(
margin: EdgeInsets.symmetric(vertical: 16),
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Icon(
Icons.message,
size: 16,
color: Color(0xFF999999),
),
Padding(padding: EdgeInsets.only(left: 6)),
Text(
data.replies.toString(),
style: TextStyle(
fontSize: 15,
color: Color(0xFF999999),
),
),
],
),
Row(
children: <Widget>[
Icon(
Icons.favorite,
size: 16,
color: Color(0xFFFFC600),
),
Padding(padding: EdgeInsets.only(left: 6)),
Text(
data.likes.toString(),
style: TextStyle(
fontSize: 15,
color: Color(0xFF999999),
),
),
],
),
Row(
children: <Widget>[
Icon(
Icons.share,
size: 16,
color: Color(0xFF999999),
),
Padding(padding: EdgeInsets.only(left: 6)),
Text(
data.shares.toString(),
style: TextStyle(
fontSize: 15,
color: Color(0xFF999999),
),
),
],
),
],
),
);
}
3.7 Резюме
В приведенном выше примере мы успешно шаг за шагом разобрали сложно выглядящий пользовательский интерфейс, использовали все компоненты, упомянутые ранее, и, наконец, получили хороший эффект. На самом деле более 90% потребностей в ежедневном развитии неотделимы от упомянутых выше базовых компонентов. Поэтому, при небольшой практике, знакомствеFlutter
Использование базовых компонентов в , это уже большой шаг~
здесь такжебанковская картаа такжеКруг друзейПрактический пример пользовательского интерфейса, из-за недостатка места код не будет опубликован, вы можете перейти крепозиторий githubСмотреть.
4. Резюме
Эта статья впервые знакомитFlutter
Наиболее часто используемые базовые компоненты для создания UI-интерфейсов (容器
,行
,列
,绝对定位布局
,文本
,图片
а также图标
)Применение. Далее представлен более сложный пример боя с пользовательским интерфейсом. Благодаря поэтапному разбору структуры Домов всесторонне использовались упомянутые выше компоненты, что можно рассматривать как закрепление ранее изученных концептуальных знаний.
Но в конце концов я должен выплюнуть:Flutter
Гнездование действительно сложное. . . Без модуля, разделяющего макет пользовательского интерфейса, это был бы абсолютный кошмар. И в отличие от стиля разработки web/rn, который можно выделить отдельно,Flutter
Такой подход к обработке стилей как атрибутов затрудняет понимание структуры DOM с первого взгляда, а разработчикам, которые плохо знакомы с кодом, требуется некоторое время для понимания. . .
Весь код в этой статье размещен по адресуздесь, вы также можете подписаться на меняBlog.