gskinnerTeam / flutter-defer-pointer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unexpected hit priority on `Flow`'s `children` property

raulmabe opened this issue · comments

Hi, I am trying to implement several FABs through the Flow widget.

Context

What defers my case from your example is that I want to have the button that expands/shrinks the Flowfrom now on menu button– is at position 0 of the children property, thus keeping it immobile. For this reason, my FlowDelegate paints the children from last to first; so when all buttons are shrunk, my menu button is painted above the others.

This video section may clear out my intentions.

Problem

My problem starts when using this package. When all widgets are shrunk, the pointer event triggers the last button, not my menu button, thus not working properly.

Expected output

Trigger the menu button (GradientFAB) onPressed property, as its painted last and above the others.

Actual output

Triggers 'Second extra button', while being painted under the menu button.

Files

Widget file
import 'dart:developer';
import 'dart:math' as math;
import 'dart:ui';

import 'package:defer_pointer/defer_pointer.dart';
import 'package:flutter/material.dart';

class AnimatedFloatingActionButton {
  const AnimatedFloatingActionButton({
    required this.text,
    required this.icon,
    this.onPressed,
  });
  final String text;
  final IconData icon;
  final VoidCallback? onPressed;
}

const kPadding = 8.0;
const fabSize = 56.0;

class AnimatedFloatingActionButtons extends StatefulWidget {
  const AnimatedFloatingActionButtons({
    Key? key,
    required this.buttons,
  }) : super(key: key);

  final List<AnimatedFloatingActionButton> buttons;

  @override
  _AnimatedFloatingActionButtonsState createState() =>
      _AnimatedFloatingActionButtonsState();
}

class _AnimatedFloatingActionButtonsState
    extends State<AnimatedFloatingActionButtons>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late CurvedAnimation _curvedAnimation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: 300.ms,
      vsync: this,
    );
    _curvedAnimation =
        CurvedAnimation(parent: _controller, curve: Curves.decelerate);
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: fabSize,
      width: fabSize,
      child: DeferPointer(
        paintOnTop: true,
        child: Flow(
          clipBehavior: Clip.none,
          delegate: _MyFlowDelegate(
            animation: _curvedAnimation,
          ),
          children: [
            DeferPointer(
              // paintOnTop: true,
              child: GradientFAB(
                onPressed: _toggleAnimation,
                child: AnimatedBuilder(
                  animation: _curvedAnimation,
                  builder: (ctx, child) => Transform.rotate(
                    angle: lerpDouble(
                      0,
                      (90 + 45) * (math.pi / 180),
                      _controller.value,
                    )!,
                    child: child,
                  ),
                  child: const Icon(
                    Icons.add,
                    color: Colors.white,
                    size: 40.0,
                  ),
                ),
              ),
            ),
            ...widget.buttons
                .map(buildButton)
                .map((e) => DeferPointer(child: e))
                .toList(),
          ],
        ),
      ),
    );
  }

  Widget buildButton(AnimatedFloatingActionButton button) {
    return AnimatedBuilder(
      animation: _curvedAnimation,
      builder: (context, child) {
        return FloatingActionButton(
          isExtended: true,
          elevation: lerpDouble(0, 6.0, _controller.value),
          heroTag: button.text,
          child: child!,
          backgroundColor: context.colorScheme.primaryVariant,
          onPressed: () => log(button.text), //button.onPressed,
        );
      },
      child: Icon(button.icon),
    );
  }

  void _toggleAnimation() {
    if (_controller.status == AnimationStatus.completed) {
      _controller.reverse();
    } else {
      _controller.forward();
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    _curvedAnimation.dispose();
    super.dispose();
  }
}

class _MyFlowDelegate extends FlowDelegate {
  const _MyFlowDelegate({
    required this.animation,
  });
  final Animation animation;

  @override
  void paintChildren(FlowPaintingContext context) {
    final size = context.size;
    final xStart = size.width - fabSize;
    final yStart = size.height - fabSize;

    for (var i = context.childCount - 1; i >= 0; --i) {
      final offset = i * animation.value * (fabSize + kPadding);
      context.paintChild(
        i,
        transform: Matrix4.translationValues(
          xStart,
          yStart - offset,
          0,
        ),
      );
    }
  }

  @override
  bool shouldRepaint(covariant _MyFlowDelegate oldDelegate) {
    return animation != oldDelegate.animation;
  }
}
Example of use
           AnimatedFloatingActionButtons(
                buttons: [
                  AnimatedFloatingActionButton(
                    text: 'First extra button',
                    icon: Icons.add_a_photo,
                    onPressed: () {},
                  ),
                  AnimatedFloatingActionButton(
                    text: 'Second extra button',
                    icon: Icons.add_alarm,
                    onPressed: () {},
                  ),
                ],
              ),