Irbisa · Шпаргалка

Базовые виджеты

Основные виджеты макета: Container, Row, Column, Stack, ListView

Junior-разработчик · 24 вопроса

  1. 01

    В чём разница между Container, SizedBox, Padding и ColoredBox?

    SizedBox, Padding и ColoredBox делают по одной вещи.

  2. 02

    Что произойдёт при построении этого Container и как это исправить?

    Container(  color: Colors.white,  padding: const EdgeInsets.all(16),  decoration: BoxDecoration(    borderRadius: BorderRadius.circular(12),  ),  child: const Text('Card'),)

    В debug-сборке сработает assert в конструкторе: «Cannot provide both a color and a decoration».

  3. 03

    Что в Row и Column задают mainAxisAlignment, crossAxisAlignment и mainAxisSize?

    Главной осью называют направление, в котором выкладываются дети (горизонталь у Row, вертикаль у Column); поперечная ось перпендикулярна ей.

    Row(  mainAxisAlignment: MainAxisAlignment.spaceBetween,  crossAxisAlignment: CrossAxisAlignment.baseline,  textBaseline: TextBaseline.alphabetic, // required for baseline  children: [    Text('Total', style: Theme.of(context).textTheme.titleMedium),…
  4. 04

    В чём разница между Expanded и Flexible?

    Expanded представляет собой Flexible с fit: FlexFit.tight: ребёнок обязан заполнить ровно выделенную ему долю свободного места.

    // Expanded: both panels fill the row, 1/3 and 2/3Row(  children: [    Expanded(child: Container(color: Colors.red)),    Expanded(flex: 2, child: Container(color: Colors.blue)),  ],…
  5. 05

    Что делают AspectRatio, FittedBox и ConstrainedBox и когда каждый из них нужен?

    Все три ничего не рисуют, а меняют размер: AspectRatio подбирает размер с заданным соотношением ширины и высоты, FittedBox масштабирует ребёнка под доступное место, а ConstrainedBox добавляет min/max-ограничения поверх ограничений родителя.

    // 16:9 box at any screen widthAspectRatio(  aspectRatio: 16 / 9,  child: Image.network(thumbnailUrl, fit: BoxFit.cover),)…
  6. 06

    Как работает Stack вместе с Positioned и когда его выбирают вместо Row или Column?

    Stack рисует детей друг поверх друга, последний оказывается сверху.

    // A caption over a photo: the image sets the Stack's sizeStack(  children: [    Image.asset('assets/cover.jpg'),    const Positioned.fill(      child: DecoratedBox(…
  7. 07

    Что делает Wrap и чем он отличается от Row, который переполняется?

    Wrap заполняет строку детьми и переносит остальных на новую, как слова в абзаце; у Row строка ровно одна, и лишние дети переполняют его с жёлтыми полосами.

    Wrap(  spacing: 8, // between chips in a line  runSpacing: 8, // between lines  children: [    for (final tag in tags) Chip(label: Text(tag)),  ],…
  8. 08

    Этот ряд работает с короткими именами, но ломается на длинных. Какая ошибка появится и как её исправить?

    Row(  children: const [    Icon(Icons.person),    SizedBox(width: 8),    Text('Konstantin Konstantinopolsky-Vasilevsky, Senior Software Engineer, Platform Team'),    Icon(Icons.chevron_right),  ],)

    «A RenderFlex overflowed by N pixels on the right» с жёлто-чёрными полосами.

  9. 09

    Что делает SafeArea и куда его ставить?

    SafeArea добавляет ребёнку отступы, чтобы тот не залезал под вырез, статус-бар, скруглённые углы и индикатор «домой».

  10. 10

    В чём разница между ListView(children: ...), ListView.builder и Column внутри SingleChildScrollView?

    ListView.builder создаёт строки по требованию, когда они въезжают в экран, поэтому десять тысяч элементов стоят примерно один экран.

    // Data-driven list: rows are built only when they come into viewListView.builder(  itemCount: messages.length,  itemExtent: 72, // every row is 72 px: cheap jumps and scrollbar  itemBuilder: (context, index) => MessageTile(message: messages[index]),)…
  11. 11

    В чём разница между Visibility, Opacity и Offstage?

    Они прячут виджет на разных уровнях: Visibility(visible: false) убирает его из дерева, Opacity(opacity: 0) оставляет его разложенным и кликабельным, но невидимым, а Offstage держит его живым, но не занимает места, ничего не рисует и не принимает нажатия.

  12. 12

    Какие кнопки есть в Material 3 и когда какую выбирать?

    Кнопки Material 3 различаются акцентом, и выбирают их по важности действия: FilledButton берут для главного действия, OutlinedButton для второстепенного, TextButton для самого тихого.

  13. 13

    Почему InkWell внутри цветного Container не показывает рябь и что использовать вместо этого?

    InkWell рисует рябь на ближайшем предке Material, а цвет Container рисуется поверх этого Material и закрывает всплеск.

    // No visual feedback; opaque makes the whole padded box tappableGestureDetector(  behavior: HitTestBehavior.opaque,  onTap: _select,  child: const Padding(padding: EdgeInsets.all(12), child: Text('Tap me')),)…
  14. 14

    Как показать картинку из ассетов, сети, памяти и файла и чем Image.network отличается от NetworkImage?

    У Image есть именованный конструктор под каждый источник: Image.asset, Image.network, Image.memory, Image.file.

    // pubspec.yaml -> flutter: assets: [assets/images/]Image.asset('assets/images/logo.png', width: 96) Image.network(  'https://cdn.example.com/avatar.jpg',  width: 64,…
  15. 15

    Как показать SnackBar и зачем нужен ScaffoldMessenger?

    Вызовите ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: ...)).

    ScaffoldMessenger.of(context)  ..hideCurrentSnackBar()  ..showSnackBar(    SnackBar(      content: const Text('Message archived'),      behavior: SnackBarBehavior.floating,…
  16. 16

    Как сделать вкладки на TabBar, TabBarView и DefaultTabController?

    Оберните экран в DefaultTabController(length: n), поставьте TabBar в AppBar.bottom, а TabBarView положите в body.

    DefaultTabController(  length: 3,  child: Scaffold(    appBar: AppBar(      title: const Text('Orders'),      bottom: const TabBar(…
  17. 17

    Объясните правило «ограничения идут вниз, размеры идут вверх, позицию задаёт родитель». Почему width: 300 у Container иногда игнорируется?

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

    // wrong: SizedBox.expand forces the full size, so 300 is clampedSizedBox.expand(  child: Container(width: 300, height: 120, color: Colors.teal), // fills everything); // right: Center loosens the constraints, so the child may be smaller…
  18. 18

    ListView внутри Column падает с ошибкой вёрстки. В чём причина и как это исправить?

    Column даёт детям неограниченную высоту, а ListView старается стать настолько высоким, насколько позволяют ограничения, то есть бесконечным.

    // wrong: Vertical viewport was given unbounded heightColumn(  children: [    const ProfileHeader(),    ListView.builder(itemCount: items.length, itemBuilder: _row),  ],…
  19. 19

    Как GridView определяет высоту плитки и почему плитки переполняются на более узком телефоне?

    Размер плитки задаёт делегат сетки, а содержимое права голоса не имеет: при crossAxisCount ширина плитки = (доступная ширина − отступы) ÷ число колонок, а высота = ширина ÷ childAspectRatio.

    // wrong: height comes from width, so large text or a narrow phone overflowsGridView.builder(  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(    crossAxisCount: 2,    childAspectRatio: 0.72,  ),…
  20. 20

    Нажатие на эту кнопку бросает «Scaffold.of() called with a context that does not contain a Scaffold». Почему и как исправить?

    class HomePage extends StatelessWidget {  const HomePage({super.key});   @override  Widget build(BuildContext context) {    return Scaffold(      drawer: const Drawer(child: Text('Menu')),      body: Center(        child: TextButton(          onPressed: () => Scaffold.of(context).openDrawer(),          child: const Text('Open menu'),        ),      ),    );  }}

    Scaffold.of(context) ищет вверх от context, а context, переданный в HomePage.build, принадлежит HomePage, который стоит выше созданного им же Scaffold.

  21. 21

    Найдите баг: после переключения на другую нижнюю вкладку и обратно лента прыгает наверх и снова загружает данные.

    class _HomeShellState extends State<HomeShell> {  int _index = 0;  static const _pages = [FeedPage(), SearchPage(), ProfilePage()];   @override  Widget build(BuildContext context) {    return Scaffold(      body: _pages[_index],      bottomNavigationBar: NavigationBar(        selectedIndex: _index,        onDestinationSelected: (i) => setState(() => _index = i),        destinations: _destinations,      ),    );  }}

    body: _pages[_index] при каждом переключении кладёт в тот же слот другой виджет, поэтому Flutter размонтирует старую страницу и уничтожает её State с позицией скролла и загруженными данными.

  22. 22

    В чём разница между неявными и явными анимациями и с каких стоит начинать?

    Неявная (implicit) анимация сама анимирует свойство, когда меняется его значение.

    // Implicit: change the value, Flutter animates to itAnimatedContainer(  duration: const Duration(milliseconds: 250),  curve: Curves.easeOut,  width: _expanded ? 240 : 120,  color: _expanded ? Colors.teal : Colors.grey,…
  23. 23

    Как работает CustomPainter и когда его использовать?

    CustomPainter даёт Canvas, на котором рисуют то, что не выразить виджетами: графики, кольца прогресса, подписи от руки, нестандартные фигуры.

    import 'dart:math' as math; class RingPainter extends CustomPainter {  RingPainter({required this.progress, required this.color});   final double progress; // 0.0 to 1.0…
  24. 24

    Найдите баг: кольцо загрузки застывает на первом значении, хотя родитель вызывает setState и передаёт новый RingPainter(_progress).

    class RingPainter extends CustomPainter {  RingPainter(this.progress);  final double progress; // 0.0 to 1.0   @override  void paint(Canvas canvas, Size size) {    final paint = Paint()      ..style = PaintingStyle.stroke      ..strokeWidth = 8;    final rect = (Offset.zero & size).deflate(4);    canvas.drawArc(rect, -math.pi / 2, progress * 2 * math.pi, false, paint);  }   @override  bool shouldRepaint(RingPainter oldDelegate) => false;}

    shouldRepaint всегда возвращает false, поэтому, когда родитель перестраивается с новым painter, рендер-объект оставляет старую картинку и кольцо застывает.