#android #flutter #dart #layout #flutter-layout
Вопрос:
TL; DR
Как я могу узнать правила компоновки виджета (какой размер он будет запрашивать у своего родителя и какие ограничения он будет передавать своим дочерним элементам), если нет документации об этом?
Подробности проблемы
У меня есть это очень простое приложение
void main() {
runApp(
Container(
color: Colors.red,
width: 20,
height: 20,
),
);
}
Я ожидал Container
, что ширина и высота будут равны 20, но я получил Container
, который заполнил весь экран.
Читая эту статью на flutter.dev о понимании ограничений, в ее последней части, называемой «Изучение правил компоновки для конкретных виджетов», упоминается, как это сделать, найдя createRenderObject
метод, а затем найдя performLayout
метод.
Однако этот createRenderObject
метод доступен только для подклассов RenderObjectWidget
. Например, просматривая код Transform
виджета, я нахожу createRenderObject
, что возвращает a RenderTransform
, который расширяется RenderProxyBox
, который в конечном итоге реализуется performLayout
как :
@override
void performLayout() {
if (child != null) {
child!.layout(constraints, parentUsesSize: true);
size = child!.size;
} else {
size = computeSizeForNoChild(constraints);
}
}
Я могу сделать вывод, что Transform
виджет, наконец, примет размер своего дочернего элемента из-за этой строки size = child!.size;
.
Но в случае Container
, указанном выше, непосредственно расширяется StatelessWidget
. Я не мог найти, просматривая его код, методы performLayout
и createRenderObject
, я мог только найти createElement
, но я ищу RenderObject
в дереве рендеринга, связанном с контейнером, а не с элементом.
Вопрос
Итак, вопрос в том, как найти этот объект рендеринга, связанный с виджетом без состояния / виджетом с сохранением состояния, чтобы узнать правила компоновки, которые этот виджет передаст своим дочерним элементам, и сам будет следовать им в этом случае?
Ответ №1:
Вы правы. Я бы сказал, что моя статья неточна в этом отношении.
Виджету не нужно создавать RenderObject
. Вместо этого он может использовать композицию других виджетов, которые создают RenderObjects
сами себя.
Если виджет состоит из других виджетов, то вместо того, чтобы смотреть на performLayout
, вы можете просто посмотреть на build
метод этого виджета, чтобы увидеть, что он делает. В случае a Container
это его build
метод:
Widget build(BuildContext context) {
Widget? current = child;
if (child == null amp;amp; (constraints == null || !constraints!.isTight)) {
current = LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
);
}
if (alignment != null)
current = Align(alignment: alignment!, child: current);
final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = Padding(padding: effectivePadding, child: current);
if (color != null)
current = ColoredBox(color: color!, child: current);
if (clipBehavior != Clip.none) {
assert(decoration != null);
current = ClipPath(
clipper: _DecorationClipper(
textDirection: Directionality.maybeOf(context),
decoration: decoration!,
),
clipBehavior: clipBehavior,
child: current,
);
}
if (decoration != null)
current = DecoratedBox(decoration: decoration!, child: current);
if (foregroundDecoration != null) {
current = DecoratedBox(
decoration: foregroundDecoration!,
position: DecorationPosition.foreground,
child: current,
);
}
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
if (margin != null)
current = Padding(padding: margin!, child: current);
if (transform != null)
current = Transform(transform: transform!, alignment: transformAlignment, child: current);
return current!;
}
Как вы можете видеть, он определяется в терминах других виджетов. И эти виджеты также могут быть определены в терминах других виджетов и так далее. Но в какой-то момент вы дойдете до виджетов, которые создают RenderObject
s.
Что касается причины Container
, по которой размер не равен 20×20, это потому, что, как объясняется в статье, размеры устанавливаются родителями. Таким Container
образом, размер s устанавливается Container
родительским элементом, который в данном случае является экраном. И экран всегда заставляет своего дочернего элемента занимать все доступное пространство, в данном случае игнорируя Container
желание быть 20×20. Исправление здесь заключается в предоставлении Container
другого родителя. Тот, который позволяет Container
выбирать свой собственный размер. Например, оба Center
и Align
позволят этому произойти, и именно поэтому вы можете решить проблему, выполнив:
void main() {
runApp(
Center(
child: Container(
color: Colors.red,
width: 20,
height: 20,
),),);
}
Что касается того, почему экран заставляет своего дочернего элемента занимать все доступное пространство: именно так решили создатели Flutter. Если вы покопаетесь в коде Flutter, вы найдете его там. Но, вероятно, лучше всего просто запомнить этот факт.
Надеюсь, это поможет!
Комментарии:
1. Спасибо, что нашли время, чтобы ответить на этот вопрос, я только изучаю этот макет и рендеринг, и его сложность заставила меня пропустить метод сборки в первую очередь 🙂 Спасибо!
Ответ №2:
Meh, пока не слишком усложняйте. вот самая простая вещь, которую я могу вам объяснить, позволяет разделить приложение на 4 разных уровня.
- Приложение -> Вы можете использовать
MaterialApp
для создания приложения material - Страницы виджетов -> Вы можете выбирать между использованием
Stateless
илиStatefulWidget
, это зависит от ваших потребностей. Если вам нужен динамический, с множеством изменяющихся состояний, которые вы можете использоватьStatefulWidget
, или вы можете создать статическую страницуStatelessWidget
. - Scaffold -> Страницы виджетов должны возвращать каркас, чтобы сформировать страницы материалов, здесь вы можете использовать
Scaffold
, вы можете добавить AppBar, fab, body, BottomNavigationBar и т. Д. - Виджеты -> Здесь виджеты могут быть любыми, это может быть панель приложений для вашей панели приложений scaffold, или ListView, GridView или пользовательский виджет.
Итак, ваш код должен выглядеть так, вроде
/// This block is the first point of your application, this will run your application
void main() {
runApp(myApp());
}
/// Then you need an material app, this part should return your app
class MyApp extends StatelessWidget{
/// This is very important, both StatelessWidget / StatefullWidget
/// always having a build method. It's should returning a Widget. But in this case we will return an material application
@override
Widget build(BuildContext context){
return MaterialApp(
home: MyHome(),
),
}
}
class MyHome extends StatelessWidget{
/// This is home page, it's have Scaffold so the page will using, material guidelines.
@override
Widget build(BuildContext context){
return Scaffold(
body:Container(
color: Colors.red,
width: 20,
height: 20,
),
);
}
}
Комментарии:
1. Спасибо, но мой вопрос о том, как узнать правила компоновки любого виджета, но это не отвечает на мой вопрос.
2. Чувак, ты упускаешь из виду, что здесь
Scaffold
тот, который заставляет ваш виджет отображаться в соответствии с вашими ожиданиями, без Scaffold ваш виджет не будет отображаться корректно3. это всего лишь демонстрация вопроса, я знаю о каркасе, вы читали TL; DR?? пожалуйста, прочтите это, чтобы понять мой вопрос
4. хорошо, тогда я понятия не имею, чего вы пытаетесь достичь. удачи