Позиционирование / изменение размера виджета в зависимости от положения / размера другого виджета

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

Несколько примеров моих худших кошмаров во время трепета: динамическая привязка виджетов друг к другу. В css у нас было бы это:

.root {
    display: flex;
    background: grey;
    padding: 4px;
}
.root > div {
  background: lightgrey;
  margin-right: 5px;
  padding: 5px;
}
<div class="root">
    <div>
         dynamic height
         <br/>
         dynamic width
         <br/>
         fat
         <br/>
         super fat
    </div>
    <div>
         dynamic width
         <br/>
         dynamic height
    </div>
</div>

  • родитель принимает всю ширину (не важно).
  • родитель принимает рост самых больших детей.
  • дети с меньшей высотой растягиваются, чтобы соответствовать родителю
  • дети располагаются рядом друг с другом

Во флаттере, я не думаю, что у нас могут быть все 4 очка сразу.

А что, если у нас есть 2 из этого списка и анимация, в которой один элемент переходит из одного списка в другой? пример

введите описание изображения здесь  введите описание изображения здесь

Другой пример. Что, если бы мы хотели иметь SlideTransition, который будет выровнен по вертикали с другим виджетом, не имеющим ничего общего? Нравится :

введите описание изображения здесь

Это совершенно случайные примеры. Мне не нужно воспроизводить одно и то же. Настоящий вопрос здесь: есть ли общий способ сделать что-то подобное (получить размер / положение экрана)? Что-то, что не будет специфичным для этого варианта использования и будет легко поддерживаться позже?


person Rémi Rousselet    schedule 01.09.2017    source источник


Ответы (2)


Чтобы ответить на ваш исходный вопрос о макете, вы можете выполнить этот макет с помощью IntrinsicHeight виджет вместе с CrossAxisAlignment.stretch. Это выглядит так:

скриншот

Вот код:

import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Padding(
        padding: new EdgeInsets.all(10.0),
        child: new IntrinsicHeight(
          child: new Container(
            color: Colors.grey,
            padding: new EdgeInsets.all(4.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                new Container(
                  padding: new EdgeInsets.all(5.0),
                  margin: new EdgeInsets.only(right: 5.0),
                  color: Colors.grey[200],
                  child: new Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      new Text('dynamic height'),
                      new Text('dynamic width'),
                      new Text('fat'),
                      new Text('super fat'),
                    ],
                  ),
                ),
                new Container(
                  padding: new EdgeInsets.all(5.0),
                  color: Colors.grey[200],
                  child: new Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      new Text('dynamic width'),
                      new Text('dynamic height'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Чтобы реализовать более сложные примеры анимации, я бы рекомендовал использовать CustomMultiChildLayout для позиционирования коробки. См. демонстрацию анимации в Галерее для примера расширенная анимация с использованием этого класса:

скриншот

person Collin Jackson    schedule 05.09.2017
comment
Круто, не знал, что можно использовать IntrinsicHeight вот так. С другой стороны, для показанных мною анимаций CustomChildLayout не подойдет. Не только размер CustomChildLayout не может зависеть от дочерних элементов; но это все равно не решит проблему доступа к позиции / размеру глобального виджета в мире. - person Rémi Rousselet; 05.09.2017
comment
Вы задаете много вопросов, и я думаю, вам, вероятно, стоит подумать о том, чтобы разделить их на отдельные вопросы о переполнении стека. Если вы используете MultiChildLayoutDelegate, размер вашего виджета указан в performLayout. Вы можете определить размер своих детей, позвонив по телефону layoutChild. Если вы хотите получить глобальные координаты своего renderObject, вы можете использовать _5 _ хотя, если вы делаете это, вы, вероятно, нарушаете инкапсуляцию и должны реализовать настраиваемый макет на более высоком уровне - person Collin Jackson; 06.09.2017
comment
Обратите внимание, что для простых случаев использования вы можете использовать LayoutBuilder, чтобы получить ограничения размера, окружающие виджет. Однако вам это не понадобится, если вы реализуете MultiChildLayoutDelegate. - person Collin Jackson; 06.09.2017
comment
Спасибо, но, как я уже сказал, MultiChildLayout (также известный как CustomChildlayout) не будет работать. Хотя вы можете получить размер дочерних элементов внутри performLayout, вы не сможете получить доступ к их размеру внутри getSize. - person Rémi Rousselet; 06.09.2017
comment
Это нарушение инкапсуляции. Но другого решения для какой-то анимации, типа Hero like, нет. Допустим, вы хотите визуально преобразовать изображение внутри тела Scaffold в значок тележки внутри Scaffold AppBar. У тебя нет выбора. - person Rémi Rousselet; 06.09.2017
comment
Я бы обернул Scaffold в Stack, если вы хотите добиться такого эффекта. Таким образом, у вас будет полный контроль над расположением элементов, которые появляются наверху Scaffold. - person Collin Jackson; 06.09.2017
comment
Если вы столкнетесь с ограничениями того, что вы можете делать с существующими виджетами бокса и ленты, другой вариант - раскрыть слой и реализовать настраиваемую логику на уровне дерева рендеринга. Это потребует написания большего количества кода, но даст вам более точный контроль над вещами. - person Collin Jackson; 06.09.2017
comment
Позвольте нам продолжить это обсуждение в чате. - person Rémi Rousselet; 06.09.2017

  • родитель - рост самых больших детей:

Вы можете использовать MainAxisSize.min в своей строке или столбце, он работает как обертка для родительских виджетов.

body: new Container(

 color: Colors.yellowAccent,

 child: new Row(

 mainAxisSize: MainAxisSize.min,

 children: [...],

 ),

)
  • дочерние элементы с меньшей высотой, чтобы соответствовать родительскому:

FractionallySizedBox - это виджет (контейнер), и в соответствии с его widthFactor и heightFactor будут установлены все дочерние виджеты. Вы можете рассматривать это как своего рода масштабное значение. Итак, если мы хотим иметь контейнер, который занимает половину доступного пространства по горизонтали и вертикали, мы должны сделать следующее:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("FractionallySizedBox"),
      ),
      body: FractionallySizedBox(
        widthFactor: 0.5,
        heightFactor: 0.5,
        child: Container(
          // this container won't be larger than
          // half of its parent size
        ),
      ),
    );
  }
  • дочерние элементы располагаются рядом друг с другом:

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

Row(
    children: [
          YourWidget(),
          YourWidget(),
          YourWidget(),
      ]
 )
person ZealousWeb    schedule 26.02.2021