Флаттер, поток внутри выравнивания не соответствует правилам выравнивания

#flutter #alignment #flow

#флаттер #макет #выравнивание

Вопрос:

Я пытаюсь использовать виджет Flow Flutter, выровненный по верхнему правому краю экрана и перемещающийся влево. Я пробовал использовать Stack Positioned, я пробовал использовать Container Align. Поток всегда придерживается верхнего левого угла. Вот код, с которым я борюсь;

 /// Flutter code sample for Flow

// This example uses the [Flow] widget to create a menu that opens and closes
// as it is interacted with, shown above. The color of the button in the menu
// changes to indicate which one has been selected.

import 'package:flutter/material.dart';

void main() => runApp(FlowApp());

class FlowApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flow Example'),
        ),
        body: FlowMenu(),
      ),
    );
  }
}

class FlowMenu extends StatefulWidget {
  @override
  _FlowMenuState createState() => _FlowMenuState();
}

class _FlowMenuState extends State<FlowMenu>
    with SingleTickerProviderStateMixin {
  final double buttonDiameter = 40;
  AnimationController menuAnimation;
  IconData lastTapped = Icons.notifications;
  final List<IconData> menuItems = <IconData>[
    Icons.home,
    Icons.new_releases,
    Icons.notifications,
    Icons.settings,
    Icons.menu,
  ];

  void _updateMenu(IconData icon) {
    if (icon != Icons.menu) setState(() => lastTapped = icon);
  }

  @override
  void initState() {
    super.initState();
    menuAnimation = AnimationController(
      duration: const Duration(milliseconds: 250),
      vsync: this,
    );
  }

  Widget flowMenuItem(IconData icon) {
//    final double buttonDiameter =  MediaQuery.of(context).size.width / menuItems.length;

    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 0.0),
      child: RawMaterialButton(
        fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,
        splashColor: Colors.amber[100],
        shape: CircleBorder(),
        constraints: BoxConstraints.tight(Size(buttonDiameter, buttonDiameter)),
        onPressed: () {
          _updateMenu(icon);
          menuAnimation.status == AnimationStatus.completed
              ? menuAnimation.reverse()
              : menuAnimation.forward();
        },
        child: Icon(
          icon,
          color: Colors.white,
          size: buttonDiameter - 10,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width,
      decoration: BoxDecoration(color: Colors.green),
      child: Align(
        alignment: Alignment.topRight,
        child: Container(
          constraints: BoxConstraints.tight(
              Size((buttonDiameter) * menuItems.length, buttonDiameter 5)),
          decoration: BoxDecoration(color: Colors.red),
          child: Align( //why this align does not work?
            alignment: Alignment.topRight,
            child: Flow(
              delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
              children: menuItems
                  .map<Widget>((IconData icon) => flowMenuItem(icon))
                  .toList(),
            ),
          ),
        ),
      ),
    );
  }
}

class FlowMenuDelegate extends FlowDelegate {
  FlowMenuDelegate({this.menuAnimation}) : super(repaint: menuAnimation);

  final Animation<double> menuAnimation;

  @override
  bool shouldRepaint(FlowMenuDelegate oldDelegate) {
    return menuAnimation != oldDelegate.menuAnimation;
  }

  @override
  void paintChildren(FlowPaintingContext context) {
    double dx = 0.0;
    for (int i = 0; i < context.childCount;   i) {
      dx = context.getChildSize(i).width * i;
      context.paintChild(
        i,
        transform: Matrix4.translationValues(
          -dx * menuAnimation.value,
          0,
          0,
        ),
      );
    }
  }
}


  

И результирующий экран;

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

Можете ли вы помочь мне с выравниванием потока в правом верхнем углу? Большое вам спасибо за чтение или ответ.

ps. В нем говорится, что мне не разрешено создавать теги. Итак, вот список на будущее: виджет Flow, виджет Align, Flutter

Редактировать

Чего я пытаюсь достичь:

  1. и 2. получены GIF-файлы. в последнем показано, как мне не удалось выровнять его по правому краю.

Не этот,

Не этот,

Не работает

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

1. @pskink Я подготовил и добавил gif-файлы к вопросу, чтобы иметь возможность дополнительно объяснить мою потребность. Там, 3. gif показывает, что я пытаюсь переместить поток вправо и открыть его влево, но он остается слева и при анимации выходит за пределы поля.

