Виджет Wrap - наш незаменимый помощник

В этой статье я покажу код и примеры работы с этим полезным виджетом. В конце статьи лежит код обучающего приложения. Поиграйте с ним самостоятельно, чтобы лучше понять, как работает виджет Wrap.
Основы
Обычно, когда вы хотите расположить несколько виджетов по горизонтали или вертикали, вы используете виджеты Row или Column.
Row(
children: [
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
],
),

Но если места недостаточно, содержимое обрезается, и вы получаете желто-полосатое предупреждение о переполнении.
Row(
children: [
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
],
),

Чтобы исправить это, можно использовать виджет Wrap вместо Row.
Wrap(
children: [
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
],
),

По умолчанию используется горизонтальный перенос строк, но если требуется вертикальное расположение, нужно установить direction
.
Wrap(
direction: Axis.vertical,
children: [
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
],
),

Вы также можете установить выравнивание и интервал между виджетами. spacing
- это пространство перед следующим виджетом. runSpacing
- это пространство между строками или столбцами.
Wrap(
direction: Axis.vertical,
alignment: WrapAlignment.end,
spacing: 10.0,
runSpacing: 20.0,
children: [
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
],
),

Применение
Очень удобно использовать Wrap, при работе с кнопками:
Wrap(
children: [
MyButton('OK'),
MyButton('Cancel'),
MyButton('Remind me again tomorrow'),
],
),

Или, например, с чипсами:
Wrap(
children: [
MyChip('Andrew Brogdon'),
MyChip('Emily Fortuna'),
MyChip('Filip Hráček'),
],
),

Погружаемся глубже
Вы можете заставить виджеты начинаться с любого угла и располагаться в любом направлении, играя со свойствами direction, verticalDirection и textDirection.
Wrap(
direction: Axis.horizontal, // default
children: [
MyWidget('A'),
MyWidget('B'),
MyWidget('C'),
MyWidget('D'),
MyWidget('E'),
],
),

direction: Axis.vertical,

verticalDirection: VerticalDirection.up,

direction: Axis.vertical,
verticalDirection: VerticalDirection.up,

textDirection: TextDirection.rtl,

textDirection: TextDirection.rtl,
direction: Axis.vertical,

textDirection: TextDirection.rtl,
verticalDirection: VerticalDirection.up,

textDirection: TextDirection.rtl,
direction: Axis.vertical,
verticalDirection: VerticalDirection.up,

Выравнивание с помощью Wrap
В этом разделе показано, как выравнивание влияет на размещение элементов. Ширина и высота установлены в 280, что слишком мало для четырех предметов.
start
Wrap(
alignment: WrapAlignment.start, // default
children: [
MyWidget('A'),
MyWidget('B'),
MyWidget('C'),
MyWidget('D'),
MyWidget('E'),
//MyWidget('F'),
//MyWidget('G'),
],
),

end
alignment: WrapAlignment.end,

center
alignment: WrapAlignment.center,

spaceAround
alignment: WrapAlignment.spaceAround,

spaceBetween
alignment: WrapAlignment.spaceBetween,

spaceEvenly
alignment: WrapAlignment.spaceEvenly,

Еще немного про выравнивание
runAlignment
влияет на выравнивание последовательных строк или столбцов.
start
Wrap(
runAlignment: WrapAlignment.start, // default
children: [
MyWidget('A'),
MyWidget('B'),
MyWidget('C'),
MyWidget('D'),
MyWidget('E'),
],
),

end
runAlignment: WrapAlignment.end,

center
runAlignment: WrapAlignment.center,

spaceAround
runAlignment: WrapAlignment.spaceAround,

spaceBetween
runAlignment: WrapAlignment.spaceBetween,

spaceEvenly
runAlignment: WrapAlignment.spaceEvenly,

Почему мои изменения не вступают в силу?
Одна из причин может заключаться в том, что ширина вашего виджета Wrap получается настолько узкой, насколько это возможно, и нет места для выравнивания. Это может произойти, если он находится внутри столбца. Если это так, попробуйте установить CrossAxisAlignment.stretch
для столбца.
Простое приложение с примерами
Создайте во флаттере новый проект и замените весь код в main.dart на этот:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: WrapWidgetDemo(),
),
);
}
}
class WrapWidgetDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 300,
//height: 300,
color: Colors.blue,
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(10),
child:
// Замените этот код примерами из статьи
Wrap(
children: [
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
MyWidget(),
],
),
),
);
}
}
class MyWidget extends StatelessWidget {
//final String text;
//MyWidget(this.text);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10),
width: 50,
height: 50,
color: Colors.yellow.shade700,
//child: Center(child: Text(text)),
);
}
}
class MyButton extends StatelessWidget {
final String text;
MyButton(this.text);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(5),
child: RaisedButton(
color: Colors.purple,
textColor: Colors.white,
child: Text(text),
onPressed: () {},
),
);
}
}
class MyChip extends StatelessWidget {
final String name;
MyChip(this.name);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Chip(
backgroundColor: Colors.lightGreenAccent,
label: Text(name),
),
);
}
}