Вопрос или проблема
У меня проблема с переполнением пикселей RenderFlex в Flutter (Dart). Исключение из библиотеки рендеринга.
Как я могу управлять или применить возможность прокрутки к представлению страницы моего приложения и избежать исключений рендеринга Flutter с сообщениями, подобными:
RenderFlex переполнил на 28 пикселей снизу.
Если вам нужен полный журнал, чтобы помочь мне, он здесь:
при горячей перезагрузке появляется желто-черная полоска внизу, согласно сообщению.
Могу ли я управлять этим с помощью прокручиваемого виджета? Или я могу объявить свои виджеты иначе, чтобы контролировать это?
полный код, если нужно (я изменил данные текста, но предположим, что отображаемые тексты длиннее размера экрана, и поэтому возникает ошибка):
@override
Widget build(BuildContext context) {
return new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
tabs: [
new Tab(text: "xxx",),
new Tab(text: "xxx",),
new Tab(text: "xxx",),
],
),
title: new Text(data["xxx"]),
),
body: new TabBarView(
children: [
new Column(
children: <Widget>[
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.blue,
fontSize: 16.0
),),
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.blue,
fontSize: 10.0
),),
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.blue,
fontSize: 16.0
),),
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.blue,
fontSize: 8.0
),
),
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.blue,
fontSize: 8.0
),),
new Row(
children: <Widget>[
new Expanded(
child: new Text("xxx"),
),
new Expanded(
child: new Icon(Icons.file_download, color: Colors.green, size: 30.0,),
),
],
),
new Divider(),
new Text("xxx",
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.red,
fontSize: 16.0
),
),
],
),
new ListView.builder(
itemBuilder: (BuildContext context, int index) => new EntryItem(_lstTiles[index]),
itemCount: _lstTiles.length,
),
new Column(
children: <Widget>[
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.green[900],
fontSize: 16.0
),
),
new Text(data["xxx"],
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.green[900],
fontSize: 16.0
),),
new Text(data["xxx"]),
new ListTile(title: new Text("xxx")),
new Text(data["xxx"]),
new ListTile(title: new Text("xxx")),
new Divider(),
new Text("xxx",
style: new TextStyle(
fontStyle: FontStyle.italic,
color: Colors.red,
fontSize: 16.0
),
),
],
),
],
),
),
);
}
Это довольно распространенная проблема, с которой сталкиваются, особенно когда вы начинаете тестировать свое приложение на нескольких устройствах и в разных ориентациях. Галерея виджетов Flutter содержит раздел, посвященный различным прокручиваемым виджетам:
https://flutter.io/widgets/scrolling/
Я бы рекомендовал либо обернуть все свое содержимое в SingleChildScrollView
, либо использовать прокручиваемый ListView
.
ИЗМЕНЕНИЕ: Этот вопрос и ответ получили некоторое внимание, поэтому я хотел бы предоставить немного больше помощи для тех, кто попадает сюда.
Команда Flutter SDK прилагает много усилий для хорошей документации внутри самого кода SDK. Один из лучших ресурсов для понимания алгоритма, который виджеты Flex
(как Row
, так и Column
являются подклассами Flex
) используют для раскладки своих детей — это DartDoc, который прилагается к самому классу:
Сайт Flutter также содержит учебник по построению макетов и интерактивный кодлаб о том, как использовать виджеты Row
и Column
.
Предположим, у вас есть List
из 100 Text
виджетов, как этот:
final children = List<Widget>.generate(100, (i) => Text('Item $i')).toList();
В зависимости от экрана устройства эти виджеты могут переполнять, для этого есть несколько решений.
-
Используйте
Column
, обернутый вSingleChildScrollView
SingleChildScrollView( child: Column(children: children), )
-
Используйте
ListView
ListView( children: children )
-
Используйте комбинацию
Column
иListView
(вы должны использоватьExpanded
/Flexible
или задать фиксированную высоту дляListView
при этом).Column( children: [ ...children.take(2).toList(), // показать первые 2 дочерних элемента в Column Expanded( child: ListView( children: children.getRange(3, children.length).toList(), ), // И остальные в ListView ), ], )
Я также сталкивался с этой проблемой, попробуйте это ….
resizeToAvoidBottomPadding: false,
Перед частью тела
ИЗМЕНЕНИЕ – 1
resizeToAvoidBottomPadding
устарел
resizeToAvoidBottomInset: false
Если вы используете Column, оберните свой внутренний виджет в expanded, как это
Expanded(
child: Center(
child: Image(
image: AssetImage('assets/icons/${menuItem.iconData}'),
),
),
),
Вы можете использовать виджет Expanded
для каждого контейнера внутри колонки. А затем используйте flex
, чтобы сделать правильные настройки.
Оберните Column в SingleChildScrollView.
Если Column вас не устраивает, попробуйте Wrap:
// не проходит модульные тесты
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
icon ?? Icons.cancel_outlined,
size: 64.0,
color: theme.colorScheme.error,
),
Text(
errorTitle,
style: theme.textTheme.displayLarge,
textAlign: TextAlign.center,
),
Text(
errorDetails,
style: theme.textTheme.titleLarge,
textAlign: TextAlign.center,
),
if (onPressedActionButton != null && textActionButton != null)
ElevatedButton(
onPressed: onPressedActionButton,
child: Text(textActionButton!),
),
],
);
// проходит модульные тесты
return Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.center,
runSpacing: 32,
children: [
Icon(
icon ?? Icons.cancel_outlined,
size: 64.0,
color: theme.colorScheme.error,
),
Text(
errorTitle,
style: theme.textTheme.displayLarge,
textAlign: TextAlign.center,
),
Text(
errorDetails,
style: theme.textTheme.titleLarge,
textAlign: TextAlign.center,
),
if (onPressedActionButton != null && textActionButton != null)
ElevatedButton(
onPressed: onPressedActionButton,
child: Text(textActionButton!),
),
],
);
Вы можете обернуть текст в виджет PreferredSize:
bottom: PreferredSize(
preferredSize: Size.fromHeight(120.0),
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(
right: 5.0, left: 5.0, bottom: 2.0, top: 2.0),
child: Text(
hospitalName,
textAlign: TextAlign.left,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 14.0),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: 1.0,
width: MediaQuery.of(context).size.width,
color: Colors.white,
),
),
Padding(
padding: EdgeInsets.only(
top: 2.0, right: 5.0, left: 5.0, bottom: 2.0),
child: Text(
doctorName,
textAlign: TextAlign.left,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 14.0),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: 1.0,
width: MediaQuery.of(context).size.width,
color: Colors.white,
),
),
TabBar(
isScrollable: true,
tabs: [
Tab(
text: "Tab1",
),
Tab(text: "Tab2"),
Tab(text: "Tab3"),
],
),
],
)),
Для меня я добавил текст к одному из дочерних элементов строки, я получал данные из firebase, и они были null. Так что убедитесь, что данные поступают.
при увеличении размера текста, не желая увеличивать размер виджета, используйте FittedBox для идеальной подгонки
например
Text(
'Unread: $notificationCount',
style: TextStyle(fontSize: 12, color: Colors.red),
),
//вы можете сделать это, обернув виджет в FittedBox
FittedBox(
fit: BoxFit.scaleDown, // отрегулируйте свойство BoxFit по своему усмотрению
child: Text(
'Unread: $notificationCount',
style: TextStyle(fontSize: 12, color: Colors.red),
),
),
Я столкнулся с этой проблемой при написании patrolWidgetTest
для виджета Form.
Форма была длинной формой и использовалась внутри прокручиваемого виджета. Поэтому в тесте нам также нужно обернуть это в прокручиваемый виджет.
Таким образом, код изменился с
Material(child: Form(
key: formKey,
child: formUnderTest,
));
на
Material(child: SingleChildScrollView(
child: Form(
key: formKey,
child: formUnderTest,
),
))
где formUnderTest
— это объект виджета формы, который мы хотим протестировать
Ошибка, возникшая при запуске теста, была следующей
Определенный RenderFlex, который вызывает вопросы: RenderFlex#b642a ПЕРЕПОЛНЯЕТСЯ:
нуждается в компоновке
создатель: Column ← SizedBox ← EjItemPicker ← Padding ← Stack ←
NotificationListener<DraggableScrollableNotification> ← DefaultTextStyle ←
AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#53d6d ink renderer] ←
NotificationListener<LayoutChangedNotification> ← CustomPaint ← _ShapeBorderPaint ← ⋯
parentData: <none> (можно использовать размер)
ограничения: BoxConstraints(w=640.0, h=200.0)
Другой вариант, который сработал, заключался в увеличении размера во время теста, чтобы сделать вид, что у нас больший экран. Поскольку экран по умолчанию, используемый патрульным тестом виджетов, маленький constraints: BoxConstraints(w=640.0, h=200.0)
Таким образом, в тесте, перед инициализацией приложения
final dpi = $.tester.view.devicePixelRatio;
$.tester.view.physicalSize = Size(412 * dpi, 915 * dpi);
Ответ или решение
Обработка переполнения RenderFlex в Flutter
Ошибка переполнения RenderFlex
в Flutter — это распространенная проблема, с которой сталкиваются разработчики, особенно когда работают с динамическим контентом, который может превышать размеры экрана. Когда вам выдается сообщение о том, что «RenderFlex переполнен на 28 пикселей снизу», это означает, что ваши виджеты не помещаются в отведенное для них пространство.
Причины возникновения ошибки
К этой проблеме может привести использование виджетов, таких как Column
или Row
, которые пытаются отобразить контент, превышающий высоту или ширину экрана. В вашем примере, где используется несколько Text
виджетов внутри Column
, это типичная ситуация, когда постающие элементы выходят за пределы видимой области.
Решения проблемы с переполнением
-
Использование
SingleChildScrollView
:
Один из простых способов управления переполнением — обернуть вашColumn
вSingleChildScrollView
. Это позволяет вашим дочерним элементам прокручиваться, если они выходят за пределы видимой области.@override Widget build(BuildContext context) { return SingleChildScrollView( child: Column( children: [ // ваши виджеты ], ), ); }
-
Замена на
ListView
:
Если ваш контент лучше структурировать в виде списка, стоит рассмотреть использованиеListView
как альтернативу. Этот виджет по умолчанию поддерживает прокрутку.@override Widget build(BuildContext context) { return ListView( children: [ // ваши виджеты ], ); }
-
Комбинация
Column
иListView
:
При необходимости совместить статичный и динамичный контент, можно использовать несокращающийColumn
, содержащийListView
. Здесь важно использоватьExpanded
или фиксировать высотуListView
, чтобы избежать переполнения.@override Widget build(BuildContext context) { return Column( children: [ // Статичный контент Expanded( child: ListView( children: [ // Динамический контент ], ), ), ], ); }
-
Настройка
resizeToAvoidBottomInset
:
Если ваша страница находится под клавиатурой, то имеет смысл использовать следующую настройку вScaffold
:Scaffold( resizeToAvoidBottomInset: true, // По умолчанию. Измените на false, если не требуется. body: // ваши виджеты );
-
Использование
Expanded
:
ВнутриColumn
можно использоватьExpanded
, чтобы указать виджет, который должен занять свободное пространство.Column( children: [ Text('Пример текста'), Expanded( child: ListView( children: [ // ваши элементы списка ], ), ), ], );
Обработка ошибок
Чтобы избежать возникновения ошибок, важно всегда проверять длину текста или размера контента перед его отображением. Убедитесь, что используется достаточное пространство, чтобы поместить все элементы в видимой области.
Заключение
Ошибки переполнения виджетов могут быть простыми в исправлении, если использовать подходящие виджеты для управления контентом. Используйте SingleChildScrollView
, ListView
, и комбинированные подходы для более гибкого контроля расположения виджетов. Правильные настройки и аккуратная работа с размерами помогут избежать этих распространенных проблем разработки в Flutter.
Адекватное обращение с вариантами размещения виджетов гарантирует плавную работу вашего приложения и улучшенное взаимодействие пользователей с интерфейсом.