Как мне вручную рассчитать макет в Flutter

#flutter #layout

#flutter #макет

Вопрос:

Я хотел бы поместить следующие виджеты в SingleChildScrollView , но я хочу, чтобы синяя рамка располагалась прямо под сгибом при рендеринге страницы. Пользователь может прокрутить его в поле зрения по мере необходимости, но для начала он находится вне поля зрения.

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

Возможно, мне следует использовать SizedBox вместо настройки поля моего красного поля, что не будет проблемой. Но мне все еще нужно иметь возможность рассчитать высоту пустого пространства в верхней части моего макета.

макет flutter

Комментарии:

1. проверьте CustomMultiChildLayout

Ответ №1:

У @pskink была правильная идея. Вот что я сделал с ним в коде для достижения макета.

 enum AuthWidgets { form, action, disclaimer }

class AuthLayoutDelegate extends MultiChildLayoutDelegate {
  AuthLayoutDelegate({@required screenHeight}) : _screenHeight = screenHeight;

  double _overlap = 50;
  double _screenHeight;

  @override
  void performLayout(Size size) {
    Size formSize;
    Size actionSize;
    Size disclaimerSize;

    // if (formSize == null amp;amp; hasChild(AuthWidgets.form)) {
    formSize = layoutChild(AuthWidgets.form, BoxConstraints.loose(size));
    // }

    // if (actionSize == null amp;amp; hasChild(AuthWidgets.action)) {
    actionSize = layoutChild(AuthWidgets.action, BoxConstraints.loose(size));
    // }

    // if (disclaimerSize == null amp;amp; hasChild(AuthWidgets.disclaimer)) {
    disclaimerSize =
        layoutChild(AuthWidgets.disclaimer, BoxConstraints.loose(size));
    // }

    if (formSize != null amp;amp; actionSize != null amp;amp; disclaimerSize != null) {
      print('laying out children');
      // Need the height of the form and action area, minus the overlap
      var aboveTheFold = formSize.height   actionSize.height - _overlap;

      var offsetY = _screenHeight - aboveTheFold;

      offsetY  = formSize.height - _overlap;
      positionChild(AuthWidgets.action, Offset(0, offsetY));

      offsetY  = actionSize.height;
      positionChild(AuthWidgets.disclaimer, Offset(0, offsetY));

      positionChild(AuthWidgets.form, Offset(0, _screenHeight - aboveTheFold));
    }
  }

  @override
  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
    return false;
  }
}

class AuthLayout extends StatelessWidget {
  const AuthLayout({@required this.screenHeight, Key key}) : super(key: key);

  final double screenHeight;

  @override
  Widget build(BuildContext context) {
    return CustomMultiChildLayout(
      delegate: AuthLayoutDelegate(screenHeight: screenHeight),
      children: [
        LayoutId(id: AuthWidgets.action, child: ActionMenu()),
        LayoutId(id: AuthWidgets.disclaimer, child: Disclaimer()),
        LayoutId(id: AuthWidgets.form, child: SignInForm()),
      ],
    );
  }
}

class SignInForm extends StatelessWidget {
  const SignInForm({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 16),
      child: Container(
        color: Colors.red.withOpacity(0.5),
        height: 350,
        child: Center(
          child: Text('SignInForm'),
        ),
      ),
    );
  }
}

class Disclaimer extends StatelessWidget {
  const Disclaimer({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      color: Colors.blue.withOpacity(0.5),
      child: Center(child: Text('Disclaimer')),
    );
  }
}

class ActionMenu extends StatelessWidget {
  const ActionMenu({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 200,
      color: Colors.green.withOpacity(0.5),
      child: Center(child: Text('ActionMenu')),
    );
  }
}
  

Используя делегат и performLayout функцию, я смог расположить свои виджеты именно там, где они мне были нужны. Я действительно хотел, чтобы моя красная область отображалась «поверх» зеленой области, поэтому я должен был убедиться, что она была добавлена CustomMultiChildLayout последней (или, по крайней мере, после зеленой рамки).

Теперь я изо всех сил пытаюсь заставить это прокручиваться на странице, но это вопрос для другого потока.