2. это потому, что ваши transform параметры неверны: вы должны перевести Matrix4 вправо, но положение потока правильное — оно выровнено правильно

3. Хорошая анимация, спасибо. Мне не нужно использовать flow, конечно, всегда есть обходной путь, но я новичок в flutter и хотел учиться. В любом случае, спасибо.

Ответ №1:

Похоже, проблема заключается в том, что виджет заполнения обертывает виджет RawMaterialButton. Поэтому я заменил его на Align, а также немного изменил ваш рабочий код анимации следующим образом

 /// Flutter code sample for Flow

// This example uses the [Flow] widget to create a menu that opens and closes
// as it is interacted with, shown above. The color of the button in the menu
// changes to indicate which one has been selected.

import 'package:flutter/material.dart';

void main() => runApp(FlowApp());

class FlowApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flow Example'),
        ),
        body: FlowMenu(),
      ),
    );
  }
}

class FlowMenu extends StatefulWidget {
  @override
  _FlowMenuState createState() => _FlowMenuState();
}

class _FlowMenuState extends State<FlowMenu>
    with SingleTickerProviderStateMixin {
  final double buttonDiameter = 40;
  AnimationController menuAnimation;
  IconData lastTapped = Icons.notifications;
  final List<IconData> menuItems = <IconData>[
    Icons.home,
    Icons.new_releases,
    Icons.notifications,
    Icons.settings,
    Icons.menu,
  ];
//  @override Size getSize(BoxConstraints constraints) => Size(100, 100);
//  @override Size getSize(BoxConstraints constraints) => constraints.biggest / 2;

  void _updateMenu(IconData icon) {
    if (icon != Icons.menu) setState(() => lastTapped = icon);
  }

  @override
  void initState() {
    super.initState();
    menuAnimation = AnimationController(
      duration: const Duration(milliseconds: 250),
      vsync: this,
    );
  }

  Widget flowMenuItem(IconData icon) {
//    final double buttonDiameter =  MediaQuery.of(context).size.width / menuItems.length;

    return Container(
      decoration: BoxDecoration(
        color: Colors.white
      ),
      child: Align(
//      padding: const EdgeInsets.symmetric(vertical: 0.0),
        alignment: Alignment.topRight,
        child: RawMaterialButton(
          fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,
          splashColor: Colors.amber[100],
          shape: CircleBorder(),
          constraints: BoxConstraints.tight(Size(buttonDiameter, buttonDiameter)),
          onPressed: () {
            _updateMenu(icon);
            menuAnimation.status == AnimationStatus.completed
                ? menuAnimation.reverse()
                : menuAnimation.forward();
          },
          child: Icon(
            icon,
            color: Colors.white,
            size: buttonDiameter - 10,
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width,
      decoration: BoxDecoration(color: Colors.green),
      child: Align(
        alignment: Alignment.topRight,
        child: Container(
          constraints: BoxConstraints.tight(
              Size(buttonDiameter * (menuItems.length 1), buttonDiameter 5)),
          decoration: BoxDecoration(color: Colors.red),
          child: Align(
            alignment: Alignment.topRight,
            child: Flow(
              delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
              children: menuItems
                  .map<Widget>((IconData icon) => flowMenuItem(icon))
                  .toList(),
            ),
          ),
        ),
      ),
    );
  }
}

class FlowMenuDelegate extends FlowDelegate {
  FlowMenuDelegate({this.menuAnimation}) : super(repaint: menuAnimation);

  final Animation<double> menuAnimation;

  @override
  bool shouldRepaint(FlowMenuDelegate oldDelegate) {
    return menuAnimation != oldDelegate.menuAnimation;
  }
//  @override Size getSize(BoxConstraints constraints) => constraints.biggest / 2;


  @override
  void paintChildren(FlowPaintingContext context) {
    double dx = 0.0;
    for (int i = 0; i < context.childCount;   i) {
      dx = 40.0 * i;
      context.paintChild(
        i,
        transform: Matrix4.translationValues(
          -dx * menuAnimation.value,
          0,
          0,
        ),
      );
    }
  }
}
  

решение gif