desktopCall function

Widget desktopCall(
  1. CallController c,
  2. BuildContext context
)

Returns a desktop design of a CallView.

Implementation

Widget desktopCall(CallController c, BuildContext context) {
  final style = Theme.of(context).style;

  return LayoutBuilder(
    builder: (context, constraints) {
      // Call stackable content.
      List<Widget> content = [
        const SvgImage.asset(
          'assets/images/background_dark.svg',
          width: double.infinity,
          height: double.infinity,
          fit: BoxFit.cover,
        ),
      ];

      // Secondary view possible alignment.
      Widget possibleContainer() {
        return Obx(() {
          Alignment? alignment = c.possibleSecondaryAlignment.value;
          if (alignment == null) {
            return Container();
          }

          double width = 10;
          double height = 10;

          if (alignment == Alignment.topCenter ||
              alignment == Alignment.bottomCenter) {
            width = double.infinity;
          } else {
            height = double.infinity;
          }

          return Align(
            alignment: alignment,
            child: ConditionalBackdropFilter(
              child: Container(
                height: height,
                width: width,
                color:
                    ConditionalBackdropFilter.enabled
                        ? style.colors.primaryAuxiliaryOpacity25
                        : style.colors.primaryAuxiliaryOpacity55,
              ),
            ),
          );
        });
      }

      content.addAll([
        // Call's primary view.
        Column(
          children: [
            Obx(
              () => SizedBox(
                width: double.infinity,
                height:
                    c.secondary.isNotEmpty &&
                            c.secondaryAlignment.value == Alignment.topCenter
                        ? c.secondaryHeight.value
                        : 0,
              ),
            ),
            Expanded(
              child: Row(
                children: [
                  Obx(
                    () => SizedBox(
                      height: double.infinity,
                      width:
                          c.secondary.isNotEmpty &&
                                  c.secondaryAlignment.value ==
                                      Alignment.centerLeft
                              ? c.secondaryWidth.value
                              : 0,
                    ),
                  ),
                  Expanded(
                    child: Stack(
                      children: [
                        Obx(() {
                          final bool isOutgoing =
                              (c.outgoing ||
                                  c.state.value == OngoingCallState.local) &&
                              !c.started;

                          final bool isIncoming =
                              c.state.value != OngoingCallState.active &&
                              c.state.value != OngoingCallState.joining &&
                              !isOutgoing;

                          final Widget child;

                          if (!isIncoming) {
                            child = _primaryView(c);
                          } else {
                            if (c.isDialog) {
                              final RxUser? user =
                                  c.chat.value?.members.values
                                      .firstWhereOrNull(
                                        (e) => e.user.id != c.me.id.userId,
                                      )
                                      ?.user;

                              child = CallCoverWidget(
                                c.chat.value?.callCover,
                                user: user,
                              );
                            } else {
                              if (c.chat.value?.avatar.value != null) {
                                final Avatar avatar =
                                    c.chat.value!.avatar.value!;
                                child = CallCoverWidget(
                                  UserCallCover(
                                    full: avatar.full,
                                    original: avatar.original,
                                    square: avatar.full,
                                    vertical: avatar.full,
                                  ),
                                );
                              } else {
                                child = CallCoverWidget(
                                  null,
                                  chat: c.chat.value,
                                );
                              }
                            }
                          }

                          return SafeAnimatedSwitcher(
                            duration: 400.milliseconds,
                            child: child,
                          );
                        }),
                      ],
                    ),
                  ),
                  Obx(
                    () => SizedBox(
                      height: double.infinity,
                      width:
                          c.secondary.isNotEmpty &&
                                  c.secondaryAlignment.value ==
                                      Alignment.centerRight
                              ? c.secondaryWidth.value
                              : 0,
                    ),
                  ),
                ],
              ),
            ),
            Obx(
              () => SizedBox(
                width: double.infinity,
                height:
                    c.secondary.isNotEmpty &&
                            c.secondaryAlignment.value == Alignment.bottomCenter
                        ? c.secondaryHeight.value
                        : 0,
              ),
            ),
          ],
        ),

        // Reconnection indicator.
        Obx(() {
          final Widget child = IgnorePointer(
            child: Container(
              width: double.infinity,
              height: double.infinity,
              color: style.colors.onBackgroundOpacity70,
              padding: const EdgeInsets.all(21.0),
              child: Center(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    if (!Config.disableInfiniteAnimations)
                      const DoubleBounceLoadingIndicator(),
                    const SizedBox(height: 16),
                    Text(
                      'label_reconnecting_ellipsis'.l10n,
                      style: style.fonts.normal.regular.onPrimary,
                    ),
                  ],
                ),
              ),
            ),
          );

          return AnimatedOpacity(
            opacity: c.connectionLost.isTrue ? 1 : 0,
            duration: 200.milliseconds,
            child: child,
          );
        }),

        possibleContainer(),

        // Makes UI appear on click.
        Listener(
          behavior: HitTestBehavior.translucent,
          onPointerDown: (d) {
            c.downPosition = d.localPosition;
            c.downButtons = d.buttons;
          },
          onPointerUp: (d) {
            if (c.downButtons & kPrimaryButton != 0 &&
                (d.localPosition.distanceSquared -
                            c.downPosition.distanceSquared)
                        .abs() <=
                    1500) {
              if (c.primaryDrags.value == 0 && c.secondaryDrags.value == 0) {
                if (c.state.value == OngoingCallState.active) {
                  if (!c.showUi.value) {
                    c.keepUi();
                  } else {
                    c.keepUi(false);
                  }
                }
              }
            }
          },
        ),

        // Empty drop zone if [secondary] is empty.
        Obx(() {
          final Axis secondaryAxis =
              c.size.width >= c.size.height ? Axis.horizontal : Axis.vertical;

          /// Pre-calculate the [ReorderableFit]'s size.
          final double panelSize = max(
            ReorderableFit.calculateSize(
              maxSize: c.size.shortestSide / 4,
              constraints: Size(c.size.width, c.size.height - 45),
              axis:
                  c.size.width >= c.size.height
                      ? Axis.horizontal
                      : Axis.vertical,
              length: c.secondary.length,
            ),
            130,
          );

          return SafeAnimatedSwitcher(
            key: const Key('SecondaryTargetAnimatedSwitcher'),
            duration: 200.milliseconds,
            child:
                c.secondary.isEmpty && c.doughDraggedRenderer.value != null
                    ? DropBoxArea<_DragData>(
                      size: panelSize,
                      axis: secondaryAxis,
                      visible: c.primaryDrags.value >= 1,
                      onWillAccept: (d) => d?.chatId == c.chatId.value,
                      onAccept: (_DragData d) {
                        if (secondaryAxis == Axis.horizontal) {
                          c.secondaryAlignment.value = Alignment.centerRight;
                        } else {
                          c.secondaryAlignment.value = Alignment.topCenter;
                        }

                        c.unfocus(d.participant);
                      },
                    )
                    : const SizedBox(),
          );
        }),
      ]);

      // Builds the [Dock] containing the [CallController.buttons].
      Widget dock() {
        return Obx(() {
          final bool isOutgoing =
              (c.outgoing || c.state.value == OngoingCallState.local) &&
              !c.started;

          final bool showBottomUi =
              (c.showUi.isTrue ||
                  c.draggedButton.value != null ||
                  c.state.value != OngoingCallState.active ||
                  (c.state.value == OngoingCallState.active &&
                      c.locals.isEmpty &&
                      c.remotes.isEmpty &&
                      c.focused.isEmpty &&
                      c.paneled.isEmpty));

          final bool answer =
              c.state.value != OngoingCallState.joining &&
              c.state.value != OngoingCallState.active &&
              !isOutgoing;

          final Widget child;

          if (answer) {
            child = Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                const SizedBox(width: 11),
                AcceptAudioButton(
                  c,
                  highlight: !c.withVideo,
                ).build(hinted: false),
                const SizedBox(width: 24),
                AcceptVideoButton(
                  c,
                  highlight: c.withVideo,
                ).build(hinted: false),
                const SizedBox(width: 24),
                DeclineButton(c).build(hinted: false),
                const SizedBox(width: 11),
              ],
            );
          } else {
            child = Dock<CallButton>(
              items: c.buttons,
              itemWidth: CallController.buttonSize,
              itemBuilder:
                  (e) => e.build(hinted: c.draggedButton.value == null),
              onReorder: (buttons) {
                c.buttons.value = buttons;
                c.relocateSecondary();
              },
              onDragStarted: (b) {
                c.showDragAndDropButtonsHint = false;
                c.draggedButton.value = b;
              },
              onDragEnded: (_) => c.draggedButton.value = null,
              onLeave: (_) => c.displayMore.value = true,
              onWillAccept: (d) => d?.c == c,
            );
          }

          return DockDecorator(
            show: showBottomUi,
            dockKey: c.dockKey,
            onAnimation:
                () => Future.delayed(Duration.zero, c.relocateSecondary),
            onEnter: (d) => c.keepUi(true),
            onHover: (d) => c.keepUi(true),
            onExit:
                c.showUi.value && !c.displayMore.value
                    ? (d) => c.keepUi(false)
                    : (d) => c.keepUi(),
            child: child,
          );
        });
      }

      // Builds the [Launchpad] panel containing the [CallController.panel].
      Widget launchpad() {
        return Obx(() {
          bool enabled =
              c.displayMore.isTrue &&
              c.primaryDrags.value == 0 &&
              c.secondaryDrags.value == 0;

          return Flexible(
            child: AnimatedOpacity(
              duration: const Duration(milliseconds: 150),
              opacity: c.displayMore.value ? 1 : 0,
              child: IgnorePointer(
                ignoring: !c.displayMore.value,
                child: Launchpad(
                  onEnter: enabled ? (d) => c.keepUi(true) : null,
                  onHover: enabled ? (d) => c.keepUi(true) : null,
                  onExit: enabled ? (d) => c.keepUi() : null,
                  onAccept: (CallButton data) {
                    c.buttons.remove(data);
                    c.draggedButton.value = null;
                  },
                  onWillAccept:
                      (CallButton? a) => a?.c == c && a?.isRemovable == true,
                  children:
                      c.panel.map((e) {
                        return DelayedDraggable(
                          feedback: Transform.translate(
                            offset: const Offset(
                              CallController.buttonSize / 2 * -1,
                              CallController.buttonSize / 2 * -1,
                            ),
                            child: e.build(),
                          ),
                          data: e,
                          onDragStarted: () {
                            c.showDragAndDropButtonsHint = false;
                            c.draggedButton.value = e;
                          },
                          onDragCompleted: () => c.draggedButton.value = null,
                          onDragEnd: (_) => c.draggedButton.value = null,
                          onDraggableCanceled:
                              (_, __) => c.draggedButton.value = null,
                          maxSimultaneousDrags: e.isRemovable ? null : 0,
                          dragAnchorStrategy: pointerDragAnchorStrategy,
                          child: e.build(
                            hinted: false,
                            big: true,
                            expanded: true,
                          ),
                        );
                      }).toList(),
                ),
              ),
            ),
          );
        });
      }

      // Footer part of the call with buttons.
      List<Widget> footer = [
        // Animated bottom buttons.
        Align(
          alignment: Alignment.bottomCenter,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Flexible(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  verticalDirection: VerticalDirection.up,
                  children: [dock(), launchpad()],
                ),
              ),
            ],
          ),
        ),

        // Display the more hint, if not dismissed.
        Obx(() {
          return SafeAnimatedSwitcher(
            duration: 150.milliseconds,
            child:
                c.showDragAndDropButtonsHint && c.displayMore.value
                    ? Align(
                      alignment: Alignment.bottomCenter,
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          AnimatedDelayedSwitcher(
                            delay: const Duration(milliseconds: 500),
                            duration: const Duration(milliseconds: 200),
                            child: Align(
                              alignment: Alignment.topCenter,
                              child: Container(
                                width: 290,
                                padding: EdgeInsets.only(
                                  top:
                                      10 +
                                      (WebUtils.isPopup
                                          ? 0
                                          : CallController.titleHeight),
                                ),
                                child: HintWidget(
                                  text: 'label_hint_drag_n_drop_buttons'.l10n,
                                  onTap:
                                      () =>
                                          c.showDragAndDropButtonsHint = false,
                                ),
                              ),
                            ),
                          ),
                          const Flexible(child: SizedBox(height: 420)),
                        ],
                      ),
                    )
                    : Container(),
          );
        }),
      ];

      List<Widget> ui = [
        Obx(() {
          bool preferTitle = c.state.value != OngoingCallState.active;
          return GestureDetector(
            behavior: HitTestBehavior.translucent,
            onDoubleTap: c.toggleFullscreen,
            onPanUpdate:
                preferTitle
                    ? (d) {
                      c.left.value = c.left.value + d.delta.dx;
                      c.top.value = c.top.value + d.delta.dy;
                      c.applyConstraints(context);
                    }
                    : null,
          );
        }),

        // Sliding from the top title bar.
        Obx(() {
          final bool isOutgoing =
              (c.outgoing || c.state.value == OngoingCallState.local) &&
              !c.started;

          final bool preferTitle =
              c.state.value != OngoingCallState.active && !isOutgoing;

          return SafeAnimatedSwitcher(
            key: const Key('AnimatedSwitcherCallTitle'),
            duration: const Duration(milliseconds: 200),
            child:
                preferTitle
                    ? Align(
                      key: const Key('CallTitlePadding'),
                      alignment: Alignment.topCenter,
                      child: Padding(
                        padding: EdgeInsets.only(
                          left: 10,
                          right: 10,
                          top: c.size.height * 0.05,
                        ),
                        child: callTitle(c),
                      ),
                    )
                    : Container(key: UniqueKey()),
          );
        }),

        // Bottom [MouseRegion] that toggles UI on hover.
        Obx(() {
          final bool enabled =
              c.primaryDrags.value == 0 && c.secondaryDrags.value == 0;
          return Align(
            alignment: Alignment.bottomCenter,
            child: SizedBox(
              height: 100,
              width: double.infinity,
              child: MouseRegion(
                opaque: false,
                onEnter: enabled ? (d) => c.keepUi(true) : null,
                onHover: enabled ? (d) => c.keepUi(true) : null,
                onExit:
                    c.showUi.value && enabled
                        ? (d) {
                          if (c.displayMore.isTrue) {
                            c.keepUi();
                          } else {
                            c.keepUi(false);
                          }
                        }
                        : null,
              ),
            ),
          );
        }),

        // Secondary panel itself.
        Obx(() {
          final bool isIncoming =
              c.state.value != OngoingCallState.active &&
              c.state.value != OngoingCallState.joining &&
              !(c.outgoing || c.state.value == OngoingCallState.local);

          if (isIncoming) {
            return const SizedBox();
          }

          return LayoutBuilder(
            builder: (_, constraints) {
              // Scale the secondary panel after this frame is displayed, as
              // otherwise it invokes re-drawing twice in a frame, resulting in an
              // error.
              WidgetsBinding.instance.addPostFrameCallback((_) {
                c.scaleSecondary(constraints);
                WidgetsBinding.instance.addPostFrameCallback(
                  (_) => c.relocateSecondary(),
                );
              });

              return _secondaryView(c, context);
            },
          );
        }),

        // [MouseRegion] changing the cursor.
        Obx(() {
          return MouseRegion(
            opaque: false,
            cursor:
                c.draggedRenderer.value != null ||
                        c.doughDraggedRenderer.value != null
                    ? CustomMouseCursors.grabbing
                    : c.isCursorHidden.value
                    ? SystemMouseCursors.none
                    : c.hoveredRenderer.value != null
                    ? CustomMouseCursors.grab
                    : c.hoveredParticipant.value != null
                    ? SystemMouseCursors.basic
                    : MouseCursor.defer,
          );
        }),

        // Top [MouseRegion] that toggles info header on hover.
        Align(
          alignment: Alignment.topCenter,
          child: SizedBox(
            height: 100,
            width: double.infinity,
            child: MouseRegion(
              opaque: false,
              onEnter: (_) {
                c.showHeader.value = true;
                c.isCursorHidden.value = false;
              },
              onHover: (_) {
                c.showHeader.value = true;
                c.isCursorHidden.value = false;
              },
              onExit: (_) {
                c.showHeader.value = false;
              },
            ),
          ),
        ),

        // Show a hint if any renderer is draggable.
        Obx(() {
          final bool hideSecondary = c.size.width < 500 && c.size.height < 500;
          final bool mayDragVideo =
              !hideSecondary &&
              (c.focused.length > 1 ||
                  (c.focused.isEmpty &&
                      c.primary.length + c.secondary.length > 1));

          return SafeAnimatedSwitcher(
            duration: 150.milliseconds,
            child:
                c.showDragAndDropVideosHint && mayDragVideo
                    ? Padding(
                      padding: EdgeInsets.only(
                        top:
                            c.secondary.isNotEmpty &&
                                    c.secondaryAlignment.value ==
                                        Alignment.topCenter
                                ? 10 + c.secondaryHeight.value
                                : 10,
                        right:
                            c.secondary.isNotEmpty &&
                                    c.secondaryAlignment.value ==
                                        Alignment.centerRight
                                ? 10 + c.secondaryWidth.value
                                : 10,
                      ),
                      child: Align(
                        alignment: Alignment.topRight,
                        child: SizedBox(
                          width: 320,
                          child: HintWidget(
                            text: 'label_hint_drag_n_drop_video'.l10n,
                            onTap: () => c.showDragAndDropVideosHint = false,
                          ),
                        ),
                      ),
                    )
                    : const SizedBox(),
          );
        }),

        // Sliding from the top info header.
        if (WebUtils.isPopup)
          Obx(() {
            return Align(
              alignment: Alignment.topCenter,
              child: AnimatedSlider(
                duration: 400.milliseconds,
                translate: false,
                beginOffset: const Offset(0, -1),
                endOffset: const Offset(0, 0),
                isOpen:
                    c.state.value == OngoingCallState.active &&
                    c.showHeader.value,
                child: MouseRegion(
                  onEnter: (_) {
                    c.showHeader.value = true;
                    c.headerHovered = true;
                  },
                  onHover: (_) {
                    c.showHeader.value = true;
                    c.headerHovered = true;
                  },
                  onExit: (_) {
                    c.showHeader.value = false;
                    c.headerHovered = false;
                  },
                  child: Container(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(11),
                      boxShadow: [
                        CustomBoxShadow(
                          color: style.colors.onBackgroundOpacity20,
                          blurRadius: 8,
                          blurStyle: BlurStyle.outer,
                        ),
                      ],
                    ),
                    margin: const EdgeInsets.fromLTRB(10, 5, 10, 2),
                    child: ConditionalBackdropFilter(
                      borderRadius: BorderRadius.circular(11),
                      filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
                      child: Container(
                        decoration: BoxDecoration(
                          color:
                              ConditionalBackdropFilter.enabled
                                  ? style.colors.primaryAuxiliaryOpacity25
                                  : style.colors.primaryAuxiliaryOpacity55,
                          borderRadius: BorderRadius.circular(11),
                        ),
                        padding: const EdgeInsets.symmetric(
                          vertical: 8,
                          horizontal: 10,
                        ),
                        child: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            if (c.fullscreen.value) ...[
                              Text(
                                'label_call_title'.l10nfmt(c.titleArguments),
                                style: style.fonts.small.regular.onPrimary,
                                overflow: TextOverflow.ellipsis,
                              ),
                              Container(
                                margin: const EdgeInsets.fromLTRB(12, 0, 12, 0),
                                color: style.colors.onPrimary,
                                width: 1,
                                height: 12,
                              ),
                            ],
                            AnimatedButton(
                              enabled: c.draggedRenderer.value == null,
                              onPressed: c.layoutAsPrimary,
                              child: const SvgIcon(SvgIcons.callGallery),
                            ),
                            const SizedBox(width: 16),
                            AnimatedButton(
                              enabled: c.draggedRenderer.value == null,
                              onPressed:
                                  () => c.layoutAsSecondary(floating: true),
                              child: const SvgIcon(SvgIcons.callFloating),
                            ),
                            const SizedBox(width: 16),
                            AnimatedButton(
                              enabled: c.draggedRenderer.value == null,
                              onPressed:
                                  () => c.layoutAsSecondary(floating: false),
                              child: const SvgIcon(SvgIcons.callSide),
                            ),
                            const SizedBox(width: 16),
                            AnimatedButton(
                              enabled: c.draggedRenderer.value == null,
                              onPressed: c.toggleFullscreen,
                              child: SvgIcon(
                                c.fullscreen.value
                                    ? SvgIcons.fullscreenExitSmall
                                    : SvgIcons.fullscreenEnterSmall,
                              ),
                            ),
                            if (c.fullscreen.value) const SizedBox(width: 4),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            );
          }),

        // If there's any notifications to show, display them.
        Align(
          alignment: Alignment.topCenter,
          child: Padding(
            padding: const EdgeInsets.only(top: 8),
            child: Obx(() {
              if (c.notifications.isEmpty) {
                return const SizedBox();
              }

              return Column(
                mainAxisSize: MainAxisSize.min,
                children:
                    c.notifications.reversed.take(3).map((e) {
                      return CallNotificationWidget(
                        e,
                        onClose: () => c.notifications.remove(e),
                      );
                    }).toList(),
              );
            }),
          ),
        ),

        Obx(() {
          if (c.minimized.value && !c.fullscreen.value) {
            return Container();
          }

          return Stack(children: footer);
        }),
      ];

      // Combines all the stackable content into [Scaffold].
      Widget scaffold = Scaffold(
        backgroundColor: style.colors.onBackground,
        body: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            if (!WebUtils.isPopup)
              GestureDetector(
                behavior: HitTestBehavior.translucent,
                onPanUpdate: (d) {
                  c.left.value = c.left.value + d.delta.dx;
                  c.top.value = c.top.value + d.delta.dy;
                  c.applyConstraints(context);
                },
                child: Container(
                  decoration: BoxDecoration(
                    color: style.colors.transparent,
                    borderRadius: BorderRadius.circular(30),
                    boxShadow: [
                      CustomBoxShadow(
                        color: style.colors.onBackgroundOpacity20,
                        blurRadius: 8,
                        blurStyle: BlurStyle.outer,
                      ),
                    ],
                  ),
                  child: Obx(() {
                    return TitleBar(
                      title: 'label_call_title'.l10nfmt(c.titleArguments),
                      chat: c.chat.value,
                      fullscreen: c.fullscreen.value,
                      height: CallController.titleHeight,
                      toggleFullscreen:
                          c.draggedRenderer.value == null
                              ? c.toggleFullscreen
                              : null,
                      onPrimary:
                          c.draggedRenderer.value == null
                              ? c.layoutAsPrimary
                              : null,
                      onFloating:
                          c.draggedRenderer.value == null
                              ? () => c.layoutAsSecondary(floating: true)
                              : null,
                      onSecondary:
                          c.draggedRenderer.value == null
                              ? () => c.layoutAsSecondary(floating: false)
                              : null,
                    );
                  }),
                ),
              ),
            Expanded(child: Stack(children: [...content, ...ui])),
          ],
        ),
      );

      if (c.minimized.value && !c.fullscreen.value) {
        // Applies constraints on every rebuild.
        // This includes the screen size changes.
        c.applyConstraints(context);

        if (c.hidden.value) {
          return const SizedBox();
        }

        // Returns a [Scaler] scaling the minimized view.
        Widget scaler({
          Key? key,
          MouseCursor cursor = MouseCursor.defer,
          required Function(double, double) onDrag,
          double? width,
          double? height,
        }) {
          return Obx(() {
            return MouseRegion(
              cursor:
                  c.draggedRenderer.value != null ? MouseCursor.defer : cursor,
              child: Scaler(
                key: key,
                onDragUpdate: onDrag,
                onDragEnd: (_) => c.updateSecondaryAttach(),
                width: width ?? Scaler.size,
                height: height ?? Scaler.size,
              ),
            );
          });
        }

        // Returns a stack of draggable [Scaler]s on each of the sides:
        //
        // +-------+
        // |       |
        // |       |
        // |       |
        // +-------+
        //
        // 1) + is a cornered scale point;
        // 2) | is a horizontal scale point;
        // 3) - is a vertical scale point.
        return Stack(
          children: [
            // Top middle.
            Obx(() {
              return Positioned(
                top: c.top.value - Scaler.size / 2,
                left: c.left.value + Scaler.size / 2,
                child: scaler(
                  cursor: SystemMouseCursors.resizeUpDown,
                  width: c.width.value - Scaler.size,
                  onDrag:
                      (dx, dy) => c.resize(context, y: ScaleModeY.top, dy: dy),
                ),
              );
            }),

            // Center left.
            Obx(() {
              return Positioned(
                top: c.top.value + Scaler.size / 2,
                left: c.left.value - Scaler.size / 2,
                child: scaler(
                  cursor: SystemMouseCursors.resizeLeftRight,
                  height: c.height.value - Scaler.size,
                  onDrag:
                      (dx, dy) => c.resize(context, x: ScaleModeX.left, dx: dx),
                ),
              );
            }),

            // Center right.
            Obx(() {
              return Positioned(
                top: c.top.value + Scaler.size / 2,
                left: c.left.value + c.width.value - Scaler.size / 2,
                child: scaler(
                  cursor: SystemMouseCursors.resizeLeftRight,
                  height: c.height.value - Scaler.size,
                  onDrag:
                      (dx, dy) =>
                          c.resize(context, x: ScaleModeX.right, dx: -dx),
                ),
              );
            }),

            // Bottom center.
            Obx(() {
              return Positioned(
                top: c.top.value + c.height.value - Scaler.size / 2,
                left: c.left.value + Scaler.size / 2,
                child: scaler(
                  cursor: SystemMouseCursors.resizeUpDown,
                  width: c.width.value - Scaler.size,
                  onDrag:
                      (dx, dy) =>
                          c.resize(context, y: ScaleModeY.bottom, dy: -dy),
                ),
              );
            }),

            // Top left.
            Obx(() {
              return Positioned(
                top: c.top.value - Scaler.size / 2,
                left: c.left.value - Scaler.size / 2,
                child: scaler(
                  cursor: CustomMouseCursors.resizeUpLeftDownRight,
                  width: Scaler.size * 2,
                  height: Scaler.size * 2,
                  onDrag:
                      (dx, dy) => c.resize(
                        context,
                        y: ScaleModeY.top,
                        x: ScaleModeX.left,
                        dx: dx,
                        dy: dy,
                      ),
                ),
              );
            }),

            // Top right.
            Obx(() {
              return Positioned(
                top: c.top.value - Scaler.size / 2,
                left: c.left.value + c.width.value - 3 * Scaler.size / 2,
                child: scaler(
                  cursor: CustomMouseCursors.resizeUpRightDownLeft,
                  width: Scaler.size * 2,
                  height: Scaler.size * 2,
                  onDrag:
                      (dx, dy) => c.resize(
                        context,
                        y: ScaleModeY.top,
                        x: ScaleModeX.right,
                        dx: -dx,
                        dy: dy,
                      ),
                ),
              );
            }),

            // Bottom left.
            Obx(() {
              return Positioned(
                top: c.top.value + c.height.value - 3 * Scaler.size / 2,
                left: c.left.value - Scaler.size / 2,
                child: scaler(
                  cursor: CustomMouseCursors.resizeUpRightDownLeft,
                  width: Scaler.size * 2,
                  height: Scaler.size * 2,
                  onDrag:
                      (dx, dy) => c.resize(
                        context,
                        y: ScaleModeY.bottom,
                        x: ScaleModeX.left,
                        dx: dx,
                        dy: -dy,
                      ),
                ),
              );
            }),

            // Bottom right.
            Obx(() {
              return Positioned(
                top: c.top.value + c.height.value - 3 * Scaler.size / 2,
                left: c.left.value + c.width.value - 3 * Scaler.size / 2,
                child: scaler(
                  cursor: CustomMouseCursors.resizeUpLeftDownRight,
                  width: Scaler.size * 2,
                  height: Scaler.size * 2,
                  onDrag:
                      (dx, dy) => c.resize(
                        context,
                        y: ScaleModeY.bottom,
                        x: ScaleModeX.right,
                        dx: -dx,
                        dy: -dy,
                      ),
                ),
              );
            }),

            Obx(() {
              return Positioned(
                left: c.left.value,
                top: c.top.value,
                width: c.width.value,
                height: c.height.value,
                child: Material(
                  type: MaterialType.card,
                  borderRadius: BorderRadius.circular(10),
                  elevation: 10,
                  child: Stack(
                    children: [
                      ClipRRect(
                        borderRadius: BorderRadius.circular(10),
                        child: scaffold,
                      ),
                      ClipRect(child: Stack(children: footer)),
                    ],
                  ),
                ),
              );
            }),
          ],
        );
      }

      // If the call popup is not [minimized], then return the [scaffold].
      return scaffold;
    },
  );
}